From 326ff47eddb0e2a89dec8a0b045ef8e25c0b6c8c Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 14 May 2025 13:25:57 +0200 Subject: [PATCH 1/2] Some improvements --- CHANGELOG.md | 14 +++-- migrations/Version20250514051344.php | 31 +++++++++++ migrations/Version20250514101117.php | 31 +++++++++++ .../RunScheduledCommandTasksCommand.php | 23 ++++----- src/Controller/OgAgent/RunScriptAction.php | 4 +- .../OgBoot/PxeBootFile/DeleteAction.php | 41 +++++++++++++++ .../OgBoot/PxeBootFile/GetAction.php | 4 +- src/Dto/Input/ImageRepositoryInput.php | 1 - src/Dto/Input/PxeTemplateInput.php | 2 + src/Entity/Client.php | 15 ++++++ src/Entity/Command.php | 3 ++ src/Entity/CommandTask.php | 22 ++++---- src/EventListener/ClientMacListener.php | 51 +++++++++++++++++++ .../Constraints/PxeTemplateUniqueDefault.php | 23 +++++++++ .../PxeTemplateUniqueDefaultValidator.php | 46 +++++++++++++++++ translations/validators.en.yaml | 1 + translations/validators.es.yaml | 1 + 17 files changed, 281 insertions(+), 32 deletions(-) create mode 100644 migrations/Version20250514051344.php create mode 100644 migrations/Version20250514101117.php create mode 100644 src/Controller/OgBoot/PxeBootFile/DeleteAction.php create mode 100644 src/EventListener/ClientMacListener.php create mode 100644 src/Validator/Constraints/PxeTemplateUniqueDefault.php create mode 100644 src/Validator/Constraints/PxeTemplateUniqueDefaultValidator.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 9422a7c..f0e75b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog -<<<<<<< HEAD +## [0.12.1] - 2025-05-14 +### Improved +- Se ha eliminado la restriccion en el formulario de crear/editar repositorio, que hacia que la comprobara el formato de IP. Ahora tambien puede ser DNS. +- Mejora en el script de ejecutar tareas. +- Ahora al editar la mac de un cliente, se borra el fichero de arranque antiguo. +- Se ha añadido una restriccion en plantillas para que tan solo haya 1 por defecto + +--- + ## [0.12.0] - 2025-05-13 ### Added - Se ha añadido nueva API para poder gestionar las tareas y acciones programadas. @@ -12,11 +20,11 @@ ## Fixed - Se ha corregido el bug en la creacion de clientes masivos donde no se le asignaba la plantilla PXE. - Se ha corregido un bug en el DTO de clientes, que hacia que PHP diera un timeout por bucle infinito. -======= + +--- ## [0.11.2] - 2025-04-23 ### Fixed - Se ha cambiado la forma en guardar la fecha al recibir "ping" de los clientes. ->>>>>>> main --- ## [0.11.1] - 2025-04-16 diff --git a/migrations/Version20250514051344.php b/migrations/Version20250514051344.php new file mode 100644 index 0000000..47baece --- /dev/null +++ b/migrations/Version20250514051344.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE client ADD token VARCHAR(255) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE client DROP token'); + } +} diff --git a/migrations/Version20250514101117.php b/migrations/Version20250514101117.php new file mode 100644 index 0000000..e203294 --- /dev/null +++ b/migrations/Version20250514101117.php @@ -0,0 +1,31 @@ +addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_NAME ON command (name)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP INDEX UNIQ_IDENTIFIER_NAME ON command'); + } +} diff --git a/src/Command/RunScheduledCommandTasksCommand.php b/src/Command/RunScheduledCommandTasksCommand.php index 06e9971..3a5e7c5 100644 --- a/src/Command/RunScheduledCommandTasksCommand.php +++ b/src/Command/RunScheduledCommandTasksCommand.php @@ -10,6 +10,7 @@ use App\Dto\Input\CommandExecuteInput; use App\Dto\Input\DeployImageInput; use App\Dto\Output\ClientOutput; use App\Entity\CommandTask; +use App\Model\ClientStatus; use App\Repository\CommandTaskRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Attribute\AsCommand; @@ -59,22 +60,20 @@ class RunScheduledCommandTasksCommand extends Command usort($scripts, fn($a, $b) => $a->getExecutionOrder() <=> $b->getExecutionOrder()); foreach ($scripts as $script) { - try { - $output->writeln(" - Ejecutando script de tipo {$script->getType()} con orden {$script->getExecutionOrder()}"); + $output->writeln(" - Ejecutando script de tipo {$script->getType()} con orden {$script->getExecutionOrder()}"); - if ($script->getType() === 'run-script') { - $input = new CommandExecuteInput(); + if ($script->getType() === 'run-script') { + $input = new CommandExecuteInput(); - foreach ($task->getOrganizationalUnit()?->getClients() as $client) { - $input->clients[] = new ClientOutput($client); + foreach ($task->getOrganizationalUnit()?->getClients() as $client) { + if ($client->getStatus() !== ClientStatus::OG_LIVE) { + continue; } - $input->script = $script->getContent(); - - $this->runScriptAction->__invoke($input); + $input->clients[] = new ClientOutput($client); } - } catch (TransportExceptionInterface $e) { - $output->writeln("Error ejecutando script: " . $e->getMessage()); - continue; + $input->script = $script->getContent(); + + $this->runScriptAction->__invoke($input); } } diff --git a/src/Controller/OgAgent/RunScriptAction.php b/src/Controller/OgAgent/RunScriptAction.php index 0282f3a..e4a999c 100644 --- a/src/Controller/OgAgent/RunScriptAction.php +++ b/src/Controller/OgAgent/RunScriptAction.php @@ -54,10 +54,10 @@ class RunScriptAction extends AbstractController ], 'json' => $data, ]); - $this->logger->info('Rebooting client', ['client' => $client->getId()]); + $this->logger->info('Executing run-script', ['client' => $client->getId(), 'response' => $response->getContent()]); } catch (TransportExceptionInterface $e) { - $this->logger->error('Error rebooting client', ['client' => $client->getId(), 'error' => $e->getMessage()]); + $this->logger->error('Error executing run-script', ['client' => $client->getId(), 'error' => $e->getMessage()]); return new JsonResponse( data: ['error' => $e->getMessage()], status: Response::HTTP_INTERNAL_SERVER_ERROR diff --git a/src/Controller/OgBoot/PxeBootFile/DeleteAction.php b/src/Controller/OgBoot/PxeBootFile/DeleteAction.php new file mode 100644 index 0000000..884bd11 --- /dev/null +++ b/src/Controller/OgBoot/PxeBootFile/DeleteAction.php @@ -0,0 +1,41 @@ +httpClient->request('DELETE', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxes/'.$mac, [ + 'headers' => [ + 'accept' => 'application/json', + ], + ]); + } catch (TransportExceptionInterface $e) { + return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $data = json_decode($response->getContent(), true); + + return new JsonResponse( data: $data, status: Response::HTTP_OK); + + } +} \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeBootFile/GetAction.php b/src/Controller/OgBoot/PxeBootFile/GetAction.php index 4f4fb02..e2efc0d 100644 --- a/src/Controller/OgBoot/PxeBootFile/GetAction.php +++ b/src/Controller/OgBoot/PxeBootFile/GetAction.php @@ -24,10 +24,10 @@ class GetAction extends AbstractOgBootController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(Client $client, HttpClientInterface $httpClient): JsonResponse + public function __invoke(Client $client): JsonResponse { try { - $response = $httpClient->request('GET', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxes/'.$client->getMac(), [ + $response = $this->httpClient->request('GET', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxes/'.$client->getMac(), [ 'headers' => [ 'accept' => 'application/json', ], diff --git a/src/Dto/Input/ImageRepositoryInput.php b/src/Dto/Input/ImageRepositoryInput.php index 56623e1..f4e9089 100644 --- a/src/Dto/Input/ImageRepositoryInput.php +++ b/src/Dto/Input/ImageRepositoryInput.php @@ -22,7 +22,6 @@ final class ImageRepositoryInput public ?string $name = null; #[Assert\NotBlank] - #[Assert\Ip] #[Groups(['repository:write'])] #[ApiProperty(description: 'The IP of the repository', example: "")] public ?string $ip = null; diff --git a/src/Dto/Input/PxeTemplateInput.php b/src/Dto/Input/PxeTemplateInput.php index 58d4946..e221edd 100644 --- a/src/Dto/Input/PxeTemplateInput.php +++ b/src/Dto/Input/PxeTemplateInput.php @@ -4,9 +4,11 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; use App\Entity\PxeTemplate; +use App\Validator\Constraints\PxeTemplateUniqueDefault; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; +#[PxeTemplateUniqueDefault] final class PxeTemplateInput { #[Assert\NotBlank(message: 'validators.pxe_template.name.not_blank')] diff --git a/src/Entity/Client.php b/src/Entity/Client.php index 023fcf8..889d9c2 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -89,6 +89,9 @@ class Client extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?string $firmwareType = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $token = null; + public function __construct() { parent::__construct(); @@ -354,4 +357,16 @@ class Client extends AbstractEntity return $this; } + + public function getToken(): ?string + { + return $this->token; + } + + public function setToken(?string $token): static + { + $this->token = $token; + + return $this; + } } diff --git a/src/Entity/Command.php b/src/Entity/Command.php index ab5d4bb..1aa5024 100644 --- a/src/Entity/Command.php +++ b/src/Entity/Command.php @@ -7,8 +7,11 @@ use Doctrine\DBAL\Types\Types; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; #[ORM\Entity(repositoryClass: CommandRepository::class)] +#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_NAME', fields: ['name'])] +#[UniqueEntity(fields: ['name'], message: 'validators.command.name.unique')] class Command extends AbstractEntity { use NameableTrait; diff --git a/src/Entity/CommandTask.php b/src/Entity/CommandTask.php index f572155..8bb719c 100644 --- a/src/Entity/CommandTask.php +++ b/src/Entity/CommandTask.php @@ -227,19 +227,17 @@ class CommandTask extends AbstractEntity if ($type === 'none') { $execDate = $schedule->getExecutionDate(); - if ($execDate !== null && $execDate > $now) { - if ($executionTime !== null) { - $execDateTime = \DateTime::createFromFormat( - 'Y-m-d H:i:s', - $execDate->format('Y-m-d') . ' ' . $executionTime->format('H:i:s') - ); - } else { - $execDateTime = $execDate; - } + if ($executionTime !== null) { + $execDateTime = \DateTime::createFromFormat( + 'Y-m-d H:i:s', + $execDate->format('Y-m-d') . ' ' . $executionTime->format('H:i:s') + ); + } else { + $execDateTime = $execDate; + } - if ($closestDateTime === null || $execDateTime < $closestDateTime) { - $closestDateTime = $execDateTime; - } + if ($closestDateTime === null || $execDateTime < $closestDateTime) { + $closestDateTime = $execDateTime; } } else { $details = $schedule->getRecurrenceDetails(); diff --git a/src/EventListener/ClientMacListener.php b/src/EventListener/ClientMacListener.php new file mode 100644 index 0000000..dc21532 --- /dev/null +++ b/src/EventListener/ClientMacListener.php @@ -0,0 +1,51 @@ +getObjectManager(); + $uow = $em->getUnitOfWork(); + $changeSet = $uow->getEntityChangeSet($client); + + if (!array_key_exists('mac', $changeSet)) { + return; + } + + $oldMac = isset($changeSet['mac'][0]) ? $changeSet['mac'][0] : null; + + if ($oldMac === null) { + return; + } + + $this->deleteAction->__invoke($oldMac); + } + +} \ No newline at end of file diff --git a/src/Validator/Constraints/PxeTemplateUniqueDefault.php b/src/Validator/Constraints/PxeTemplateUniqueDefault.php new file mode 100644 index 0000000..97f2e24 --- /dev/null +++ b/src/Validator/Constraints/PxeTemplateUniqueDefault.php @@ -0,0 +1,23 @@ +message = 'Ya hay un oglive marcado como predeterminado.'; + } + + public function getTargets(): string + { + return self::CLASS_CONSTRAINT; + } +} \ No newline at end of file diff --git a/src/Validator/Constraints/PxeTemplateUniqueDefaultValidator.php b/src/Validator/Constraints/PxeTemplateUniqueDefaultValidator.php new file mode 100644 index 0000000..e7b7bb3 --- /dev/null +++ b/src/Validator/Constraints/PxeTemplateUniqueDefaultValidator.php @@ -0,0 +1,46 @@ +requestStack->getCurrentRequest(); + + if (!$value instanceof PxeTemplateInput) { + return; + } + + if ($value->isDefault === false) { + return; + } + + $ogLiveDefault = $this->entityManager->getRepository(PxeTemplate::class) + ->findOneBy([ + 'isDefault' => true, + ]); + + if ($ogLiveDefault) { + $this->context->buildViolation($constraint->message)->addViolation(); + } + + } +} \ No newline at end of file diff --git a/translations/validators.en.yaml b/translations/validators.en.yaml index 2ea7718..2c58ac9 100644 --- a/translations/validators.en.yaml +++ b/translations/validators.en.yaml @@ -11,6 +11,7 @@ validators: command: name: not_blank: 'The name should not be blank.' + unique: 'The name should be unique.' script: not_blank: 'The script should not be blank.' diff --git a/translations/validators.es.yaml b/translations/validators.es.yaml index d3b821e..93dcaef 100644 --- a/translations/validators.es.yaml +++ b/translations/validators.es.yaml @@ -11,6 +11,7 @@ validators: command: name: not_blank: 'El nombre no debería estar vacío.' + unique: 'El nombre debería ser único. Ya existe un comando con ese nombre.' script: not_blank: 'El script no debería estar vacío.' From 527383bce16c1a7033beeda74373d1b28fc620b2 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 14 May 2025 13:28:02 +0200 Subject: [PATCH 2/2] solve conflicts --- CHANGELOG.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9422a7c..cc07a70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,4 @@ # Changelog -<<<<<<< HEAD ## [0.12.0] - 2025-05-13 ### Added - Se ha añadido nueva API para poder gestionar las tareas y acciones programadas. @@ -12,11 +11,11 @@ ## Fixed - Se ha corregido el bug en la creacion de clientes masivos donde no se le asignaba la plantilla PXE. - Se ha corregido un bug en el DTO de clientes, que hacia que PHP diera un timeout por bucle infinito. -======= + +--- ## [0.11.2] - 2025-04-23 ### Fixed - Se ha cambiado la forma en guardar la fecha al recibir "ping" de los clientes. ->>>>>>> main --- ## [0.11.1] - 2025-04-16