From 2d6b058eaf50af42f4174f2911c85beb680022a1 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 1 Apr 2025 10:58:11 +0200 Subject: [PATCH 01/16] refs #1794. Updted assistants endpoints --- migrations/Version20250325075647.php | 31 +++++++ migrations/Version20250326061450.php | 31 +++++++ migrations/Version20250331144522.php | 31 +++++++ src/Controller/OgAgent/CreateImageAction.php | 2 +- src/Controller/OgAgent/DeployImageAction.php | 2 +- src/Controller/OgAgent/LoginAction.php | 93 +++++++++++++++++++ .../OgAgent/PartitionAssistantAction.php | 15 ++- src/Controller/OgAgent/PowerOffAction.php | 4 +- src/Controller/OgAgent/RebootAction.php | 6 +- src/Controller/OgAgent/StatusAction.php | 6 +- ...ntsController.php => StatusController.php} | 4 +- .../OgBoot/PxeBootFile/PostAction.php | 2 +- .../OgRepository/Image/DeployImageAction.php | 4 +- src/Dto/Input/CommandInput.php | 9 ++ src/Dto/Output/ClientOutput.php | 4 + src/Dto/Output/CommandOutput.php | 4 + src/Dto/Output/ImageImageRepositoryOutput.php | 4 + src/Dto/Output/TraceOutput.php | 2 +- 18 files changed, 234 insertions(+), 20 deletions(-) create mode 100644 migrations/Version20250325075647.php create mode 100644 migrations/Version20250326061450.php create mode 100644 migrations/Version20250331144522.php create mode 100644 src/Controller/OgAgent/LoginAction.php rename src/Controller/OgAgent/Webhook/{ClientsController.php => StatusController.php} (98%) diff --git a/migrations/Version20250325075647.php b/migrations/Version20250325075647.php new file mode 100644 index 0000000..6048e92 --- /dev/null +++ b/migrations/Version20250325075647.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE client ADD firmware_type 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 firmware_type'); + } +} diff --git a/migrations/Version20250326061450.php b/migrations/Version20250326061450.php new file mode 100644 index 0000000..2eebbef --- /dev/null +++ b/migrations/Version20250326061450.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE command ADD parameters TINYINT(1) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE command DROP parameters'); + } +} diff --git a/migrations/Version20250331144522.php b/migrations/Version20250331144522.php new file mode 100644 index 0000000..b5b2f71 --- /dev/null +++ b/migrations/Version20250331144522.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image_image_repository ADD datasize 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 image_image_repository DROP datasize'); + } +} diff --git a/src/Controller/OgAgent/CreateImageAction.php b/src/Controller/OgAgent/CreateImageAction.php index 55c87a7..2a0d485 100644 --- a/src/Controller/OgAgent/CreateImageAction.php +++ b/src/Controller/OgAgent/CreateImageAction.php @@ -90,7 +90,7 @@ class CreateImageAction extends AbstractController try { $this->logger->info('Creating image', ['image' => $image->getId()]); - $response = $this->httpClient->request('POST', 'https://'.$image->getClient()->getIp().':8000/CloningEngine/CrearImagen', [ + $response = $this->httpClient->request('POST', 'https://'.$image->getClient()->getIp().':8000/opengnsys/CrearImagen', [ 'verify_peer' => false, 'verify_host' => false, 'headers' => [ diff --git a/src/Controller/OgAgent/DeployImageAction.php b/src/Controller/OgAgent/DeployImageAction.php index eaacd9b..6bf3c8d 100644 --- a/src/Controller/OgAgent/DeployImageAction.php +++ b/src/Controller/OgAgent/DeployImageAction.php @@ -92,7 +92,7 @@ class DeployImageAction extends AbstractController ]; try { - $response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/CloningEngine/RestaurarImagen', [ + $response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/opengnsys/RestaurarImagen', [ 'verify_peer' => false, 'verify_host' => false, 'headers' => [ diff --git a/src/Controller/OgAgent/LoginAction.php b/src/Controller/OgAgent/LoginAction.php new file mode 100644 index 0000000..42fd7e1 --- /dev/null +++ b/src/Controller/OgAgent/LoginAction.php @@ -0,0 +1,93 @@ +clients as $clientEntity) { + /** @var Client $client */ + $client = $clientEntity->getEntity(); + + + if (!$client->getIp()) { + throw new ValidatorException('IP is required'); + } + + if ($client->getStatus() !== ClientStatus::OG_LIVE) { + throw new ValidatorException('Client is not in OG_LIVE status'); + } + + $data = [ + 'nfn' => 'IniciarSesion', + 'dsk' => '1', + 'par' => '1', + 'ids' => '0' + ]; + + try { + $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/IniciarSesion', [ + 'verify_peer' => false, + 'verify_host' => false, + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'json' => $data, + ]); + $this->logger->info('Login client', ['client' => $client->getId()]); + + } catch (TransportExceptionInterface $e) { + $this->logger->error('Login rebooting client', ['client' => $client->getId(), 'error' => $e->getMessage()]); + return new JsonResponse( + data: ['error' => $e->getMessage()], + status: Response::HTTP_INTERNAL_SERVER_ERROR + ); + } + + $jobId = json_decode($response->getContent(), true)['job_id']; + + $client->setStatus(ClientStatus::INITIALIZING); + $this->entityManager->persist($client); + $this->entityManager->flush(); + + $this->createService->__invoke($client, CommandTypes::REBOOT, TraceStatus::SUCCESS, $jobId, []); + } + + return new JsonResponse(data: [], status: Response::HTTP_OK); + } +} diff --git a/src/Controller/OgAgent/PartitionAssistantAction.php b/src/Controller/OgAgent/PartitionAssistantAction.php index 7e8021d..5dfe433 100644 --- a/src/Controller/OgAgent/PartitionAssistantAction.php +++ b/src/Controller/OgAgent/PartitionAssistantAction.php @@ -48,6 +48,7 @@ class PartitionAssistantAction extends AbstractController } foreach ($input->clients as $clientInput) { + /** @var Client $client */ $client = $clientInput->getEntity(); $disks = []; @@ -72,13 +73,10 @@ class PartitionAssistantAction extends AbstractController ]; } - if ($partition->filesystem === 'CACHE') { - $disks[$diskNumber]['diskData'] = [ - 'dis' => (string) $diskNumber, - 'che' => "0", - 'tch' => (string) ($partition->size * 1024), - ]; - } + $disks[$diskNumber]['diskData'] = [ + 'dis' => (string) $diskNumber, + 'tch' => (string) ($partition->size * 1024), + ]; $disks[$diskNumber]['partitionData'][] = [ 'par' => (string) $partition->partitionNumber, @@ -104,7 +102,7 @@ class PartitionAssistantAction extends AbstractController ]; try { - $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/CloningEngine/Configurar', [ + $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/opengnsys/Configurar', [ 'verify_peer' => false, 'verify_host' => false, 'headers' => [ @@ -130,5 +128,4 @@ class PartitionAssistantAction extends AbstractController return new JsonResponse(data: [], status: Response::HTTP_OK); } - } diff --git a/src/Controller/OgAgent/PowerOffAction.php b/src/Controller/OgAgent/PowerOffAction.php index 39f2e5b..8cde198 100644 --- a/src/Controller/OgAgent/PowerOffAction.php +++ b/src/Controller/OgAgent/PowerOffAction.php @@ -53,13 +53,15 @@ class PowerOffAction extends AbstractController continue; } + $endpoint = $client->getStatus() === ClientStatus::OG_LIVE ? 'opengnsys/Apagar' : 'opengnsys/poweroff'; + $data = [ 'nfn' => 'Apagar', 'ids' => '0' ]; try { - $response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/ogAdmClient/Apagar', [ + $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/'.$endpoint, [ 'verify_peer' => false, 'verify_host' => false, 'headers' => [ diff --git a/src/Controller/OgAgent/RebootAction.php b/src/Controller/OgAgent/RebootAction.php index 72d9a43..0be2163 100644 --- a/src/Controller/OgAgent/RebootAction.php +++ b/src/Controller/OgAgent/RebootAction.php @@ -41,6 +41,7 @@ class RebootAction extends AbstractController public function __invoke(MultipleClientsInput $input): JsonResponse { foreach ($input->clients as $clientEntity) { + /** @var Client $client */ $client = $clientEntity->getEntity(); @@ -48,13 +49,15 @@ class RebootAction extends AbstractController throw new ValidatorException('IP is required'); } + $endpoint = $client->getStatus() === ClientStatus::OG_LIVE ? 'opengnsys/Reiniciar' : '/opengnsys/reboot'; + $data = [ 'nfn' => 'Reiniciar', 'ids' => '0' ]; try { - $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/Reiniciar', [ + $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/'.$endpoint, [ 'verify_peer' => false, 'verify_host' => false, 'headers' => [ @@ -62,6 +65,7 @@ class RebootAction extends AbstractController ], 'json' => $data, ]); + $this->logger->info('Rebooting client', ['client' => $client->getId()]); } catch (TransportExceptionInterface $e) { diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index 77b5699..d41b4b2 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -70,6 +70,10 @@ class StatusAction extends AbstractController { $this->logger->info('Checking client status', ['client' => $client->getId()]); + $params = [ + 'full-config' => true, + ]; + try { $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/status', [ 'verify_peer' => false, @@ -78,7 +82,7 @@ class StatusAction extends AbstractController 'headers' => [ 'Content-Type' => 'application/json', ], - 'json' => [], + 'json' => $params, ]); $statusCode = $response->getStatusCode(); $client->setStatus($statusCode === Response::HTTP_OK ? ClientStatus::OG_LIVE : ClientStatus::OFF); diff --git a/src/Controller/OgAgent/Webhook/ClientsController.php b/src/Controller/OgAgent/Webhook/StatusController.php similarity index 98% rename from src/Controller/OgAgent/Webhook/ClientsController.php rename to src/Controller/OgAgent/Webhook/StatusController.php index b5970e3..7973748 100644 --- a/src/Controller/OgAgent/Webhook/ClientsController.php +++ b/src/Controller/OgAgent/Webhook/StatusController.php @@ -32,7 +32,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; #[AsController] -class ClientsController extends AbstractController +class StatusController extends AbstractController { const string CREATE_IMAGE = 'RESPUESTA_CrearImagen'; const string RESTORE_IMAGE = 'RESPUESTA_RestaurarImagen'; @@ -72,7 +72,7 @@ class ClientsController extends AbstractController if (isset($data['progress'])){ $trace = $this->entityManager->getRepository(Trace::class)->findOneBy(['jobId' => $data['job_id']]); if ($trace){ - $trace->setProgress($data['progress'] * 1000); + $trace->setProgress($data['progress'] * 100); $this->entityManager->persist($trace); $this->entityManager->flush(); } diff --git a/src/Controller/OgBoot/PxeBootFile/PostAction.php b/src/Controller/OgBoot/PxeBootFile/PostAction.php index b2245b8..75a30c3 100644 --- a/src/Controller/OgBoot/PxeBootFile/PostAction.php +++ b/src/Controller/OgBoot/PxeBootFile/PostAction.php @@ -40,7 +40,7 @@ class PostAction extends AbstractOgBootController 'json' => [ 'template_name' => $pxeTemplate->getName(), 'mac' => strtolower($client->getMac()), - 'lang' => 'es_ES.UTF_8', + 'lang' => 'es_ES.UTF-8', 'ip' => $client->getIp(), 'server_ip' => $this->ogBootApiUrl, 'router' => $client->getOrganizationalUnit()->getNetworkSettings()->getRouter(), diff --git a/src/Controller/OgRepository/Image/DeployImageAction.php b/src/Controller/OgRepository/Image/DeployImageAction.php index 600163f..64b0099 100644 --- a/src/Controller/OgRepository/Image/DeployImageAction.php +++ b/src/Controller/OgRepository/Image/DeployImageAction.php @@ -45,9 +45,9 @@ class DeployImageAction extends AbstractOgRepositoryController ]; $type = match ($input->method) { - 'udpcast', 'udpcast_direct' => DeployMethodTypes::MULTICAST_UDPCAST, + 'udpcast', 'udpcast-direct' => DeployMethodTypes::MULTICAST_UDPCAST, 'p2p' => DeployMethodTypes::TORRENT, - default => DeployMethodTypes::MULTICAST_UFTP, + default => null, }; $repository = $client->getRepository(); diff --git a/src/Dto/Input/CommandInput.php b/src/Dto/Input/CommandInput.php index 10697d8..d381efc 100644 --- a/src/Dto/Input/CommandInput.php +++ b/src/Dto/Input/CommandInput.php @@ -40,6 +40,13 @@ final class CommandInput )] public ?bool $enabled = true; + #[Groups(['command:write'])] + #[ApiProperty( + description: 'Tiene parámetros?', + example: 'true', + )] + public ?bool $parameters = true; + #[Groups(['command:write'])] #[ApiProperty( description: 'Los comentarios del comando', @@ -57,6 +64,7 @@ final class CommandInput $this->script = $command->getScript(); $this->enabled = $command->isEnabled(); $this->readOnly = $command->isReadOnly(); + $this->parameters = $command->isParameters(); $this->comments = $command->getComments(); } @@ -70,6 +78,7 @@ final class CommandInput $command->setScript($this->script); $command->setEnabled($this->enabled); $command->setReadOnly($this->readOnly); + $command->setParameters($this->parameters); $command->setComments($this->comments); return $command; diff --git a/src/Dto/Output/ClientOutput.php b/src/Dto/Output/ClientOutput.php index f625812..7e7e8ec 100644 --- a/src/Dto/Output/ClientOutput.php +++ b/src/Dto/Output/ClientOutput.php @@ -25,6 +25,9 @@ final class ClientOutput extends AbstractOutput #[Groups(['client:read', 'organizational-unit:read', 'pxe-template:read', 'trace:read', 'subnet:read'])] public ?string $mac = ''; + #[Groups(['client:read'])] + public ?string $firmwareType = ''; + #[Groups(['client:read', 'organizational-unit:read', 'trace:read'])] public ?string $serialNumber = ''; @@ -91,6 +94,7 @@ final class ClientOutput extends AbstractOutput $this->ip = $client->getIp(); $this->netiface = $client->getNetiface(); $this->netDriver = $client->getNetDriver(); + $this->firmwareType = $client->getFirmwareType(); if ($client->getOrganizationalUnit()) { $this->organizationalUnit = new OrganizationalUnitOutput($client->getOrganizationalUnit()); diff --git a/src/Dto/Output/CommandOutput.php b/src/Dto/Output/CommandOutput.php index 394a5d1..0622b70 100644 --- a/src/Dto/Output/CommandOutput.php +++ b/src/Dto/Output/CommandOutput.php @@ -21,6 +21,9 @@ final class CommandOutput extends AbstractOutput #[Groups(['command:read'])] public ?bool $enabled = true; + #[Groups(['command:read'])] + public ?bool $parameters = true; + #[Groups(['command:read'])] public ?string $comments = ''; @@ -37,6 +40,7 @@ final class CommandOutput extends AbstractOutput $this->name = $command->getName(); $this->script = $command->getScript(); $this->readOnly = $command->isReadOnly(); + $this->parameters = $command->isParameters(); $this->enabled = $command->isEnabled(); $this->comments = $command->getComments(); $this->createdAt = $command->getCreatedAt(); diff --git a/src/Dto/Output/ImageImageRepositoryOutput.php b/src/Dto/Output/ImageImageRepositoryOutput.php index a7b3830..665b565 100644 --- a/src/Dto/Output/ImageImageRepositoryOutput.php +++ b/src/Dto/Output/ImageImageRepositoryOutput.php @@ -21,6 +21,9 @@ class ImageImageRepositoryOutput extends AbstractOutput #[Groups(['image-image-repository:read', 'image:read'])] public ?string $imageFullsum = null; + #[Groups(['image-image-repository:read', 'image:read'])] + public ?string $datasize = null; + #[Groups(['image-image-repository:read', 'image:read'])] public \DateTime $createdAt; @@ -43,6 +46,7 @@ class ImageImageRepositoryOutput extends AbstractOutput $this->status = $imageImageRepository->getStatus(); $this->imageFullsum = $imageImageRepository->getImageFullsum(); + $this->datasize = $imageImageRepository->getDatasize(); $this->createdAt = $imageImageRepository->getCreatedAt(); $this->createdBy = $imageImageRepository->getCreatedBy(); } diff --git a/src/Dto/Output/TraceOutput.php b/src/Dto/Output/TraceOutput.php index 14ea96b..94bd188 100644 --- a/src/Dto/Output/TraceOutput.php +++ b/src/Dto/Output/TraceOutput.php @@ -59,7 +59,7 @@ final class TraceOutput extends AbstractOutput $this->output = $trace->getOutput(); $this->input = $trace->getInput(); $this->finishedAt = $trace->getFinishedAt(); - $this->progress = $trace->getProgress() / 100; + $this->progress = $trace->getProgress(); $this->createdAt = $trace->getCreatedAt(); $this->createdBy = $trace->getCreatedBy(); } From bc036b65cf48b7f4e51a2e5f37e32622a6ed14cb Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 1 Apr 2025 10:58:55 +0200 Subject: [PATCH 02/16] refs #1740. Run assistant logic --- config/api_platform/Client.yaml | 7 ++ config/api_platform/Command.yaml | 5 +- config/services/api_platform.yaml | 2 +- src/Controller/OgAgent/RunScriptAction.php | 78 +++++++++++++++++++ src/Controller/OgRepository/SyncAction.php | 15 ++-- src/Dto/Input/CommandExecuteInput.php | 5 ++ src/Entity/Client.php | 15 ++++ src/Entity/Command.php | 15 ++++ src/Entity/ImageImageRepository.php | 15 ++++ .../TraceStatusProgressNotifier.php | 2 +- .../OrganizationalUnitSubscriber.php | 8 +- src/Model/CommandTypes.php | 2 + src/Service/CreatePartitionService.php | 18 ++++- 13 files changed, 171 insertions(+), 16 deletions(-) create mode 100644 src/Controller/OgAgent/RunScriptAction.php diff --git a/config/api_platform/Client.yaml b/config/api_platform/Client.yaml index a4c48f5..ad274d0 100644 --- a/config/api_platform/Client.yaml +++ b/config/api_platform/Client.yaml @@ -50,6 +50,13 @@ resources: uriTemplate: /clients/server/{uuid}/get-pxe controller: App\Controller\OgBoot\PxeBootFile\GetAction + login_client: + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\MultipleClientsInput + uriTemplate: /clients/server/login-client + controller: App\Controller\OgAgent\LoginAction + reboot_client: class: ApiPlatform\Metadata\Post method: POST diff --git a/config/api_platform/Command.yaml b/config/api_platform/Command.yaml index 425259b..0d26a19 100644 --- a/config/api_platform/Command.yaml +++ b/config/api_platform/Command.yaml @@ -28,9 +28,8 @@ resources: class: ApiPlatform\Metadata\Post method: POST input: App\Dto\Input\CommandExecuteInput - uriTemplate: /commands/{uuid}/execute - controller: App\Controller\CommandExecuteAction - + uriTemplate: /commands/run-script + controller: App\Controller\OgAgent\RunScriptAction properties: App\Entity\Command: id: diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index eabd61c..6f91d56 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -42,7 +42,7 @@ services: api_platform.filter.command.boolean: parent: 'api_platform.doctrine.orm.boolean_filter' - arguments: [ { 'enabled': ~ } ] + arguments: [ { 'enabled': ~, 'readOnly': ~ } ] tags: [ 'api_platform.filter' ] api_platform.filter.hardware.order: diff --git a/src/Controller/OgAgent/RunScriptAction.php b/src/Controller/OgAgent/RunScriptAction.php new file mode 100644 index 0000000..ee840ff --- /dev/null +++ b/src/Controller/OgAgent/RunScriptAction.php @@ -0,0 +1,78 @@ +clients as $clientEntity) { + $client = $clientEntity->getEntity(); + + if (!$client->getIp()) { + throw new ValidatorException('IP is required'); + } + + //TODO: base64 script content + $data = [ + 'nfn' => 'EjecutarScript', + 'scp' => $input->script, + 'ids' => '0' + ]; + + try { + $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/EjecutarScript', [ + 'verify_peer' => false, + 'verify_host' => false, + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'json' => $data, + ]); + $this->logger->info('Rebooting client', ['client' => $client->getId()]); + + } catch (TransportExceptionInterface $e) { + $this->logger->error('Error rebooting client', ['client' => $client->getId(), 'error' => $e->getMessage()]); + return new JsonResponse( + data: ['error' => $e->getMessage()], + status: Response::HTTP_INTERNAL_SERVER_ERROR + ); + } + + $jobId = json_decode($response->getContent(), true)['job_id']; + + $this->entityManager->persist($client); + $this->entityManager->flush(); + + $this->createService->__invoke($client, CommandTypes::RUN_SCRIPT, TraceStatus::SUCCESS, $jobId, []); + } + + return new JsonResponse(data: [], status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgRepository/SyncAction.php b/src/Controller/OgRepository/SyncAction.php index f4fd765..11e623c 100644 --- a/src/Controller/OgRepository/SyncAction.php +++ b/src/Controller/OgRepository/SyncAction.php @@ -56,13 +56,16 @@ class SyncAction extends AbstractOgRepositoryController if (!$imageImageRepositoryEntity) { $imageImageRepositoryEntity = new ImageImageRepository(); - $imageImageRepositoryEntity->setImageFullsum($image['fullsum']); - $imageImageRepositoryEntity->setStatus(ImageStatus::SUCCESS); - $imageImageRepositoryEntity->setImage($imageEntity); - $imageImageRepositoryEntity->setRepository($input); - - $this->entityManager->persist($imageImageRepositoryEntity); } + + $imageImageRepositoryEntity->setImageFullsum($image['fullsum']); + $imageImageRepositoryEntity->setDatasize($image['datasize']); + $imageImageRepositoryEntity->setStatus(ImageStatus::SUCCESS); + $imageImageRepositoryEntity->setImage($imageEntity); + $imageImageRepositoryEntity->setRepository($input); + + $this->entityManager->persist($imageImageRepositoryEntity); + } foreach ($existingImages as $existingImage) { diff --git a/src/Dto/Input/CommandExecuteInput.php b/src/Dto/Input/CommandExecuteInput.php index 4630a47..9d903f0 100644 --- a/src/Dto/Input/CommandExecuteInput.php +++ b/src/Dto/Input/CommandExecuteInput.php @@ -14,4 +14,9 @@ final class CommandExecuteInput #[Assert\NotNull] #[Groups(['command:write'])] public array $clients = []; + + + #[Assert\NotNull] + #[Groups(['command:write'])] + public string $script = ''; } \ No newline at end of file diff --git a/src/Entity/Client.php b/src/Entity/Client.php index 6f210ba..d550907 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -82,6 +82,9 @@ class Client extends AbstractEntity #[ORM\Column(nullable: true)] private ?bool $pxeSync = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $firmwareType = null; + public function __construct() { parent::__construct(); @@ -335,4 +338,16 @@ class Client extends AbstractEntity return $this; } + + public function getFirmwareType(): ?string + { + return $this->firmwareType; + } + + public function setFirmwareType(?string $firmwareType): static + { + $this->firmwareType = $firmwareType; + + return $this; + } } diff --git a/src/Entity/Command.php b/src/Entity/Command.php index f43aa30..0fea5cf 100644 --- a/src/Entity/Command.php +++ b/src/Entity/Command.php @@ -35,6 +35,9 @@ class Command extends AbstractEntity #[ORM\ManyToMany(targetEntity: CommandTask::class, mappedBy: 'commands')] private Collection $commandTasks; + #[ORM\Column(nullable: true)] + private ?bool $parameters = null; + public function __construct() { parent::__construct(); @@ -131,4 +134,16 @@ class Command extends AbstractEntity return $this; } + + public function isParameters(): ?bool + { + return $this->parameters; + } + + public function setParameters(?bool $parameters): static + { + $this->parameters = $parameters; + + return $this; + } } diff --git a/src/Entity/ImageImageRepository.php b/src/Entity/ImageImageRepository.php index 169df79..a215c5c 100644 --- a/src/Entity/ImageImageRepository.php +++ b/src/Entity/ImageImageRepository.php @@ -28,6 +28,9 @@ class ImageImageRepository extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?string $imageFullsum = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $datasize = null; + public function getImage(): ?Image { return $this->image; @@ -87,4 +90,16 @@ class ImageImageRepository extends AbstractEntity return $this; } + + public function getDatasize(): ?string + { + return $this->datasize; + } + + public function setDatasize(?string $datasize): static + { + $this->datasize = $datasize; + + return $this; + } } diff --git a/src/EventListener/TraceStatusProgressNotifier.php b/src/EventListener/TraceStatusProgressNotifier.php index 53eaff9..9737c46 100644 --- a/src/EventListener/TraceStatusProgressNotifier.php +++ b/src/EventListener/TraceStatusProgressNotifier.php @@ -42,7 +42,7 @@ class TraceStatusProgressNotifier { $update = new Update( 'traces', - json_encode(['@id' => '/traces/' . $trace->getUuid(), 'status' => $trace->getStatus(), 'progress' => $trace->getProgress() / 100]) + json_encode(['@id' => '/traces/' . $trace->getUuid(), 'status' => $trace->getStatus(), 'progress' => $trace->getProgress()]) ); $this->hub->publish($update); diff --git a/src/EventSubscriber/OrganizationalUnitSubscriber.php b/src/EventSubscriber/OrganizationalUnitSubscriber.php index d305545..57f0adb 100644 --- a/src/EventSubscriber/OrganizationalUnitSubscriber.php +++ b/src/EventSubscriber/OrganizationalUnitSubscriber.php @@ -58,12 +58,18 @@ final readonly class OrganizationalUnitSubscriber implements EventSubscriberInte { /** @var OrganizationalUnit $childUnit */ foreach ($parentUnit->getOrganizationalUnits() as $childUnit) { - //var_dump($childUnit->getNetworkSettings()->getMcastPort()); if ($childUnit->isExcludeParentChanges()) { $childUnit->setNetworkSettings(null); } else{ $childUnit->setNetworkSettings($networkSettings); + foreach ($childUnit->getClients() as $client) { + $client->setOgLive($networkSettings->getOgLive()); + $client->setMenu($networkSettings->getMenu()); + $client->setRepository($networkSettings->getRepository()); + $this->entityManager->persist($client); + } + } $this->entityManager->persist($childUnit); diff --git a/src/Model/CommandTypes.php b/src/Model/CommandTypes.php index d86c277..40c65af 100644 --- a/src/Model/CommandTypes.php +++ b/src/Model/CommandTypes.php @@ -21,6 +21,7 @@ final class CommandTypes public const string LOGOUT = 'logout'; public const string PARTITION_AND_FORMAT = 'partition-and-format'; public const string INSTALL_OGLIVE = 'install-oglive'; + public const string RUN_SCRIPT = 'run-script'; private const array COMMAND_TYPES = [ self::DEPLOY_IMAGE => 'Deploy Image', @@ -40,6 +41,7 @@ final class CommandTypes self::PARTITION_AND_FORMAT => 'Partition and Format', self::TRANSFER_IMAGE => 'Transfer Image', self::INSTALL_OGLIVE => 'Instalar OgLive', + self::RUN_SCRIPT => 'Run Script', ]; public static function getCommandTypes(): array diff --git a/src/Service/CreatePartitionService.php b/src/Service/CreatePartitionService.php index 5b63ea8..f5153b1 100644 --- a/src/Service/CreatePartitionService.php +++ b/src/Service/CreatePartitionService.php @@ -26,11 +26,19 @@ class CreatePartitionService $receivedPartitions = []; - foreach ($data['cfg'] as $cfg) { - if (!isset($cfg['disk'], $cfg['par'], $cfg['tam'], $cfg['uso'], $cfg['fsi'])) { - continue; - } + $filteredCfg = array_filter($data['cfg'], function ($cfg) { + return isset($cfg['disk'], $cfg['par'], $cfg['tam'], $cfg['uso'], $cfg['fsi']); + }); + foreach ($data['cfg'] as $cfg) { + if (!empty($cfg['fwt'])) { + $clientEntity->setFirmwareType($cfg['fwt']); + $this->entityManager->persist($clientEntity); + break; + } + } + + foreach ($filteredCfg as $cfg) { $partitionEntity = $this->entityManager->getRepository(Partition::class) ->findOneBy(['client' => $clientEntity, 'diskNumber' => $cfg['disk'], 'partitionNumber' => $cfg['par']]); @@ -38,6 +46,8 @@ class CreatePartitionService $partitionEntity = new Partition(); } + $partitionEntity->setOperativeSystem(null); + if (isset($cfg['soi']) && $cfg['soi'] !== '') { $operativeSystem = $this->entityManager->getRepository(OperativeSystem::class) ->findOneBy(['name' => $cfg['soi']]); From 912cf9b00825ff1d3c6589973d3e7f767172dd34 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 1 Apr 2025 19:44:59 +0200 Subject: [PATCH 03/16] refs #1797. Rename image. Crete image --- .../OgRepository/Image/RenameAction.php | 81 +++++++++++++++++++ src/Dto/Input/ImageInput.php | 6 +- src/State/Processor/ImageProcessor.php | 11 ++- 3 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 src/Controller/OgRepository/Image/RenameAction.php diff --git a/src/Controller/OgRepository/Image/RenameAction.php b/src/Controller/OgRepository/Image/RenameAction.php new file mode 100644 index 0000000..8d83633 --- /dev/null +++ b/src/Controller/OgRepository/Image/RenameAction.php @@ -0,0 +1,81 @@ +getImageImageRepositories(); + + if ($repositories->count() === 0) { + return new JsonResponse(data: ['error' => 'Image is not in any repository', 'code' => Response::HTTP_INTERNAL_SERVER_ERROR], status: Response::HTTP_BAD_REQUEST); + } + + $allGood = true; + foreach ($repositories as $repository) { + try { + $content = $this->createRequest('GET', 'http://'.$repository->getRepository()->getIp(). ':8006/ogrepository/v1/status'); + } catch (TransportExceptionInterface $e) { + $allGood = false; + break; + } + } + + if (!$allGood) { + $this->logger->info('Image is not available in all repositories', ['image' => $image->getName()]); + return new JsonResponse(data: ['error' => 'Image is not available in all repositories', 'code' => Response::HTTP_INTERNAL_SERVER_ERROR], status: Response::HTTP_BAD_REQUEST); + } + + $conditional = false; + foreach ($repositories as $repository) { + $params = [ + 'json' => [ + 'ID_img' => $repository->getImageFullsum(), + 'image_new_name' => $image->getName().'_bkp', + ] + ]; + + $content = $this->createRequest('POST', 'http://'.$repository->getRepository()->getIp().':8006/ogrepository/v1/images/rename', $params); + + if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + $conditional = true; + break; + } + } + + if ($conditional) { + return new JsonResponse(data: ['error' => 'Error renaming image'], status: Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $image->setName($image->getName().'_bkp'); + $this->entityManager->persist($image); + $this->entityManager->flush(); + + return new JsonResponse(data: [], status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Dto/Input/ImageInput.php b/src/Dto/Input/ImageInput.php index fc79464..abb8e01 100644 --- a/src/Dto/Input/ImageInput.php +++ b/src/Dto/Input/ImageInput.php @@ -5,6 +5,7 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; use App\Dto\Output\ClientOutput; use App\Dto\Output\ImageImageRepositoryOutput; +use App\Dto\Output\ImageOutput; use App\Dto\Output\ImageRepositoryOutput; use App\Dto\Output\OrganizationalUnitOutput; use App\Dto\Output\PartitionOutput; @@ -20,7 +21,6 @@ use Symfony\Component\Validator\Constraints as Assert; final class ImageInput { - #[Assert\NotBlank(message: 'validators.image.name.not_blank')] #[Groups(['image:write'])] #[ApiProperty(description: 'The name of the image', example: "Image 1")] public ?string $name = null; @@ -37,6 +37,10 @@ final class ImageInput #[ApiProperty(description: 'The type of the image', example: "Server")] public ?string $source = 'input'; + #[Groups(['image:write'])] + #[ApiProperty(description: 'The optional selected image')] + public ?ImageOutput $selectedImage = null; + #[Groups(['image:write'])] #[ApiProperty(description: 'The software profile of the image')] public ?SoftwareProfileOutput $softwareProfile = null; diff --git a/src/State/Processor/ImageProcessor.php b/src/State/Processor/ImageProcessor.php index ac7ad1a..80a9b4d 100644 --- a/src/State/Processor/ImageProcessor.php +++ b/src/State/Processor/ImageProcessor.php @@ -10,6 +10,7 @@ use ApiPlatform\Metadata\Put; use ApiPlatform\State\ProcessorInterface; use ApiPlatform\Validator\ValidatorInterface; use App\Controller\OgAgent\CreateImageAction; +use App\Controller\OgRepository\Image\RenameAction; use App\Controller\OgRepository\Image\TransferAction; use App\Dto\Input\ImageInput; use App\Dto\Input\ImageRepositoryInput; @@ -26,6 +27,7 @@ readonly class ImageProcessor implements ProcessorInterface private ImageRepository $imageRepository, private ValidatorInterface $validator, private CreateImageAction $createImageActionController, + private RenameAction $renameActionController, ) { } @@ -64,10 +66,15 @@ readonly class ImageProcessor implements ProcessorInterface $image = $data->createOrUpdateEntity($entity); $this->validator->validate($image); - if ($data->source !== 'input') { - $response = $this->createImageActionController->__invoke($image); + if ($data->selectedImage){ + $content = $this->renameActionController->__invoke($image); + + if ($content->getStatusCode() !== 200){ + throw new \Exception('Error renaming image'); + } } + $response = $this->createImageActionController->__invoke($image); $this->imageRepository->save($image); return new ImageOutput($image); From e119c14451e0439a2347fbbf0f71874da53a155d Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 2 Apr 2025 21:28:39 +0200 Subject: [PATCH 04/16] refs #1741. Syslog --- config/packages/monolog.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/config/packages/monolog.yaml b/config/packages/monolog.yaml index d0608d0..59cb0fb 100644 --- a/config/packages/monolog.yaml +++ b/config/packages/monolog.yaml @@ -15,6 +15,12 @@ when@dev: type: console process_psr_3_messages: false channels: ["!event", "!doctrine", "!console"] + syslog: + type: syslog + ident: "ogcore" + level: info + formatter: App\Formatter\CustomLineFormatter + channels: ["!event"] when@test: monolog: @@ -42,6 +48,12 @@ when@prod: type: console process_psr_3_messages: false channels: ["!event", "!doctrine"] + syslog: + type: syslog + ident: "ogcore" + level: info + formatter: App\Formatter\CustomLineFormatter + channels: ["!event"] deprecation: type: stream channels: [deprecation] From d8306f78a7aa138b6bc25ed858c56fe938c235e8 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 3 Apr 2025 08:41:59 +0200 Subject: [PATCH 05/16] refs #1797. Rename image. Versions added --- migrations/Version20250402060324.php | 31 +++++++++++++++++++ migrations/Version20250402081107.php | 31 +++++++++++++++++++ migrations/Version20250402094550.php | 31 +++++++++++++++++++ src/Controller/OgAgent/CreateImageAction.php | 5 ++- src/Controller/OgAgent/RunScriptAction.php | 7 ++++- .../OgRepository/Image/RenameAction.php | 17 ++++++---- src/Dto/Input/ImageInput.php | 6 ++++ src/Dto/Output/ImageImageRepositoryOutput.php | 4 +++ src/Dto/Output/ImageOutput.php | 4 +++ src/Entity/Image.php | 15 +++++++++ src/Entity/ImageImageRepository.php | 17 ++++++++-- src/Model/CommandTypes.php | 2 ++ .../ImageImageRepositoryRepository.php | 10 ++++++ src/State/Processor/ImageProcessor.php | 16 +++++----- 14 files changed, 179 insertions(+), 17 deletions(-) create mode 100644 migrations/Version20250402060324.php create mode 100644 migrations/Version20250402081107.php create mode 100644 migrations/Version20250402094550.php diff --git a/migrations/Version20250402060324.php b/migrations/Version20250402060324.php new file mode 100644 index 0000000..f3b99b7 --- /dev/null +++ b/migrations/Version20250402060324.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image ADD version INT DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE image DROP version'); + } +} diff --git a/migrations/Version20250402081107.php b/migrations/Version20250402081107.php new file mode 100644 index 0000000..e03a6f1 --- /dev/null +++ b/migrations/Version20250402081107.php @@ -0,0 +1,31 @@ +addSql('DROP INDEX UNIQ_IDENTIFIER_IMAGE_REPOSITORY ON image_image_repository'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_IMAGE_REPOSITORY ON image_image_repository (image_id, repository_id)'); + } +} diff --git a/migrations/Version20250402094550.php b/migrations/Version20250402094550.php new file mode 100644 index 0000000..fb6d618 --- /dev/null +++ b/migrations/Version20250402094550.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image_image_repository ADD version INT DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE image_image_repository DROP version'); + } +} diff --git a/src/Controller/OgAgent/CreateImageAction.php b/src/Controller/OgAgent/CreateImageAction.php index 2a0d485..a98033b 100644 --- a/src/Controller/OgAgent/CreateImageAction.php +++ b/src/Controller/OgAgent/CreateImageAction.php @@ -56,10 +56,13 @@ class CreateImageAction extends AbstractController $repository = $image->getClient()->getRepository(); + $latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersion(); + $imageImageRepository = new ImageImageRepository(); $imageImageRepository->setImage($image); $imageImageRepository->setRepository($repository); $imageImageRepository->setStatus(ImageStatus::IN_PROGRESS); + $imageImageRepository->setVersion($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1); $this->entityManager->persist($imageImageRepository); @@ -90,7 +93,7 @@ class CreateImageAction extends AbstractController try { $this->logger->info('Creating image', ['image' => $image->getId()]); - $response = $this->httpClient->request('POST', 'https://'.$image->getClient()->getIp().':8000/opengnsys/CrearImagen', [ + $response = $this->httpClient->request('POST', 'https://'.$image->getClient()->getIp().':8000/CloningEngine/CrearImagen', [ 'verify_peer' => false, 'verify_host' => false, 'headers' => [ diff --git a/src/Controller/OgAgent/RunScriptAction.php b/src/Controller/OgAgent/RunScriptAction.php index ee840ff..170c91b 100644 --- a/src/Controller/OgAgent/RunScriptAction.php +++ b/src/Controller/OgAgent/RunScriptAction.php @@ -70,7 +70,12 @@ class RunScriptAction extends AbstractController $this->entityManager->persist($client); $this->entityManager->flush(); - $this->createService->__invoke($client, CommandTypes::RUN_SCRIPT, TraceStatus::SUCCESS, $jobId, []); + $inputData = [ + 'script' => $input->script, + ]; + + + $this->createService->__invoke($client, CommandTypes::RUN_SCRIPT, TraceStatus::SUCCESS, $jobId, $inputData); } return new JsonResponse(data: [], status: Response::HTTP_OK); diff --git a/src/Controller/OgRepository/Image/RenameAction.php b/src/Controller/OgRepository/Image/RenameAction.php index 8d83633..d66ac9c 100644 --- a/src/Controller/OgRepository/Image/RenameAction.php +++ b/src/Controller/OgRepository/Image/RenameAction.php @@ -51,20 +51,27 @@ class RenameAction extends AbstractOgRepositoryController return new JsonResponse(data: ['error' => 'Image is not available in all repositories', 'code' => Response::HTTP_INTERNAL_SERVER_ERROR], status: Response::HTTP_BAD_REQUEST); } + $image->setVersion($image->getVersion() + 1); + $this->entityManager->persist($image); + $conditional = false; - foreach ($repositories as $repository) { + + $latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersion(); + $repoWithImage = $this->entityManager->getRepository(ImageImageRepository::class)->findBy(['image' => $image, 'version' => $latestImageRepo->getVersion()]); + + + foreach ($repoWithImage as $repository) { $params = [ 'json' => [ 'ID_img' => $repository->getImageFullsum(), - 'image_new_name' => $image->getName().'_bkp', + 'image_new_name' => $image->getName().'_v'.$image->getVersion(), ] ]; - $content = $this->createRequest('POST', 'http://'.$repository->getRepository()->getIp().':8006/ogrepository/v1/images/rename', $params); + $content = $this->createRequest('PUT', 'http://'.$repository->getRepository()->getIp().':8006/ogrepository/v1/images/rename', $params); if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { $conditional = true; - break; } } @@ -72,8 +79,6 @@ class RenameAction extends AbstractOgRepositoryController return new JsonResponse(data: ['error' => 'Error renaming image'], status: Response::HTTP_INTERNAL_SERVER_ERROR); } - $image->setName($image->getName().'_bkp'); - $this->entityManager->persist($image); $this->entityManager->flush(); return new JsonResponse(data: [], status: Response::HTTP_OK); diff --git a/src/Dto/Input/ImageInput.php b/src/Dto/Input/ImageInput.php index abb8e01..00dd01d 100644 --- a/src/Dto/Input/ImageInput.php +++ b/src/Dto/Input/ImageInput.php @@ -21,6 +21,7 @@ use Symfony\Component\Validator\Constraints as Assert; final class ImageInput { + #[Assert\NotBlank(message: 'validators.image.name.not_blank')] #[Groups(['image:write'])] #[ApiProperty(description: 'The name of the image', example: "Image 1")] public ?string $name = null; @@ -64,6 +65,10 @@ final class ImageInput #[ApiProperty(description: 'The parent of the image')] public ?self $parent = null; + #[Groups(['image:write'])] + #[ApiProperty(description: 'The parent of the image')] + public ?int $version = null; + #[Groups(['image:write'])] #[ApiProperty(description: 'The remote pc of the image')] public ?bool $remotePc = false; @@ -83,6 +88,7 @@ final class ImageInput $this->comments = $image->getComments(); $this->remotePc = $image->isRemotePc(); $this->isGlobal = $image->isGlobal(); + $this->version = $image->getVersion(); if ($image->getSoftwareProfile()) { $this->softwareProfile = new SoftwareProfileOutput($image->getSoftwareProfile()); diff --git a/src/Dto/Output/ImageImageRepositoryOutput.php b/src/Dto/Output/ImageImageRepositoryOutput.php index 665b565..2db73bc 100644 --- a/src/Dto/Output/ImageImageRepositoryOutput.php +++ b/src/Dto/Output/ImageImageRepositoryOutput.php @@ -24,6 +24,9 @@ class ImageImageRepositoryOutput extends AbstractOutput #[Groups(['image-image-repository:read', 'image:read'])] public ?string $datasize = null; + #[Groups(['image:read', 'image-image-repository:read'])] + public ?int $version = null; + #[Groups(['image-image-repository:read', 'image:read'])] public \DateTime $createdAt; @@ -44,6 +47,7 @@ class ImageImageRepositoryOutput extends AbstractOutput $this->imageRepository = new ImageRepositoryOutput($imageImageRepository->getRepository()); } + $this->version = $imageImageRepository->getVersion(); $this->status = $imageImageRepository->getStatus(); $this->imageFullsum = $imageImageRepository->getImageFullsum(); $this->datasize = $imageImageRepository->getDatasize(); diff --git a/src/Dto/Output/ImageOutput.php b/src/Dto/Output/ImageOutput.php index d1f4aa1..5daead3 100644 --- a/src/Dto/Output/ImageOutput.php +++ b/src/Dto/Output/ImageOutput.php @@ -39,6 +39,9 @@ final class ImageOutput extends AbstractOutput #[Groups(['image:read'])] public ?array $partitionInfo = null; + #[Groups(['image:read', 'image-image-repository:read'])] + public ?int $version = null; + #[Groups(['image:read'])] public \DateTime $createdAt; @@ -58,6 +61,7 @@ final class ImageOutput extends AbstractOutput fn(ImageImageRepository $image) => new ImageImageRepositoryOutput($image) )->toArray(); + $this->version = $image->getVersion(); $this->partitionInfo = json_decode($image->getPartitionInfo(), true); $this->remotePc = $image->isRemotePc(); $this->isGlobal = $image->isGlobal(); diff --git a/src/Entity/Image.php b/src/Entity/Image.php index e9607d7..727c212 100644 --- a/src/Entity/Image.php +++ b/src/Entity/Image.php @@ -46,6 +46,9 @@ class Image extends AbstractEntity #[ORM\OneToMany(mappedBy: 'image', targetEntity: ImageImageRepository::class, cascade: ['persist'], orphanRemoval: true)] private Collection $imageImageRepositories; + #[ORM\Column(nullable: true)] + private ?int $version = null; + public function __construct() { parent::__construct(); @@ -191,4 +194,16 @@ class Image extends AbstractEntity return false; } + + public function getVersion(): ?int + { + return $this->version; + } + + public function setVersion(?int $version): static + { + $this->version = $version; + + return $this; + } } diff --git a/src/Entity/ImageImageRepository.php b/src/Entity/ImageImageRepository.php index a215c5c..d80012c 100644 --- a/src/Entity/ImageImageRepository.php +++ b/src/Entity/ImageImageRepository.php @@ -7,8 +7,6 @@ use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; #[ORM\Entity(repositoryClass: ImageImageRepositoryRepository::class)] -#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_IMAGE_REPOSITORY', columns: ['image_id', 'repository_id'])] -#[UniqueEntity(fields: ['image', 'repository'], message: 'This image is already associated with this repository')] class ImageImageRepository extends AbstractEntity { #[ORM\ManyToOne(targetEntity: Image::class, cascade: ['persist'], inversedBy: 'imageImageRepositories')] @@ -31,6 +29,9 @@ class ImageImageRepository extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?string $datasize = null; + #[ORM\Column(nullable: true)] + private ?int $version = null; + public function getImage(): ?Image { return $this->image; @@ -102,4 +103,16 @@ class ImageImageRepository extends AbstractEntity return $this; } + + public function getVersion(): ?int + { + return $this->version; + } + + public function setVersion(?int $version): static + { + $this->version = $version; + + return $this; + } } diff --git a/src/Model/CommandTypes.php b/src/Model/CommandTypes.php index 40c65af..4f31c21 100644 --- a/src/Model/CommandTypes.php +++ b/src/Model/CommandTypes.php @@ -12,6 +12,7 @@ final class CommandTypes public const string BACKUP_IMAGE = 'backup-image'; public const string IMPORT_IMAGE = 'import-image'; public const string EXPORT_IMAGE = 'export-image'; + public const string RENAME_IMAGE = 'rename-image'; public const string CONVERT_IMAGE_TO_VIRTUAL = 'convert-image-to-virtual'; public const string TRANSFER_IMAGE = 'transfer-image'; public const string POWER_ON = 'power-on'; @@ -33,6 +34,7 @@ final class CommandTypes self::BACKUP_IMAGE => 'Backup Image', self::IMPORT_IMAGE => 'Import image', self::EXPORT_IMAGE => 'Export image', + self::RENAME_IMAGE => 'Rename Image', self::POWER_ON => 'Encender', self::REBOOT => 'Reiniciar', self::SHUTDOWN => 'Apagar', diff --git a/src/Repository/ImageImageRepositoryRepository.php b/src/Repository/ImageImageRepositoryRepository.php index a72dc6b..65c57f4 100644 --- a/src/Repository/ImageImageRepositoryRepository.php +++ b/src/Repository/ImageImageRepositoryRepository.php @@ -15,4 +15,14 @@ class ImageImageRepositoryRepository extends AbstractRepository { parent::__construct($registry, ImageImageRepository::class); } + + public function findLatestVersion(): ?ImageImageRepository + { + return $this->createQueryBuilder('i') + ->orderBy('i.version', 'DESC') + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult(); + } + } diff --git a/src/State/Processor/ImageProcessor.php b/src/State/Processor/ImageProcessor.php index 80a9b4d..d4e2f18 100644 --- a/src/State/Processor/ImageProcessor.php +++ b/src/State/Processor/ImageProcessor.php @@ -63,21 +63,23 @@ readonly class ImageProcessor implements ProcessorInterface $entity = $this->imageRepository->findOneByUuid($uriVariables['uuid']); } - $image = $data->createOrUpdateEntity($entity); - $this->validator->validate($image); if ($data->selectedImage){ - $content = $this->renameActionController->__invoke($image); + $content = $this->renameActionController->__invoke($data->selectedImage->getEntity()); if ($content->getStatusCode() !== 200){ throw new \Exception('Error renaming image'); } + $response = $this->createImageActionController->__invoke($data->selectedImage->getEntity()); + + } else { + $image = $data->createOrUpdateEntity($entity); + + $response = $this->createImageActionController->__invoke($image); + $this->imageRepository->save($image); } - $response = $this->createImageActionController->__invoke($image); - $this->imageRepository->save($image); - - return new ImageOutput($image); + return new ImageOutput($data->selectedImage?->getEntity() ?? $image); } private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null From beeda955ef86506e65b1a2e2c805beb77a2810b0 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 7 Apr 2025 08:23:12 +0200 Subject: [PATCH 06/16] OgRepo createImage improvements. Agent Status fixed --- config/api_platform/Command.yaml | 2 ++ src/Controller/DeployImageAction.php | 2 +- src/Controller/OgAgent/CreateImageAction.php | 7 ++++--- src/Controller/OgAgent/StatusAction.php | 6 +++--- .../OgRepository/Image/CreateAuxFilesAction.php | 2 +- .../OgRepository/Image/DeletePermanentAction.php | 9 +++++++++ .../OgRepository/Image/DeployImageAction.php | 2 +- src/Repository/ImageImageRepositoryRepository.php | 15 +++++++++++++++ src/State/Processor/ImageProcessor.php | 5 +---- 9 files changed, 37 insertions(+), 13 deletions(-) diff --git a/config/api_platform/Command.yaml b/config/api_platform/Command.yaml index 0d26a19..c89428b 100644 --- a/config/api_platform/Command.yaml +++ b/config/api_platform/Command.yaml @@ -7,6 +7,8 @@ resources: groups: ['default', 'command:read'] denormalizationContext: groups: ['command:write'] + order: + id: 'DESC' operations: ApiPlatform\Metadata\GetCollection: provider: App\State\Provider\CommandProvider diff --git a/src/Controller/DeployImageAction.php b/src/Controller/DeployImageAction.php index ae59bc3..4912d29 100644 --- a/src/Controller/DeployImageAction.php +++ b/src/Controller/DeployImageAction.php @@ -87,7 +87,7 @@ class DeployImageAction extends AbstractController ]; try { - $this->deployImageOgRepositoryAction->__invoke($input, $image, $client->getEntity(), $this->httpClient); + $this->deployImageOgRepositoryAction->__invoke($input, $image, $client->getEntity()); } catch (\Exception $e) { continue; } diff --git a/src/Controller/OgAgent/CreateImageAction.php b/src/Controller/OgAgent/CreateImageAction.php index a98033b..2ef1cb0 100644 --- a/src/Controller/OgAgent/CreateImageAction.php +++ b/src/Controller/OgAgent/CreateImageAction.php @@ -8,6 +8,7 @@ use App\Entity\Client; use App\Entity\Command; use App\Entity\Image; use App\Entity\ImageImageRepository; +use App\Entity\ImageRepository; use App\Entity\Trace; use App\Model\ClientStatus; use App\Model\CommandTypes; @@ -56,7 +57,7 @@ class CreateImageAction extends AbstractController $repository = $image->getClient()->getRepository(); - $latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersion(); + $latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersionByImageAndRepository($image, $repository); $imageImageRepository = new ImageImageRepository(); $imageImageRepository->setImage($image); @@ -71,7 +72,7 @@ class CreateImageAction extends AbstractController 'par' => (string) $partitionInfo['numPartition'], 'cpt' => null, 'idi' => $imageImageRepository->getUuid(), - 'nci' => $image->getName(), + 'nci' => $image->getName().'_v'.$imageImageRepository->getVersion(), 'ipr' => $repository->getIp(), 'nfn' => 'CrearImagen', 'ids' => '0' @@ -93,7 +94,7 @@ class CreateImageAction extends AbstractController try { $this->logger->info('Creating image', ['image' => $image->getId()]); - $response = $this->httpClient->request('POST', 'https://'.$image->getClient()->getIp().':8000/CloningEngine/CrearImagen', [ + $response = $this->httpClient->request('POST', 'https://'.$image->getClient()->getIp().':8000/opengnsys/CrearImagen', [ 'verify_peer' => false, 'verify_host' => false, 'headers' => [ diff --git a/src/Controller/OgAgent/StatusAction.php b/src/Controller/OgAgent/StatusAction.php index d41b4b2..dde6331 100644 --- a/src/Controller/OgAgent/StatusAction.php +++ b/src/Controller/OgAgent/StatusAction.php @@ -71,14 +71,14 @@ class StatusAction extends AbstractController $this->logger->info('Checking client status', ['client' => $client->getId()]); $params = [ - 'full-config' => true, + 'full-config' => false, ]; try { - $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/status', [ + $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/opengnsys/status', [ 'verify_peer' => false, 'verify_host' => false, - 'timeout' => 30, + 'timeout' => 10, 'headers' => [ 'Content-Type' => 'application/json', ], diff --git a/src/Controller/OgRepository/Image/CreateAuxFilesAction.php b/src/Controller/OgRepository/Image/CreateAuxFilesAction.php index d5ebd1f..9da0161 100644 --- a/src/Controller/OgRepository/Image/CreateAuxFilesAction.php +++ b/src/Controller/OgRepository/Image/CreateAuxFilesAction.php @@ -39,7 +39,7 @@ class CreateAuxFilesAction extends AbstractOgRepositoryController $params = [ 'json' => [ - 'image' => $image->getName().'.img' + 'image' => $image->getName().'_v'.$data->getVersion().'.img' ] ]; diff --git a/src/Controller/OgRepository/Image/DeletePermanentAction.php b/src/Controller/OgRepository/Image/DeletePermanentAction.php index 7b14d90..892936a 100644 --- a/src/Controller/OgRepository/Image/DeletePermanentAction.php +++ b/src/Controller/OgRepository/Image/DeletePermanentAction.php @@ -5,6 +5,7 @@ namespace App\Controller\OgRepository\Image; use App\Controller\OgRepository\AbstractOgRepositoryController; use App\Entity\Image; use App\Entity\ImageImageRepository; +use App\Entity\ImageRepository; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -30,6 +31,7 @@ class DeletePermanentAction extends AbstractOgRepositoryController throw new ValidatorException('Fullsum is required'); } + /** @var ImageRepository $image */ $image = $data->getImage(); $this->logger->info('Deleting image', ['image' => $image->getName()]); @@ -43,6 +45,13 @@ class DeletePermanentAction extends AbstractOgRepositoryController $this->entityManager->remove($data); $this->entityManager->flush(); + $imageImageCollection = $image->getImageImageRepositories(); + + if ($imageImageCollection->isEmpty()) { + $this->entityManager->remove($image); + $this->entityManager->flush(); + } + return new JsonResponse(data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgRepository/Image/DeployImageAction.php b/src/Controller/OgRepository/Image/DeployImageAction.php index 64b0099..8b8eb8c 100644 --- a/src/Controller/OgRepository/Image/DeployImageAction.php +++ b/src/Controller/OgRepository/Image/DeployImageAction.php @@ -30,7 +30,7 @@ class DeployImageAction extends AbstractOgRepositoryController * @throws ClientExceptionInterface * @throws TransportExceptionInterface */ - public function __invoke(DeployImageInput $input, ImageImageRepository $data, Client $client, HttpClientInterface $httpClient): JsonResponse + public function __invoke(DeployImageInput $input, ImageImageRepository $data, Client $client): JsonResponse { $params = [ 'json' => [ diff --git a/src/Repository/ImageImageRepositoryRepository.php b/src/Repository/ImageImageRepositoryRepository.php index 65c57f4..c40e35d 100644 --- a/src/Repository/ImageImageRepositoryRepository.php +++ b/src/Repository/ImageImageRepositoryRepository.php @@ -2,7 +2,9 @@ namespace App\Repository; +use App\Entity\Image; use App\Entity\ImageImageRepository; +use App\Entity\ImageRepository as Repository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; @@ -16,6 +18,19 @@ class ImageImageRepositoryRepository extends AbstractRepository parent::__construct($registry, ImageImageRepository::class); } + public function findLatestVersionByImageAndRepository(Image $image, Repository $repository): ?ImageImageRepository + { + return $this->createQueryBuilder('i') + ->andWhere('i.image = :imageId') + ->setParameter('imageId', $image->getId()) + ->andWhere('i.repository = :repository') + ->setParameter('repository', $repository->getId()) + ->orderBy('i.version', 'DESC') + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult(); + } + public function findLatestVersion(): ?ImageImageRepository { return $this->createQueryBuilder('i') diff --git a/src/State/Processor/ImageProcessor.php b/src/State/Processor/ImageProcessor.php index d4e2f18..12bcf5d 100644 --- a/src/State/Processor/ImageProcessor.php +++ b/src/State/Processor/ImageProcessor.php @@ -65,11 +65,8 @@ readonly class ImageProcessor implements ProcessorInterface if ($data->selectedImage){ - $content = $this->renameActionController->__invoke($data->selectedImage->getEntity()); + //$content = $this->renameActionController->__invoke($data->selectedImage->getEntity()); - if ($content->getStatusCode() !== 200){ - throw new \Exception('Error renaming image'); - } $response = $this->createImageActionController->__invoke($data->selectedImage->getEntity()); } else { From b6c62996f56f05c0ea65b1f152c0454b1bec12f7 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 7 Apr 2025 15:22:45 +0200 Subject: [PATCH 07/16] refs #1855. Fixed subnet DTOs clients property --- src/Dto/Output/SubnetOutput.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Dto/Output/SubnetOutput.php b/src/Dto/Output/SubnetOutput.php index 0a9b540..672e6d1 100644 --- a/src/Dto/Output/SubnetOutput.php +++ b/src/Dto/Output/SubnetOutput.php @@ -33,9 +33,6 @@ final class SubnetOutput extends AbstractOutput #[Groups(['subnet:read'])] public ?string $dns = null; - #[Groups(['subnet:read'])] - public array $clients; - #[Groups(['subnet:read'])] public ?bool $synchronized = false; @@ -61,11 +58,6 @@ final class SubnetOutput extends AbstractOutput $this->bootFileName = $subnet->getBootFileName(); $this->synchronized = $subnet->isSynchronized(); $this->serverId = $subnet->getServerId(); - - $this->clients = $subnet->getClients()->map( - fn(Client $client) => new ClientOutput($client) - )->toArray(); - $this->createdAt = $subnet->getCreatedAt(); $this->createdBy = $subnet->getCreatedBy(); } From 097c6a710ecea28db22c952b3feffd8290c3d822 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Apr 2025 15:34:25 +0200 Subject: [PATCH 08/16] refs #1851. Added script/funcionality to check PC availability --- migrations/Version20250407154425.php | 33 +++++++ migrations/Version20250407154620.php | 31 ++++++ src/Command/CheckClientAvailability.php | 98 +++++++++++++++++++ src/Command/TestCommand.php | 44 --------- .../OgAgent/Webhook/StatusController.php | 23 +++-- src/Entity/Client.php | 3 + src/EventSubscriber/MercureSubscriber.php | 7 +- src/Model/ClientStatus.php | 2 + 8 files changed, 189 insertions(+), 52 deletions(-) create mode 100644 migrations/Version20250407154425.php create mode 100644 migrations/Version20250407154620.php create mode 100644 src/Command/CheckClientAvailability.php delete mode 100644 src/Command/TestCommand.php diff --git a/migrations/Version20250407154425.php b/migrations/Version20250407154425.php new file mode 100644 index 0000000..c472a1d --- /dev/null +++ b/migrations/Version20250407154425.php @@ -0,0 +1,33 @@ +addSql('CREATE INDEX IDX_STATUS ON client (status)'); + $this->addSql('CREATE INDEX IDX_UPDATED_AT ON client (updated_at)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP INDEX IDX_STATUS ON client'); + $this->addSql('DROP INDEX IDX_UPDATED_AT ON client'); + } +} diff --git a/migrations/Version20250407154620.php b/migrations/Version20250407154620.php new file mode 100644 index 0000000..5a2b12d --- /dev/null +++ b/migrations/Version20250407154620.php @@ -0,0 +1,31 @@ +addSql('CREATE INDEX IDX_STATUS_UPDATED_AT ON client (status, updated_at)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP INDEX IDX_STATUS_UPDATED_AT ON client'); + } +} diff --git a/src/Command/CheckClientAvailability.php b/src/Command/CheckClientAvailability.php new file mode 100644 index 0000000..bf7419e --- /dev/null +++ b/src/Command/CheckClientAvailability.php @@ -0,0 +1,98 @@ +modify(' - '.self::THRESHOLD_MINUTES . ' minutes'); + + $startQueryTime = microtime(true); + + $query = $this->entityManager->createQuery( + 'UPDATE App\Entity\Client c + SET c.status = :status + WHERE c.status = :currentStatus AND c.updatedAt < :threshold' + ); + $query->setParameter('status', ClientStatus::DISCONNECTED); + $query->setParameter('currentStatus', ClientStatus::OG_LIVE); + $query->setParameter('threshold', $threshold); + $updatedCount = $query->execute(); + + $queryTime = microtime(true) - $startQueryTime; + + $startMercureTime = microtime(true); + + $clients = $this->entityManager->createQueryBuilder() + ->select('c') + ->from(Client::class, 'c') + ->where('c.status = :status') + ->andWhere('c.updatedAt < :threshold') + ->setParameter('status', ClientStatus::DISCONNECTED) + ->setParameter('threshold', $threshold) + ->getQuery() + ->getResult(); + + $this->dispatchMercureEvent($clients); + + $mercureTime = microtime(true) - $startMercureTime; + + $io->success("Updated $updatedCount clients to DISCONNECTED status."); + $io->note("Query time: " . round($queryTime, 3) . "s"); + $io->note("Mercure dispatch time: " . round($mercureTime, 3) . "s"); + + return Command::SUCCESS; + } + + private function dispatchMercureEvent(array $clients, int $chunkSize = 10000): void + { + $chunks = array_chunk($clients, $chunkSize); + + foreach ($chunks as $chunk) { + $data = []; + + foreach ($chunk as $client) { + $data[] = [ + '@id' => '/clients/' . $client->getUuid(), + 'status' => $client->getStatus(), + ]; + } + + $update = new Update( + 'clients', + json_encode($data) + ); + + $this->hub->publish($update); + } + } + +} diff --git a/src/Command/TestCommand.php b/src/Command/TestCommand.php deleted file mode 100644 index fa8306b..0000000 --- a/src/Command/TestCommand.php +++ /dev/null @@ -1,44 +0,0 @@ -entityManager->getRepository(Trace::class)->find(7236); - - $trace->setStatus(TraceStatus::SUCCESS); - $trace->setProgress(1000); - - $this->entityManager->persist($trace); - $this->entityManager->flush(); - - return Command::SUCCESS; - } -} diff --git a/src/Controller/OgAgent/Webhook/StatusController.php b/src/Controller/OgAgent/Webhook/StatusController.php index 7973748..6871f1a 100644 --- a/src/Controller/OgAgent/Webhook/StatusController.php +++ b/src/Controller/OgAgent/Webhook/StatusController.php @@ -3,6 +3,7 @@ namespace App\Controller\OgAgent\Webhook; use App\Controller\OgRepository\Image\CreateAuxFilesAction; +use App\Entity\Client; use App\Entity\Image; use App\Entity\ImageImageRepository; use App\Entity\OperativeSystem; @@ -59,16 +60,24 @@ class StatusController extends AbstractController public function index(Request $request): JsonResponse { $data = $request->toArray(); - $requiredFields = ['job_id']; - - foreach ($requiredFields as $field) { - if (!isset($data[$field])) { - return new JsonResponse(['message' => "Missing parameter: $field"], Response::HTTP_BAD_REQUEST); - } - } $this->logger->info('Webhook data received', $data); + // Esta parte del codigo nos indica si el cliente se encuentra activo + if (isset($data['iph']) && isset($data['timestamp'])) { + $client = $this->entityManager->getRepository(Client::class)->findOneBy(['ip' => $data['iph']]); + if (!$client) { + $this->logger->error('Client not found', $data); + return new JsonResponse(['message' => 'Client not found'], Response::HTTP_NOT_FOUND); + } + + $updateAt = (new \DateTime())->setTimestamp((int)$data['timestamp']); + + $client->setUpdatedAt($updateAt); + $this->entityManager->persist($client); + $this->entityManager->flush(); + } + if (isset($data['progress'])){ $trace = $this->entityManager->getRepository(Trace::class)->findOneBy(['jobId' => $data['job_id']]); if ($trace){ diff --git a/src/Entity/Client.php b/src/Entity/Client.php index d550907..a3930ca 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -14,6 +14,9 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; #[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_MAC', fields: ['mac'])] #[UniqueEntity(fields: ['ip'], message: 'This IP address is already in use.')] #[UniqueEntity(fields: ['mac'], message: 'This MAC address is already in use.')] +#[ORM\Index(fields: ['status'], name: 'IDX_STATUS')] +#[ORM\Index(fields: ['updatedAt'], name: 'IDX_UPDATED_AT')] +#[ORM\Index(fields: ['status', 'updatedAt'], name: 'IDX_STATUS_UPDATED_AT')] class Client extends AbstractEntity { use NameableTrait; diff --git a/src/EventSubscriber/MercureSubscriber.php b/src/EventSubscriber/MercureSubscriber.php index 4f225ef..aaf1b3d 100644 --- a/src/EventSubscriber/MercureSubscriber.php +++ b/src/EventSubscriber/MercureSubscriber.php @@ -58,9 +58,14 @@ class MercureSubscriber implements EventSubscriberInterface /** @var Client $client */ $client = $clientOutput->getEntity(); + $data[] = [ + '@id' => '/clients/' . $client->getUuid(), + 'status' => $client->getStatus(), + ]; + $update = new Update( 'clients', - json_encode(['@id' => '/clients/'.$client->getUuid(), 'status' => $client->getStatus()]) + json_encode($data) ); $this->hub->publish($update); diff --git a/src/Model/ClientStatus.php b/src/Model/ClientStatus.php index c9882bb..01d88fb 100644 --- a/src/Model/ClientStatus.php +++ b/src/Model/ClientStatus.php @@ -6,6 +6,7 @@ final class ClientStatus { public const string OFF = 'off'; public const string INITIALIZING = 'initializing'; + public const string DISCONNECTED = 'disconnected'; public const string TURNING_OFF = 'turning-off'; public const string OG_LIVE = 'og-live'; public const string BUSY = 'busy'; @@ -20,6 +21,7 @@ final class ClientStatus self::OFF => 'Apagado', self::TURNING_OFF => 'Apagando', self::INITIALIZING => 'Inicializando', + self::DISCONNECTED => 'Conexión perdida', self::OG_LIVE => 'OG Live', self::BUSY => 'Ocupado', self::LINUX => 'Linux', From 927677ddc0c3d086b0c278fe98bcb348800fb3ad Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Apr 2025 15:42:53 +0200 Subject: [PATCH 09/16] refs #1858. Added description into imageImageRepositort --- migrations/Version20250407063100.php | 31 +++++++++++++++++++ .../Webhook/ResponseController.php | 9 +++++- src/Dto/Input/ImageImageRepositoryInput.php | 13 ++------ src/Dto/Output/ImageImageRepositoryOutput.php | 4 +++ src/Entity/ImageImageRepository.php | 15 +++++++++ src/State/Processor/ImageProcessor.php | 10 ++++-- .../Provider/ImageImageRepositoryProvider.php | 15 +++++++++ 7 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 migrations/Version20250407063100.php diff --git a/migrations/Version20250407063100.php b/migrations/Version20250407063100.php new file mode 100644 index 0000000..2181e1c --- /dev/null +++ b/migrations/Version20250407063100.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image_image_repository ADD description 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 image_image_repository DROP description'); + } +} diff --git a/src/Controller/OgRepository/Webhook/ResponseController.php b/src/Controller/OgRepository/Webhook/ResponseController.php index b4900f7..13541a0 100644 --- a/src/Controller/OgRepository/Webhook/ResponseController.php +++ b/src/Controller/OgRepository/Webhook/ResponseController.php @@ -51,10 +51,17 @@ class ResponseController extends AbstractOgRepositoryController $imageImageRepository = $this->getImageImageRepository($trace); if (!$imageImageRepository) return $this->jsonResponseError('Image not found', Response::HTTP_NOT_FOUND, $trace); + $imageImageRepository->setStatus(ImageStatus::SUCCESS); + $this->entityManager->persist($imageImageRepository); + + if ($data['success'] !== true) { + $this->updateTraceStatus($trace, TraceStatus::FAILED, $data['output'] ?? 'Action failed'); + return new JsonResponse(['message' => 'Success'], Response::HTTP_OK); + } + if ($setFullsum) { $imageImageRepository->setImageFullsum($data['image_id']); } - $imageImageRepository->setStatus(ImageStatus::SUCCESS); $this->entityManager->persist($imageImageRepository); $this->updateTraceStatus($trace, TraceStatus::SUCCESS); diff --git a/src/Dto/Input/ImageImageRepositoryInput.php b/src/Dto/Input/ImageImageRepositoryInput.php index 9cd95ca..b595bb2 100644 --- a/src/Dto/Input/ImageImageRepositoryInput.php +++ b/src/Dto/Input/ImageImageRepositoryInput.php @@ -12,13 +12,8 @@ use Symfony\Component\Validator\Constraints as Assert; final class ImageImageRepositoryInput { - #[Assert\NotNull] #[Groups(['image-image-repository:write'])] - public ?ImageRepositoryOutput $imageRepository = null; - - #[Assert\NotNull] - #[Groups(['image-image-repository:write'])] - public ?string $status = ''; + public ?string $description = ''; public function __construct(?ImageImageRepository $imageImageRepository = null) { @@ -26,8 +21,7 @@ final class ImageImageRepositoryInput return; } - $this->imageRepository = new ImageRepositoryOutput($imageImageRepository->getRepository()); - $this->status = $imageImageRepository->getStatus(); + $this->description = $imageImageRepository->getDescription(); } public function createOrUpdateEntity(?ImageImageRepository $imageImageRepository = null): ImageImageRepository @@ -36,8 +30,7 @@ final class ImageImageRepositoryInput $imageImageRepository = new ImageImageRepository(); } - $imageImageRepository->setRepository($this->imageRepository); - $imageImageRepository->setStatus($this->status); + $imageImageRepository->setDescription($this->description); return $imageImageRepository; } diff --git a/src/Dto/Output/ImageImageRepositoryOutput.php b/src/Dto/Output/ImageImageRepositoryOutput.php index 2db73bc..013b4f3 100644 --- a/src/Dto/Output/ImageImageRepositoryOutput.php +++ b/src/Dto/Output/ImageImageRepositoryOutput.php @@ -24,6 +24,9 @@ class ImageImageRepositoryOutput extends AbstractOutput #[Groups(['image-image-repository:read', 'image:read'])] public ?string $datasize = null; + #[Groups(['image-image-repository:read', 'image:read'])] + public ?string $description = null; + #[Groups(['image:read', 'image-image-repository:read'])] public ?int $version = null; @@ -51,6 +54,7 @@ class ImageImageRepositoryOutput extends AbstractOutput $this->status = $imageImageRepository->getStatus(); $this->imageFullsum = $imageImageRepository->getImageFullsum(); $this->datasize = $imageImageRepository->getDatasize(); + $this->description = $imageImageRepository->getDescription(); $this->createdAt = $imageImageRepository->getCreatedAt(); $this->createdBy = $imageImageRepository->getCreatedBy(); } diff --git a/src/Entity/ImageImageRepository.php b/src/Entity/ImageImageRepository.php index d80012c..fa95665 100644 --- a/src/Entity/ImageImageRepository.php +++ b/src/Entity/ImageImageRepository.php @@ -32,6 +32,9 @@ class ImageImageRepository extends AbstractEntity #[ORM\Column(nullable: true)] private ?int $version = null; + #[ORM\Column(length: 255, nullable: true)] + private ?string $description = null; + public function getImage(): ?Image { return $this->image; @@ -115,4 +118,16 @@ class ImageImageRepository extends AbstractEntity return $this; } + + public function getDescription(): ?string + { + return $this->description; + } + + public function setDescription(?string $description): static + { + $this->description = $description; + + return $this; + } } diff --git a/src/State/Processor/ImageProcessor.php b/src/State/Processor/ImageProcessor.php index 12bcf5d..efea176 100644 --- a/src/State/Processor/ImageProcessor.php +++ b/src/State/Processor/ImageProcessor.php @@ -18,16 +18,16 @@ use App\Dto\Output\ImageOutput; use App\Entity\ImageImageRepository; use App\Repository\ImageRepository; use App\Repository\ImageRepositoryRepository as ImageRepositoryRepository; +use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; readonly class ImageProcessor implements ProcessorInterface { public function __construct( - private ImageRepositoryRepository $imageRepositoryRepository, private ImageRepository $imageRepository, private ValidatorInterface $validator, private CreateImageAction $createImageActionController, - private RenameAction $renameActionController, + private KernelInterface $kernel, ) { } @@ -72,7 +72,11 @@ readonly class ImageProcessor implements ProcessorInterface } else { $image = $data->createOrUpdateEntity($entity); - $response = $this->createImageActionController->__invoke($image); + if ($this->kernel->getEnvironment() !== 'test') { + $response = $this->createImageActionController->__invoke($image); + } + + $this->validator->validate($image); $this->imageRepository->save($image); } diff --git a/src/State/Provider/ImageImageRepositoryProvider.php b/src/State/Provider/ImageImageRepositoryProvider.php index 9c47ca3..ba5a1aa 100644 --- a/src/State/Provider/ImageImageRepositoryProvider.php +++ b/src/State/Provider/ImageImageRepositoryProvider.php @@ -9,6 +9,7 @@ use ApiPlatform\Metadata\Patch; use ApiPlatform\Metadata\Put; use ApiPlatform\State\Pagination\TraversablePaginator; use ApiPlatform\State\ProviderInterface; +use App\Dto\Input\ImageImageRepositoryInput; use App\Dto\Output\ImageImageRepositoryOutput; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -26,6 +27,9 @@ readonly class ImageImageRepositoryProvider implements ProviderInterface switch ($operation){ case $operation instanceof GetCollection: return $this->provideCollection($operation, $uriVariables, $context); + case $operation instanceof Patch: + case $operation instanceof Put: + return $this->provideInput($operation, $uriVariables, $context); case $operation instanceof Get: return $this->provideItem($operation, $uriVariables, $context); } @@ -53,4 +57,15 @@ readonly class ImageImageRepositoryProvider implements ProviderInterface return new ImageImageRepositoryOutput($item); } + + public function provideInput(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + if (isset($uriVariables['uuid'])) { + $item = $this->itemProvider->provide($operation, $uriVariables, $context); + + return $item !== null ? new ImageImageRepositoryInput($item) : null; + } + + return new ImageImageRepositoryInput(); + } } From 1bca31ec5f4f6bc9bf6a3761a9a44070944f3406 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 8 Apr 2025 15:43:14 +0200 Subject: [PATCH 10/16] refs #1855. Fixed subnet DTOs clients property --- src/Dto/Output/OrganizationalUnitOutput.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Dto/Output/OrganizationalUnitOutput.php b/src/Dto/Output/OrganizationalUnitOutput.php index 7adb715..d5e0b69 100644 --- a/src/Dto/Output/OrganizationalUnitOutput.php +++ b/src/Dto/Output/OrganizationalUnitOutput.php @@ -48,9 +48,6 @@ final class OrganizationalUnitOutput extends AbstractOutput #[Groups(['organizational-unit:read'])] public array $children = []; - #[Groups(['organizational-unit:read'])] - public array $clients = []; - #[Groups(['organizational-unit:read', "client:read"])] #[ApiProperty(readableLink: true)] public ?RemoteCalendarOutput $remoteCalendar = null; @@ -96,12 +93,6 @@ final class OrganizationalUnitOutput extends AbstractOutput )->toArray(); } - if (isset($context['groups']) && in_array('organizational-unit:read', $context['groups'])) { - $this->clients = $organizationalUnit->getClients()->map( - fn(Client $client) => new ClientOutput($client) - )->toArray(); - } - $this->excludeParentChanges = $organizationalUnit->isExcludeParentChanges(); $this->path = $organizationalUnit->getPath(); $this->createdAt = $organizationalUnit->getCreatedAt(); From 6867f7409883d422dd80147aced11ed14def04bf Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Wed, 9 Apr 2025 17:58:29 +0200 Subject: [PATCH 11/16] refs #1858. Added description into deployImage selector --- src/Controller/OgRepository/Webhook/ResponseController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Controller/OgRepository/Webhook/ResponseController.php b/src/Controller/OgRepository/Webhook/ResponseController.php index 13541a0..3dee082 100644 --- a/src/Controller/OgRepository/Webhook/ResponseController.php +++ b/src/Controller/OgRepository/Webhook/ResponseController.php @@ -54,7 +54,7 @@ class ResponseController extends AbstractOgRepositoryController $imageImageRepository->setStatus(ImageStatus::SUCCESS); $this->entityManager->persist($imageImageRepository); - if ($data['success'] !== true) { + if (isset($data['success']) && $data['success'] !== true) { $this->updateTraceStatus($trace, TraceStatus::FAILED, $data['output'] ?? 'Action failed'); return new JsonResponse(['message' => 'Success'], Response::HTTP_OK); } @@ -90,6 +90,7 @@ class ResponseController extends AbstractOgRepositoryController } $newImageRepo = new ImageImageRepository(); + $newImageRepo->setName($image->getName().'_v'.($originImageImageRepository->getVersion() + 1)); $newImageRepo->setImage($image); $newImageRepo->setRepository($repository); $newImageRepo->setStatus(ImageStatus::SUCCESS); From 4578f29349a56c13d1099221298bdbd9bbce0ae7 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 10 Apr 2025 09:50:59 +0200 Subject: [PATCH 12/16] refs #1857. Rename image API integration --- config/api_platform/ImageImageRepository.yaml | 8 ++ migrations/Version20250409093554.php | 31 +++++ .../OgRepository/Image/RenameAction.php | 117 ++++++++++++------ src/Dto/Input/RenameImageInput.php | 13 ++ src/Factory/ImageImageRepositoryFactory.php | 58 +++++++++ tests/Functional/ImageTest.php | 9 +- 6 files changed, 193 insertions(+), 43 deletions(-) create mode 100644 migrations/Version20250409093554.php create mode 100644 src/Dto/Input/RenameImageInput.php create mode 100644 src/Factory/ImageImageRepositoryFactory.php diff --git a/config/api_platform/ImageImageRepository.yaml b/config/api_platform/ImageImageRepository.yaml index 279d72a..b2d3d73 100644 --- a/config/api_platform/ImageImageRepository.yaml +++ b/config/api_platform/ImageImageRepository.yaml @@ -64,6 +64,14 @@ resources: uriTemplate: /image-image-repositories/{uuid}/convert-image-to-virtual controller: App\Controller\OgRepository\Image\ConvertImageToVirtualAction + rename_image_ogrepository: + shortName: OgRepository Server + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\RenameImageInput + uriTemplate: /image-image-repositories/{uuid}/rename-image + controller: App\Controller\OgRepository\Image\RenameAction + trash_delete_image_ogrepository: shortName: OgRepository Server description: Delete Image in OgRepository diff --git a/migrations/Version20250409093554.php b/migrations/Version20250409093554.php new file mode 100644 index 0000000..4993a74 --- /dev/null +++ b/migrations/Version20250409093554.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image_image_repository ADD name VARCHAR(255) NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE image_image_repository DROP name'); + } +} diff --git a/src/Controller/OgRepository/Image/RenameAction.php b/src/Controller/OgRepository/Image/RenameAction.php index d66ac9c..6e41e16 100644 --- a/src/Controller/OgRepository/Image/RenameAction.php +++ b/src/Controller/OgRepository/Image/RenameAction.php @@ -4,8 +4,10 @@ namespace App\Controller\OgRepository\Image; use App\Controller\OgRepository\AbstractOgRepositoryController; use App\Dto\Input\DeployImageInput; +use App\Dto\Input\RenameImageInput; use App\Entity\Image; use App\Entity\ImageImageRepository; +use App\Entity\ImageRepository; use App\Model\CommandTypes; use App\Model\ImageStatus; use App\Model\TraceStatus; @@ -28,59 +30,96 @@ class RenameAction extends AbstractOgRepositoryController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(Image $image): JsonResponse + public function __invoke(RenameImageInput $input, ImageImageRepository $imageImageRepository): JsonResponse { - $repositories = $image->getImageImageRepositories(); + $image = $imageImageRepository->getImage(); - if ($repositories->count() === 0) { - return new JsonResponse(data: ['error' => 'Image is not in any repository', 'code' => Response::HTTP_INTERNAL_SERVER_ERROR], status: Response::HTTP_BAD_REQUEST); - } + if ($image->isGlobal()) { + $repositories = $image->getImageImageRepositories(); - $allGood = true; - foreach ($repositories as $repository) { - try { - $content = $this->createRequest('GET', 'http://'.$repository->getRepository()->getIp(). ':8006/ogrepository/v1/status'); - } catch (TransportExceptionInterface $e) { - $allGood = false; - break; + if ($repositories->count() === 0) { + return $this->jsonError('Image is not in any repository'); } + + if (!$this->isAvailableInAllRepositories($repositories)) { + $this->logger->info('Image is not available in all repositories', ['image' => $image->getName()]); + return $this->jsonError('Image is not available in all repositories'); + } + + $repoWithImage = $this->entityManager + ->getRepository(ImageImageRepository::class) + ->findBy(['image' => $image, 'repository' => $imageImageRepository->getRepository()]); + } else { + $repoWithImage = [$imageImageRepository]; } - if (!$allGood) { - $this->logger->info('Image is not available in all repositories', ['image' => $image->getName()]); - return new JsonResponse(data: ['error' => 'Image is not available in all repositories', 'code' => Response::HTTP_INTERNAL_SERVER_ERROR], status: Response::HTTP_BAD_REQUEST); - } - - $image->setVersion($image->getVersion() + 1); - $this->entityManager->persist($image); - - $conditional = false; - - $latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersion(); - $repoWithImage = $this->entityManager->getRepository(ImageImageRepository::class)->findBy(['image' => $image, 'version' => $latestImageRepo->getVersion()]); - + $hasError = false; foreach ($repoWithImage as $repository) { - $params = [ - 'json' => [ - 'ID_img' => $repository->getImageFullsum(), - 'image_new_name' => $image->getName().'_v'.$image->getVersion(), - ] - ]; + $content = $this->renameImageInRepository($repository, $input->newName); - $content = $this->createRequest('PUT', 'http://'.$repository->getRepository()->getIp().':8006/ogrepository/v1/images/rename', $params); - - if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { - $conditional = true; + if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) { + $hasError = true; } - } - if ($conditional) { - return new JsonResponse(data: ['error' => 'Error renaming image'], status: Response::HTTP_INTERNAL_SERVER_ERROR); + $repository->setName($input->newName); + $this->entityManager->persist($repository); } $this->entityManager->flush(); - return new JsonResponse(data: [], status: Response::HTTP_OK); + if ($hasError) { + return new JsonResponse(['error' => 'Error renaming image'], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return new JsonResponse([], Response::HTTP_OK); } + + /** + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + * @throws ServerExceptionInterface + */ + private function isAvailableInAllRepositories($repositories): bool + { + foreach ($repositories as $repository) { + try { + $this->createRequest('GET', 'http://' . $repository->getRepository()->getIp() . ':8006/ogrepository/v1/status'); + } catch (TransportExceptionInterface $e) { + return false; + } + } + return true; + } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + private function renameImageInRepository(ImageImageRepository $repository, string $newName): array + { + $params = [ + 'json' => [ + 'ID_img' => $repository->getImageFullsum(), + 'image_new_name' => $newName, + ] + ]; + + return $this->createRequest( + 'PUT', + 'http://' . $repository->getRepository()->getIp() . ':8006/ogrepository/v1/images/rename', + $params + ); + } + + private function jsonError(string $message): JsonResponse + { + return new JsonResponse( + ['error' => $message, 'code' => Response::HTTP_INTERNAL_SERVER_ERROR], + Response::HTTP_BAD_REQUEST + ); + } + } \ No newline at end of file diff --git a/src/Dto/Input/RenameImageInput.php b/src/Dto/Input/RenameImageInput.php new file mode 100644 index 0000000..7fa128d --- /dev/null +++ b/src/Dto/Input/RenameImageInput.php @@ -0,0 +1,13 @@ + + */ +final class ImageImageRepositoryFactory extends ModelFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() + { + parent::__construct(); + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + protected function getDefaults(): array + { + return [ + 'createdAt' => self::faker()->dateTime(), + 'image' => ImageFactory::new(), + 'name' => self::faker()->text(255), + 'repository' => ImageRepositoryFactory::new(), + 'status' => self::faker()->text(255), + 'updatedAt' => self::faker()->dateTime(), + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + protected function initialize(): self + { + return $this + // ->afterInstantiate(function(ImageImageRepository $imageImageRepository): void {}) + ; + } + + protected static function getClass(): string + { + return ImageImageRepository::class; + } +} diff --git a/tests/Functional/ImageTest.php b/tests/Functional/ImageTest.php index 9b323c7..419d86d 100644 --- a/tests/Functional/ImageTest.php +++ b/tests/Functional/ImageTest.php @@ -9,6 +9,7 @@ use App\Entity\OrganizationalUnit; use App\Entity\SoftwareProfile; use App\Factory\ClientFactory; use App\Factory\ImageFactory; +use App\Factory\ImageImageRepositoryFactory; use App\Factory\ImageRepositoryFactory; use App\Factory\OrganizationalUnitFactory; use App\Factory\SoftwareProfileFactory; @@ -70,12 +71,12 @@ class ImageTest extends AbstractTest SoftwareProfileFactory::createOne(['description' => self::SOFTWARE_PROFILE]); $swPIri = $this->findIriBy(SoftwareProfile::class, ['description' => self::SOFTWARE_PROFILE]); - $imageRepositories = ImageRepositoryFactory::createMany(5); + $imageRepositories = ImageImageRepositoryFactory::createMany(5); $this->createClientWithCredentials()->request('POST', '/images',['json' => [ 'name' => self::IMAGE_CREATE, 'softwareProfile' => $swPIri, - 'imageRepositories' => array_map(fn($repo) => '/image-repositories/'. $repo->getUuid(), $imageRepositories) + 'imageImageRepositories' => array_map(fn($repo) => '/image-repositories/'. $repo->getUuid(), $imageRepositories) ]]); $this->assertResponseStatusCodeSame(201); @@ -102,11 +103,11 @@ class ImageTest extends AbstractTest ImageFactory::createOne(['name' => self::IMAGE_CREATE]); $iri = $this->findIriBy(Image::class, ['name' => self::IMAGE_CREATE]); - $imageRepositories = ImageRepositoryFactory::createMany(5); + $imageRepositories = ImageImageRepositoryFactory::createMany(5); $this->createClientWithCredentials()->request('PUT', $iri, ['json' => [ 'name' => self::IMAGE_UPDATE, - 'imageRepositories' => array_map(fn($repo) => '/image-repositories/'. $repo->getUuid(), $imageRepositories) + 'imageImageRepositories' => array_map(fn($repo) => '/image-repositories/'. $repo->getUuid(), $imageRepositories) ]]); $this->assertResponseIsSuccessful(); From 9a96b04e7fe33c4b9e88a2a06a419c528fc6353e Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 10 Apr 2025 09:53:28 +0200 Subject: [PATCH 13/16] refs #1864. Added ssh_port and user in imageRepository --- migrations/Version20250408140101.php | 31 ++++++++++++++++++ .../OgRepository/Image/BackupImageAction.php | 4 ++- .../OgRepository/Image/TransferAction.php | 3 +- .../Image/TransferGlobalAction.php | 3 +- src/Dto/Input/ImageRepositoryInput.php | 14 +++++++- src/Dto/Output/ImageImageRepositoryOutput.php | 4 +++ src/Dto/Output/ImageRepositoryOutput.php | 8 +++++ src/Entity/ImageImageRepository.php | 15 +++++++++ src/Entity/ImageRepository.php | 32 +++++++++++++++++++ 9 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 migrations/Version20250408140101.php diff --git a/migrations/Version20250408140101.php b/migrations/Version20250408140101.php new file mode 100644 index 0000000..f84d86c --- /dev/null +++ b/migrations/Version20250408140101.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image_repository ADD user VARCHAR(255) DEFAULT NULL, ADD ssh_port 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 image_repository DROP user, DROP ssh_port'); + } +} diff --git a/src/Controller/OgRepository/Image/BackupImageAction.php b/src/Controller/OgRepository/Image/BackupImageAction.php index 8f8662d..51e76b4 100644 --- a/src/Controller/OgRepository/Image/BackupImageAction.php +++ b/src/Controller/OgRepository/Image/BackupImageAction.php @@ -32,6 +32,7 @@ class BackupImageAction extends AbstractOgRepositoryController public function __invoke(BackupImageInput $input, ImageImageRepository $imageImageRepository): JsonResponse { $image = $imageImageRepository->getImage(); + $repository = $imageImageRepository->getRepository(); if (!$image->getName()) { throw new ValidatorException('Name is required'); @@ -41,8 +42,9 @@ class BackupImageAction extends AbstractOgRepositoryController 'json' => [ 'ID_img' => $imageImageRepository->getImageFullsum(), 'repo_ip' => $input->repoIp, + 'ssh_port' => $repository->getSshPort() ?? '22', 'remote_path' => $input->remotePath, - 'user' => 'opengnsys' + 'user' => $repository->getUser() ?? 'opengnsys', ] ]; diff --git a/src/Controller/OgRepository/Image/TransferAction.php b/src/Controller/OgRepository/Image/TransferAction.php index c0513bb..b0707d4 100644 --- a/src/Controller/OgRepository/Image/TransferAction.php +++ b/src/Controller/OgRepository/Image/TransferAction.php @@ -47,7 +47,8 @@ class TransferAction extends AbstractOgRepositoryController 'json' => [ 'image' => $image->getName().'.img', 'repo_ip' => $imageImageRepository->getRepository()->getIp(), - 'user' => 'opengnsys', + 'user' => $repository->getUser(), + 'ssh_port' => $repository->getSshPort() ] ]; diff --git a/src/Controller/OgRepository/Image/TransferGlobalAction.php b/src/Controller/OgRepository/Image/TransferGlobalAction.php index c69ff48..0e4b31f 100644 --- a/src/Controller/OgRepository/Image/TransferGlobalAction.php +++ b/src/Controller/OgRepository/Image/TransferGlobalAction.php @@ -48,7 +48,8 @@ class TransferGlobalAction extends AbstractOgRepositoryController 'json' => [ 'image' => $image->getName().'.img', 'repo_ip' => $imageImageRepository->getRepository()->getIp(), - 'user' => 'opengnsys', + 'user' => $repository->getUser(), + 'ssh_port' => $repository->getSshPort() ] ]; diff --git a/src/Dto/Input/ImageRepositoryInput.php b/src/Dto/Input/ImageRepositoryInput.php index 96e20b7..56623e1 100644 --- a/src/Dto/Input/ImageRepositoryInput.php +++ b/src/Dto/Input/ImageRepositoryInput.php @@ -27,11 +27,19 @@ final class ImageRepositoryInput #[ApiProperty(description: 'The IP of the repository', example: "")] public ?string $ip = null; - #[Groups(['repository:write'])] #[ApiProperty(description: 'The comments of the repository', example: "Repository 1 comments")] public ?string $comments = null; + #[Assert\NotBlank] + #[Groups(['repository:write'])] + #[ApiProperty(description: 'The user of the repository', example: "Repository user")] + public ?string $user = 'opengnsys'; + + #[Groups(['repository:write'])] + #[ApiProperty(description: 'The sshPort of the repository', example: "Repository ssh port")] + public ?string $sshPort = '22'; + public function __construct(?ImageRepository $repository = null) { @@ -42,6 +50,8 @@ final class ImageRepositoryInput $this->name = $repository->getName(); $this->ip = $repository->getIp(); $this->comments = $repository->getComments(); + $this->user = $repository->getUser(); + $this->sshPort = $repository->getSshPort(); } public function createOrUpdateEntity(?ImageRepository $repository = null): ImageRepository @@ -53,6 +63,8 @@ final class ImageRepositoryInput $repository->setName($this->name); $repository->setIp($this->ip); $repository->setComments($this->comments); + $repository->setUser($this->user); + $repository->setSshPort($this->sshPort); return $repository; } diff --git a/src/Dto/Output/ImageImageRepositoryOutput.php b/src/Dto/Output/ImageImageRepositoryOutput.php index 013b4f3..9d4c4a0 100644 --- a/src/Dto/Output/ImageImageRepositoryOutput.php +++ b/src/Dto/Output/ImageImageRepositoryOutput.php @@ -18,6 +18,9 @@ class ImageImageRepositoryOutput extends AbstractOutput #[Groups(['image-image-repository:read', 'image:read'])] public string $status; + #[Groups(['image-image-repository:read', 'image:read'])] + public string $name; + #[Groups(['image-image-repository:read', 'image:read'])] public ?string $imageFullsum = null; @@ -50,6 +53,7 @@ class ImageImageRepositoryOutput extends AbstractOutput $this->imageRepository = new ImageRepositoryOutput($imageImageRepository->getRepository()); } + $this->name = $imageImageRepository->getName(); $this->version = $imageImageRepository->getVersion(); $this->status = $imageImageRepository->getStatus(); $this->imageFullsum = $imageImageRepository->getImageFullsum(); diff --git a/src/Dto/Output/ImageRepositoryOutput.php b/src/Dto/Output/ImageRepositoryOutput.php index daf72e3..44445b6 100644 --- a/src/Dto/Output/ImageRepositoryOutput.php +++ b/src/Dto/Output/ImageRepositoryOutput.php @@ -18,6 +18,12 @@ class ImageRepositoryOutput extends AbstractOutput #[Groups(['repository:read'])] public ?string $comments = ''; + #[Groups(['repository:read'])] + public ?string $sshPort = ''; + + #[Groups(['repository:read'])] + public ?string $user = ''; + #[Groups(['repository:read'])] public \DateTime $createdAt; @@ -31,6 +37,8 @@ class ImageRepositoryOutput extends AbstractOutput $this->name = $imageRepository->getName(); $this->ip = $imageRepository->getIp(); $this->comments = $imageRepository->getComments(); + $this->sshPort = $imageRepository->getSshPort(); + $this->user = $imageRepository->getUser(); $this->createdAt = $imageRepository->getCreatedAt(); $this->createdBy = $imageRepository->getCreatedBy(); } diff --git a/src/Entity/ImageImageRepository.php b/src/Entity/ImageImageRepository.php index fa95665..6de9f49 100644 --- a/src/Entity/ImageImageRepository.php +++ b/src/Entity/ImageImageRepository.php @@ -35,6 +35,9 @@ class ImageImageRepository extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?string $description = null; + #[ORM\Column(length: 255)] + private ?string $name = null; + public function getImage(): ?Image { return $this->image; @@ -130,4 +133,16 @@ class ImageImageRepository extends AbstractEntity return $this; } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(string $name): static + { + $this->name = $name; + + return $this; + } } diff --git a/src/Entity/ImageRepository.php b/src/Entity/ImageRepository.php index 1dc49fe..7588190 100644 --- a/src/Entity/ImageRepository.php +++ b/src/Entity/ImageRepository.php @@ -10,6 +10,8 @@ use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: ImageRepositoryRepository::class)] class ImageRepository extends AbstractEntity { + const string DEFAULT_USER = 'opengnsys'; + use NameableTrait; #[ORM\Column(length: 255)] @@ -24,6 +26,12 @@ class ImageRepository extends AbstractEntity #[ORM\OneToMany(mappedBy: 'repository', targetEntity: ImageImageRepository::class)] private Collection $imageImageRepositories; + #[ORM\Column(length: 255, nullable: true)] + private ?string $user = self::DEFAULT_USER; + + #[ORM\Column(length: 255, nullable: true)] + private ?string $sshPort = null; + public function __construct() { parent::__construct(); @@ -83,4 +91,28 @@ class ImageRepository extends AbstractEntity return $this; } + + public function getUser(): ?string + { + return $this->user; + } + + public function setUser(?string $user): static + { + $this->user = $user; + + return $this; + } + + public function getSshPort(): ?string + { + return $this->sshPort; + } + + public function setSshPort(?string $sshPort): static + { + $this->sshPort = $sshPort; + + return $this; + } } From 290cbb0ae0fed7d74359099579d092edc9cae28c Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 10 Apr 2025 09:54:50 +0200 Subject: [PATCH 14/16] Image improvements. Added name --- src/Controller/OgAgent/CreateImageAction.php | 1 + src/Controller/OgRepository/Image/ConvertAction.php | 4 +++- src/Controller/OgRepository/Image/CreateAuxFilesAction.php | 6 +++--- src/Controller/OgRepository/Image/DeleteTrashAction.php | 7 +++++++ src/Controller/OgRepository/Image/ImportAction.php | 4 +++- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Controller/OgAgent/CreateImageAction.php b/src/Controller/OgAgent/CreateImageAction.php index 2ef1cb0..eb89c5c 100644 --- a/src/Controller/OgAgent/CreateImageAction.php +++ b/src/Controller/OgAgent/CreateImageAction.php @@ -60,6 +60,7 @@ class CreateImageAction extends AbstractController $latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersionByImageAndRepository($image, $repository); $imageImageRepository = new ImageImageRepository(); + $imageImageRepository->setName($image->getName().'_v'.($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1)); $imageImageRepository->setImage($image); $imageImageRepository->setRepository($repository); $imageImageRepository->setStatus(ImageStatus::IN_PROGRESS); diff --git a/src/Controller/OgRepository/Image/ConvertAction.php b/src/Controller/OgRepository/Image/ConvertAction.php index a41589a..ff3a4bd 100644 --- a/src/Controller/OgRepository/Image/ConvertAction.php +++ b/src/Controller/OgRepository/Image/ConvertAction.php @@ -53,12 +53,13 @@ class ConvertAction extends AbstractOgRepositoryController } $imageImageRepositoryEntity = new ImageImageRepository(); + $imageImageRepositoryEntity->setName($imageEntity->getName().'_v'.$imageImageRepositoryEntity->getVersion() + 1); $imageImageRepositoryEntity->setStatus(ImageStatus::PENDING); $imageImageRepositoryEntity->setImage($imageEntity); $imageImageRepositoryEntity->setRepository($repository); + $imageImageRepositoryEntity->setVersion(1); $this->entityManager->persist($imageImageRepositoryEntity); - $this->entityManager->flush(); $this->logger->info('Converting image', ['image' => $image]); @@ -74,6 +75,7 @@ class ConvertAction extends AbstractOgRepositoryController if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { throw new ValidatorException('Error converting image'); } + $this->entityManager->flush(); $inputData = [ 'imageName' => $image, diff --git a/src/Controller/OgRepository/Image/CreateAuxFilesAction.php b/src/Controller/OgRepository/Image/CreateAuxFilesAction.php index 9da0161..aaf0bdb 100644 --- a/src/Controller/OgRepository/Image/CreateAuxFilesAction.php +++ b/src/Controller/OgRepository/Image/CreateAuxFilesAction.php @@ -39,18 +39,18 @@ class CreateAuxFilesAction extends AbstractOgRepositoryController $params = [ 'json' => [ - 'image' => $image->getName().'_v'.$data->getVersion().'.img' + 'image' => $data->getName().'.img' ] ]; - $this->logger->info('Creating aux files', ['image' => $image->getName()]); + $this->logger->info('Creating aux files', ['image' => $data->getName()]); $repository = $data->getRepository(); $content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/images/torrentsum', $params); $inputData = [ - 'imageName' => $image->getName(), + 'imageName' => $data->getName(), 'imageImageRepositoryUuid' => $data->getUuid(), ]; diff --git a/src/Controller/OgRepository/Image/DeleteTrashAction.php b/src/Controller/OgRepository/Image/DeleteTrashAction.php index 8ff17d7..31c245e 100644 --- a/src/Controller/OgRepository/Image/DeleteTrashAction.php +++ b/src/Controller/OgRepository/Image/DeleteTrashAction.php @@ -45,6 +45,13 @@ class DeleteTrashAction extends AbstractOgRepositoryController $this->entityManager->persist($image); $this->entityManager->flush(); + $imageImageCollection = $image->getImageImageRepositories(); + + if ($imageImageCollection->isEmpty()) { + $this->entityManager->remove($image); + $this->entityManager->flush(); + } + return new JsonResponse(data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgRepository/Image/ImportAction.php b/src/Controller/OgRepository/Image/ImportAction.php index cb28a67..4e1af2f 100644 --- a/src/Controller/OgRepository/Image/ImportAction.php +++ b/src/Controller/OgRepository/Image/ImportAction.php @@ -51,9 +51,11 @@ class ImportAction extends AbstractOgRepositoryController } $imageImageRepositoryEntity = new ImageImageRepository(); + $imageImageRepositoryEntity->setName($imageEntity->getName().'_v'.$imageImageRepositoryEntity->getVersion() + 1); $imageImageRepositoryEntity->setStatus(ImageStatus::AUX_FILES_PENDING); $imageImageRepositoryEntity->setImage($imageEntity); $imageImageRepositoryEntity->setRepository($repository); + $imageImageRepositoryEntity->setVersion(1); $this->entityManager->persist($imageImageRepositoryEntity); $this->entityManager->flush(); @@ -74,7 +76,7 @@ class ImportAction extends AbstractOgRepositoryController $inputData = [ 'imageName' => $image, - 'imageUuid' => $imageImageRepositoryEntity->getUuid(), + 'imageImageRepositoryUuid' => $imageImageRepositoryEntity->getUuid(), ]; $this->createService->__invoke(null, CommandTypes::CREATE_IMAGE_AUX_FILE, TraceStatus::IN_PROGRESS, $content['job_id'], $inputData); From 0cce7ff52de9c2d26cb0b58578f6e3294a584151 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 11 Apr 2025 08:14:07 +0200 Subject: [PATCH 15/16] Some improvements. Sync multi clients ogBoot. Added base64 encode run script --- src/Controller/OgAgent/RunScriptAction.php | 6 +-- .../Webhook/AgentSessionController.php | 40 ++++++++++++++++++- src/EventListener/ClientStatusNotifier.php | 15 ++++++- .../OrganizationalUnitSubscriber.php | 35 +++++++++++++++- 4 files changed, 89 insertions(+), 7 deletions(-) diff --git a/src/Controller/OgAgent/RunScriptAction.php b/src/Controller/OgAgent/RunScriptAction.php index 170c91b..0282f3a 100644 --- a/src/Controller/OgAgent/RunScriptAction.php +++ b/src/Controller/OgAgent/RunScriptAction.php @@ -39,15 +39,14 @@ class RunScriptAction extends AbstractController throw new ValidatorException('IP is required'); } - //TODO: base64 script content $data = [ 'nfn' => 'EjecutarScript', - 'scp' => $input->script, + 'scp' => base64_encode($input->script), 'ids' => '0' ]; try { - $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/EjecutarScript', [ + $response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/opengnsys/EjecutarScript', [ 'verify_peer' => false, 'verify_host' => false, 'headers' => [ @@ -74,7 +73,6 @@ class RunScriptAction extends AbstractController 'script' => $input->script, ]; - $this->createService->__invoke($client, CommandTypes::RUN_SCRIPT, TraceStatus::SUCCESS, $jobId, $inputData); } diff --git a/src/Controller/OgAgent/Webhook/AgentSessionController.php b/src/Controller/OgAgent/Webhook/AgentSessionController.php index 67d6a6e..6723ea8 100644 --- a/src/Controller/OgAgent/Webhook/AgentSessionController.php +++ b/src/Controller/OgAgent/Webhook/AgentSessionController.php @@ -7,6 +7,7 @@ namespace App\Controller\OgAgent\Webhook; use App\Entity\Client; use App\Model\ClientStatus; use Doctrine\ORM\EntityManagerInterface; +use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -18,7 +19,8 @@ use Symfony\Component\Routing\Annotation\Route; class AgentSessionController extends AbstractController { public function __construct( - protected readonly EntityManagerInterface $entityManager + protected readonly EntityManagerInterface $entityManager, + protected readonly LoggerInterface $logger ) { } @@ -58,6 +60,14 @@ class AgentSessionController extends AbstractController $this->entityManager->persist($client); $this->entityManager->flush(); + $this->logger->info('Client started', [ + 'mac' => $data['mac'], + 'ip' => $data['ip'], + 'ostype' => $data['ostype'], + 'osversion' => $data['osversion'], + 'agent_version' => $data['agent_version'], + ]); + return new JsonResponse([], Response::HTTP_OK); } @@ -82,6 +92,13 @@ class AgentSessionController extends AbstractController $this->entityManager->persist($client); $this->entityManager->flush(); + $this->logger->info('Client stopped', [ + 'mac' => $data['mac'], + 'ip' => $data['ip'], + 'ostype' => $data['ostype'], + 'osversion' => $data['osversion'], + ]); + return new JsonResponse([], Response::HTTP_OK); } @@ -117,6 +134,18 @@ class AgentSessionController extends AbstractController return new JsonResponse(['message' => 'Invalid status'], Response::HTTP_BAD_REQUEST); } + $this->entityManager->persist($client); + $this->entityManager->flush(); + + $this->logger->info('Client logged in', [ + 'ip' => $data['ip'], + 'user' => $data['user'], + 'language' => $data['language'], + 'session' => $data['session'], + 'ostype' => $data['ostype'], + 'osversion' => $data['osversion'], + ]); + return new JsonResponse([], Response::HTTP_OK); } @@ -152,6 +181,15 @@ class AgentSessionController extends AbstractController return new JsonResponse(['message' => 'Invalid status'], Response::HTTP_BAD_REQUEST); } + $this->entityManager->persist($client); + $this->entityManager->flush(); + + $this->logger->info('Client logged out', [ + 'ip' => $data['ip'], + 'user' => $data['user'], + 'ostype' => $data['ostype'], + ]); + return new JsonResponse([], Response::HTTP_OK); } } diff --git a/src/EventListener/ClientStatusNotifier.php b/src/EventListener/ClientStatusNotifier.php index 8ec0ff9..b4ab843 100644 --- a/src/EventListener/ClientStatusNotifier.php +++ b/src/EventListener/ClientStatusNotifier.php @@ -25,6 +25,14 @@ class ClientStatusNotifier public function postUpdate(Client $client, PostUpdateEventArgs $event): void { + $em = $event->getObjectManager(); + $uow = $em->getUnitOfWork(); + $changeSet = $uow->getEntityChangeSet($client); + + if (!array_key_exists('status', $changeSet)) { + return; + } + try { $this->notifyClientStatusChange($client); } catch (\Exception $e) { @@ -38,9 +46,14 @@ class ClientStatusNotifier private function notifyClientStatusChange(Client $client): void { + $data[] = [ + '@id' => '/clients/' . $client->getUuid(), + 'status' => $client->getStatus(), + ]; + $update = new Update( 'clients', - json_encode(['@id' => '/clients/'.$client->getUuid(), 'status' => $client->getStatus()]) + json_encode($data) ); $this->hub->publish($update); diff --git a/src/EventSubscriber/OrganizationalUnitSubscriber.php b/src/EventSubscriber/OrganizationalUnitSubscriber.php index 57f0adb..a25eae3 100644 --- a/src/EventSubscriber/OrganizationalUnitSubscriber.php +++ b/src/EventSubscriber/OrganizationalUnitSubscriber.php @@ -5,6 +5,7 @@ namespace App\EventSubscriber; use ApiPlatform\Symfony\EventListener\EventPriorities; use App\Controller\OgBoot\PxeBootFile\PostAction; use App\Dto\Output\OrganizationalUnitOutput; +use App\Entity\Client; use App\Entity\NetworkSettings; use App\Entity\OrganizationalUnit; use Doctrine\ORM\EntityManagerInterface; @@ -12,11 +13,16 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\ViewEvent; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; final readonly class OrganizationalUnitSubscriber implements EventSubscriberInterface { public function __construct( private EntityManagerInterface $entityManager, + private readonly PostAction $postAction, ) { @@ -29,6 +35,12 @@ final readonly class OrganizationalUnitSubscriber implements EventSubscriberInte ]; } + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ public function updateNetworkSettings(ViewEvent $event): void { $organizationalUnitOutput = $event->getControllerResult(); @@ -49,8 +61,9 @@ final readonly class OrganizationalUnitSubscriber implements EventSubscriberInte $newNetworkSettings = $this->buildNetworkSettings($organizationalUnitEntity); $this->updateChildrenNetworkSettings($organizationalUnitEntity, $newNetworkSettings); - $this->entityManager->flush(); + + $this->syncOgBoot($organizationalUnitEntity); } @@ -103,4 +116,24 @@ final readonly class OrganizationalUnitSubscriber implements EventSubscriberInte return $newNetworkSettings; } + + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + private function syncOgBoot (OrganizationalUnit $organizationalUnitEntity): void + { + $clients = $this->entityManager->getRepository(Client::class)->findClientsByOrganizationalUnitAndDescendants($organizationalUnitEntity->getId(), []); + + /** @var Client $client */ + foreach ($clients as $client) { + if ($client->getTemplate() === null) { + continue; + } + + $this->postAction->__invoke($client, $client->getTemplate()); + } + } } \ No newline at end of file From b1fbc4787aa0228a493479b59f00307c360a05f6 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 11 Apr 2025 09:08:20 +0200 Subject: [PATCH 16/16] Changed CHANGELOG --- CHANGELOG.md | 15 +++++++++++++++ .../OrganizationalUnitSubscriber.php | 14 +++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc21655..327bc49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,19 @@ # Changelog +## [0.11.0] - 2025-04-11 +### Added +- Se ha añadido funcionalidad para renombrar imagenes en ogRepository. Nuevo sistema de versionado. +- Se ha añadido la integracion con el ogAgent para poder ejecutar scripts. +- Se ha añadido el poder añadir descripcion a una imagen. +- Se han añadido 2 nuevos campos en la gestion de los repositorios: usuario y puerto ssh. +- Se ha añadido funcionalidad para poder gestionar el estado de un equipo de manera automatica. En caso de no haber conexion con el cliente, la web sera notificada en un tiempo maximo de 5 min. + +### Improved +- Se han modificado los logs para que puedan "salir" por syslog ademas de por fichero. + +### Fixed +- Se ha corregido el bug que hacia que cuando habia demasiados clientes, no se mostraran en pantalla debido a un error de memoria. +--- + ## [0.10.1] - 2025-03-25 ### Improved - Se ha modificado el script de creación de usuarios, añadiendole la opcion del tipo de visionalizacion por defecto de la vista "grupos". diff --git a/src/EventSubscriber/OrganizationalUnitSubscriber.php b/src/EventSubscriber/OrganizationalUnitSubscriber.php index a25eae3..b41a44b 100644 --- a/src/EventSubscriber/OrganizationalUnitSubscriber.php +++ b/src/EventSubscriber/OrganizationalUnitSubscriber.php @@ -8,6 +8,7 @@ use App\Dto\Output\OrganizationalUnitOutput; use App\Entity\Client; use App\Entity\NetworkSettings; use App\Entity\OrganizationalUnit; +use App\Model\OrganizationalUnitTypes; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; @@ -61,9 +62,10 @@ final readonly class OrganizationalUnitSubscriber implements EventSubscriberInte $newNetworkSettings = $this->buildNetworkSettings($organizationalUnitEntity); $this->updateChildrenNetworkSettings($organizationalUnitEntity, $newNetworkSettings); - $this->entityManager->flush(); - $this->syncOgBoot($organizationalUnitEntity); + if ($organizationalUnitEntity->getType() === OrganizationalUnitTypes::CLASSROOM) { + //$this->syncOgBoot($organizationalUnitEntity); + } } @@ -85,6 +87,7 @@ final readonly class OrganizationalUnitSubscriber implements EventSubscriberInte } $this->entityManager->persist($childUnit); + $this->entityManager->flush(); $this->updateChildrenNetworkSettings($childUnit, $networkSettings); } @@ -113,6 +116,7 @@ final readonly class OrganizationalUnitSubscriber implements EventSubscriberInte $newNetworkSettings->setMenu($organizationalUnitEntity->getNetworkSettings()->getMenu()); $newNetworkSettings->setRepository($organizationalUnitEntity->getNetworkSettings()->getRepository()); $newNetworkSettings->setOgLive($organizationalUnitEntity->getNetworkSettings()->getOgLive()); + $newNetworkSettings->setNetiface($organizationalUnitEntity->getNetworkSettings()->getNetiface()); return $newNetworkSettings; } @@ -123,10 +127,14 @@ final readonly class OrganizationalUnitSubscriber implements EventSubscriberInte * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - private function syncOgBoot (OrganizationalUnit $organizationalUnitEntity): void + private function syncOgBoot(OrganizationalUnit $organizationalUnitEntity): void { $clients = $this->entityManager->getRepository(Client::class)->findClientsByOrganizationalUnitAndDescendants($organizationalUnitEntity->getId(), []); + if (empty($clients)) { + return; + } + /** @var Client $client */ foreach ($clients as $client) { if ($client->getTemplate() === null) {