From a3f271a9aef2c87d32f3bdaf39fefc47fddf38e4 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Tue, 20 May 2025 16:25:54 +0200 Subject: [PATCH 01/16] Added trace date filter --- config/api_platform/Trace.yaml | 2 ++ config/services/api_platform.yaml | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/config/api_platform/Trace.yaml b/config/api_platform/Trace.yaml index 65a6a32..3d64fa4 100644 --- a/config/api_platform/Trace.yaml +++ b/config/api_platform/Trace.yaml @@ -9,6 +9,8 @@ resources: filters: - 'api_platform.filter.trace.order' - 'api_platform.filter.trace.search' + - 'api_platform.filter.trace.date' + ApiPlatform\Metadata\Get: provider: App\State\Provider\TraceProvider diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index 2a7b829..0f70054 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -304,6 +304,11 @@ services: $orderParameterName: 'order' tags: [ 'api_platform.filter' ] + api_platform.filter.trace.date: + parent: 'api_platform.doctrine.orm.date_filter' + arguments: [ { 'executedAt': ~, 'createdAt': ~ } ] + tags: [ 'api_platform.filter' ] + api_platform.filter.user.order: parent: 'api_platform.doctrine.orm.order_filter' arguments: From 76a90d11297156a0f05ebc01882022ee6cc47f99 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 22 May 2025 18:29:19 +0200 Subject: [PATCH 02/16] refs #2084. New env to ogBoot --- env.json | 5 ++- .../OgBoot/AbstractOgBootController.php | 31 ++++++++++++++----- src/Controller/OgBoot/OgLive/GetAction.php | 6 +++- .../OgBoot/OgLive/GetCollectionAction.php | 6 +++- .../OgBoot/OgLive/GetDefaultAction.php | 7 ++++- .../OgBoot/OgLive/GetIsosAction.php | 2 +- .../OgBoot/OgLive/InstallAction.php | 6 +++- .../OgBoot/OgLive/SetDefaultAction.php | 6 +++- src/Controller/OgBoot/OgLive/SyncAction.php | 6 +++- .../OgBoot/OgLive/UninstallAction.php | 6 +++- .../OgBoot/PxeBootFile/DeleteAction.php | 10 ++---- .../OgBoot/PxeBootFile/GetAction.php | 10 ++---- .../PxeBootFile/GetCollectionAction.php | 2 +- .../OgBoot/PxeBootFile/PostAction.php | 12 ++++--- .../OgBoot/PxeTemplate/DeleteAction.php | 7 ++++- .../OgBoot/PxeTemplate/GetAction.php | 10 ++---- .../PxeTemplate/GetCollectionAction.php | 17 ++++------ .../OgBoot/PxeTemplate/PostAction.php | 7 ++++- .../OgBoot/PxeTemplate/SyncAction.php | 8 +++-- src/Dto/Input/BootClientsInput.php | 8 +++++ src/Service/OgBoot/StatusService.php | 14 ++++++--- 21 files changed, 123 insertions(+), 63 deletions(-) create mode 100644 src/Dto/Input/BootClientsInput.php diff --git a/env.json b/env.json index d77f392..9020b33 100644 --- a/env.json +++ b/env.json @@ -8,6 +8,9 @@ "UDS_AUTH_USERNAME": "test", "UDS_AUTH_PASSWORD": "test", "UDS_URL": "https:\/\/localhost:8087\/uds\/rest\/", - "SSL_ENABLED": "true" + "SSL_ENABLED": "true", + "OG_BOOT_IP": "127.0.0.1", + "OG_BOOT_API_PORT": "8082", + "OG_BOOT_PXE_PORT": "8085" } } \ No newline at end of file diff --git a/src/Controller/OgBoot/AbstractOgBootController.php b/src/Controller/OgBoot/AbstractOgBootController.php index c85e0fd..18f6bea 100644 --- a/src/Controller/OgBoot/AbstractOgBootController.php +++ b/src/Controller/OgBoot/AbstractOgBootController.php @@ -9,6 +9,7 @@ use App\Service\Trace\CreateService; use App\Service\Utils\ExtractOgLiveFilenameDateService; use App\Service\Utils\SimplifyOgLiveFilenameService; use Doctrine\ORM\EntityManagerInterface; +use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\HttpFoundation\JsonResponse; @@ -25,8 +26,12 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; abstract class AbstractOgBootController extends AbstractController { public function __construct( - #[Autowire(env: 'OG_BOOT_API_URL')] - protected string $ogBootApiUrl, + #[Autowire(env: 'OG_BOOT_IP')] + protected string $ogBootIp, + #[Autowire(env: 'OG_BOOT_PXE_PORT')] + protected string $ogBootPxePort, + #[Autowire(env: 'OG_BOOT_API_PORT')] + protected string $ogBootApiPort, #[Autowire(env: 'OG_CORE_IP')] protected string $ogCoreIP, #[Autowire(env: 'OG_LOG_IP')] @@ -36,6 +41,8 @@ abstract class AbstractOgBootController extends AbstractController protected readonly CreateService $createService, protected readonly SimplifyOgLiveFilenameService $simplifyOgLiveFilenameService, protected readonly ExtractOgLiveFilenameDateService $extractOgLiveFilenameDateService, + protected readonly LoggerInterface $logger, + ) { } @@ -56,15 +63,25 @@ abstract class AbstractOgBootController extends AbstractController ]); try { - $response = $this->httpClient->request($method, $url, $params); + $response = $this->httpClient->request($method, 'http://'.$this->ogBootIp.':'.$this->ogBootApiPort.$url, $params); return json_decode($response->getContent(), true); } catch (ClientExceptionInterface | ServerExceptionInterface $e) { - $response = $e->getResponse(); - $content = json_decode($response->getContent(false), true); - throw new HttpException($response->getStatusCode(), $content['error'] ?? 'An error occurred'); + $this->logger->error(sprintf('Client/Server error in request to %s: %s', $url, $e->getMessage())); + + return [ + 'code' => Response::HTTP_INTERNAL_SERVER_ERROR, + 'error' => 'Client/Server error', + 'details' => $e->getMessage(), + ]; } catch (TransportExceptionInterface $e) { - throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, $e->getMessage()); + $this->logger->error(sprintf('Transport error in request to %s: %s', $url, $e->getMessage())); + + return [ + 'code' => Response::HTTP_INTERNAL_SERVER_ERROR, + 'error' => 'Transport error', + 'details' => $e->getMessage(), + ]; } } } diff --git a/src/Controller/OgBoot/OgLive/GetAction.php b/src/Controller/OgBoot/OgLive/GetAction.php index 0417416..296d4bd 100644 --- a/src/Controller/OgBoot/OgLive/GetAction.php +++ b/src/Controller/OgBoot/OgLive/GetAction.php @@ -29,7 +29,11 @@ class GetAction extends AbstractOgBootController throw new ValidatorException('Checksum is required'); } - $content = $this->createRequest('GET', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/oglives/'.$data->getChecksum()); + $content = $this->createRequest('GET', '/ogboot/v1/oglives/'.$data->getChecksum()); + + if (isset($content['error']) && $content['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('An error occurred: ' . $content['error']); + } return new JsonResponse(data: $content, status: Response::HTTP_OK); } diff --git a/src/Controller/OgBoot/OgLive/GetCollectionAction.php b/src/Controller/OgBoot/OgLive/GetCollectionAction.php index 91204ac..2427f49 100644 --- a/src/Controller/OgBoot/OgLive/GetCollectionAction.php +++ b/src/Controller/OgBoot/OgLive/GetCollectionAction.php @@ -23,7 +23,11 @@ class GetCollectionAction extends AbstractOgBootController */ public function __invoke(): JsonResponse { - $content = $this->createRequest('GET', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/oglives'); + $content = $this->createRequest('GET', '/ogboot/v1/oglives'); + + if (isset($content['error']) && $content['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('An error occurred: ' . $content['error']); + } return new JsonResponse(data: $content, status: Response::HTTP_OK); } diff --git a/src/Controller/OgBoot/OgLive/GetDefaultAction.php b/src/Controller/OgBoot/OgLive/GetDefaultAction.php index e5ef4f5..7b6ef5a 100644 --- a/src/Controller/OgBoot/OgLive/GetDefaultAction.php +++ b/src/Controller/OgBoot/OgLive/GetDefaultAction.php @@ -6,6 +6,7 @@ use App\Controller\OgBoot\AbstractOgBootController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; @@ -22,7 +23,11 @@ class GetDefaultAction extends AbstractOgBootController */ public function __invoke(): JsonResponse { - $content = $this->createRequest('GET', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/oglives/default'); + $content = $this->createRequest('GET', '/ogboot/v1/oglives/default'); + + if (isset($content['error']) && $content['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('An error occurred: ' . $content['error']); + } return new JsonResponse(status: Response::HTTP_OK); } diff --git a/src/Controller/OgBoot/OgLive/GetIsosAction.php b/src/Controller/OgBoot/OgLive/GetIsosAction.php index f451d88..52e08c6 100644 --- a/src/Controller/OgBoot/OgLive/GetIsosAction.php +++ b/src/Controller/OgBoot/OgLive/GetIsosAction.php @@ -23,7 +23,7 @@ class GetIsosAction extends AbstractOgBootController */ public function __invoke(): JsonResponse { - $content = $this->createRequest('GET', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/oglives/isos'); + $content = $this->createRequest('GET', '/ogboot/v1/oglives/isos'); if (!isset($content['message']) || !is_array($content['message'])) { return new JsonResponse(data: ['error' => 'Invalid response'], status: Response::HTTP_BAD_REQUEST); diff --git a/src/Controller/OgBoot/OgLive/InstallAction.php b/src/Controller/OgBoot/OgLive/InstallAction.php index f736f13..95c4e91 100644 --- a/src/Controller/OgBoot/OgLive/InstallAction.php +++ b/src/Controller/OgBoot/OgLive/InstallAction.php @@ -45,7 +45,11 @@ class InstallAction extends AbstractOgBootController 'uuid' => 'InstallOgLive_'.$data->getUuid() ]; - $content = $this->createRequest('POST', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/oglives/install', $params); + $content = $this->createRequest('POST', '/ogboot/v1/oglives/install', $params); + + if (isset($content['error']) && $content['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('An error occurred: ' . $content['error']); + } $this->createService->__invoke(null, CommandTypes::INSTALL_OGLIVE, TraceStatus::IN_PROGRESS, 'InstallOgLive_'.$data->getUuid(), $inputData); diff --git a/src/Controller/OgBoot/OgLive/SetDefaultAction.php b/src/Controller/OgBoot/OgLive/SetDefaultAction.php index 94bf7b1..d148b1b 100644 --- a/src/Controller/OgBoot/OgLive/SetDefaultAction.php +++ b/src/Controller/OgBoot/OgLive/SetDefaultAction.php @@ -36,7 +36,11 @@ class SetDefaultAction extends AbstractOgBootController ] ]; - $content = $this->createRequest('PUT', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/oglives/default', $params); + $content = $this->createRequest('PUT', '/ogboot/v1/oglives/default', $params); + + if (isset($content['error']) && $content['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('An error occurred: ' . $content['error']); + } $oldDefaultOgLive = $this->entityManager->getRepository(OgLive::class)->findBy(['isDefault' => true]); diff --git a/src/Controller/OgBoot/OgLive/SyncAction.php b/src/Controller/OgBoot/OgLive/SyncAction.php index 69cabd0..d2c8df1 100644 --- a/src/Controller/OgBoot/OgLive/SyncAction.php +++ b/src/Controller/OgBoot/OgLive/SyncAction.php @@ -30,7 +30,11 @@ class SyncAction extends AbstractOgBootController */ public function __invoke(): JsonResponse { - $content = $this->createRequest('GET', 'http://'.$this->ogBootApiUrl . '/ogboot/v1/oglives'); + $content = $this->createRequest('GET', '/ogboot/v1/oglives'); + + if (isset($content['error']) && $content['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('An error occurred: ' . $content['error']); + } $allOgLives = $this->entityManager->getRepository(OgLive::class)->findAll(); $apiChecksums = array_map(fn($ogLive) => $ogLive['id'], $content['message']['installed_ogLives']); diff --git a/src/Controller/OgBoot/OgLive/UninstallAction.php b/src/Controller/OgBoot/OgLive/UninstallAction.php index fe64ce8..4d72aee 100644 --- a/src/Controller/OgBoot/OgLive/UninstallAction.php +++ b/src/Controller/OgBoot/OgLive/UninstallAction.php @@ -31,7 +31,11 @@ class UninstallAction extends AbstractOgBootController throw new ValidatorException('Checksum is required'); } - $content = $this->createRequest( 'DELETE', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/oglives/'.$data->getChecksum()); + $content = $this->createRequest( 'DELETE', '/ogboot/v1/oglives/'.$data->getChecksum()); + + if (isset($content['error']) && $content['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('An error occurred: ' . $content['error']); + } $this->entityManager->remove($data); $this->entityManager->flush(); diff --git a/src/Controller/OgBoot/PxeBootFile/DeleteAction.php b/src/Controller/OgBoot/PxeBootFile/DeleteAction.php index 884bd11..4852896 100644 --- a/src/Controller/OgBoot/PxeBootFile/DeleteAction.php +++ b/src/Controller/OgBoot/PxeBootFile/DeleteAction.php @@ -23,13 +23,9 @@ class DeleteAction extends AbstractOgBootController */ public function __invoke(string $mac): JsonResponse { - try { - $response = $this->httpClient->request('DELETE', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxes/'.$mac, [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { + $response = $this->createRequest('DELETE', '/ogboot/v1/pxes/'.$mac); + + if ($response->getStatusCode() !== Response::HTTP_OK) { return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } diff --git a/src/Controller/OgBoot/PxeBootFile/GetAction.php b/src/Controller/OgBoot/PxeBootFile/GetAction.php index e2efc0d..9639f52 100644 --- a/src/Controller/OgBoot/PxeBootFile/GetAction.php +++ b/src/Controller/OgBoot/PxeBootFile/GetAction.php @@ -26,13 +26,9 @@ class GetAction extends AbstractOgBootController */ public function __invoke(Client $client): JsonResponse { - try { - $response = $this->httpClient->request('GET', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxes/'.$client->getMac(), [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { + $response = $this->createRequest('GET', '/ogboot/v1/pxes/'.$client->getName()); + + if ($response->getStatusCode() !== Response::HTTP_OK) { return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } diff --git a/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php b/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php index 211d548..8a10792 100644 --- a/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php +++ b/src/Controller/OgBoot/PxeBootFile/GetCollectionAction.php @@ -26,7 +26,7 @@ class GetCollectionAction extends AbstractOgBootController */ public function __invoke(HttpClientInterface $httpClient): JsonResponse { - $content = $this->createRequest('GET', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxes'); + $content = $this->createRequest('GET', '/ogboot/v1/pxes'); return new JsonResponse(data: $content, status: Response::HTTP_OK); } diff --git a/src/Controller/OgBoot/PxeBootFile/PostAction.php b/src/Controller/OgBoot/PxeBootFile/PostAction.php index 75a30c3..71f892c 100644 --- a/src/Controller/OgBoot/PxeBootFile/PostAction.php +++ b/src/Controller/OgBoot/PxeBootFile/PostAction.php @@ -28,7 +28,7 @@ class PostAction extends AbstractOgBootController */ public function __invoke(Client $client, PxeTemplate $pxeTemplate): JsonResponse { - $ogRepoIp = $this->ogBootApiUrl; + $ogRepoIp = $this->ogBootIp; $ogRepoIp = $client->getRepository()?->getIp() ?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getRepository()?->getIp(); @@ -42,7 +42,9 @@ class PostAction extends AbstractOgBootController 'mac' => strtolower($client->getMac()), 'lang' => 'es_ES.UTF-8', 'ip' => $client->getIp(), - 'server_ip' => $this->ogBootApiUrl, + 'server_ip' => $this->ogBootIp, + 'server_api_port' => $this->ogBootApiPort, + 'server_pxe_port' => $this->ogBootPxePort, 'router' => $client->getOrganizationalUnit()->getNetworkSettings()->getRouter(), 'netmask' => $client->getOrganizationalUnit()->getNetworkSettings() ? $client->getOrganizationalUnit()->getNetworkSettings()->getNetmask() : '255.255.255.0', 'computer_name' => $client->getName(), @@ -50,10 +52,10 @@ class PostAction extends AbstractOgBootController 'group' => $client->getOrganizationalUnit()->getName(), 'ogrepo' => $ogRepoIp, 'ogcore' => $this->ogCoreIP, - 'oglive' => $this->ogBootApiUrl, + 'oglive' => $this->ogBootIp, 'oglog' => $this->ogLogIp, 'ogshare' => $client->getOrganizationalUnit()->getNetworkSettings()?->getOgShare() - ? $client->getOrganizationalUnit()->getNetworkSettings()?->getOgShare(): $this->ogBootApiUrl, + ? $client->getOrganizationalUnit()->getNetworkSettings()?->getOgShare(): $this->ogBootIp, 'oglivedir' => $ogLive, 'ogprof' => 'false', 'hardprofile' => $client->getHardwareProfile() ? $client->getHardwareProfile()->getDescription() : 'default', @@ -64,7 +66,7 @@ class PostAction extends AbstractOgBootController ] ]; - $content = $this->createRequest('POST', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxes', $params); + $content = $this->createRequest('POST', '/ogboot/v1/pxes', $params); $client->setPxeSync(true); $this->entityManager->persist($client); diff --git a/src/Controller/OgBoot/PxeTemplate/DeleteAction.php b/src/Controller/OgBoot/PxeTemplate/DeleteAction.php index c816365..f319303 100644 --- a/src/Controller/OgBoot/PxeTemplate/DeleteAction.php +++ b/src/Controller/OgBoot/PxeTemplate/DeleteAction.php @@ -10,6 +10,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; @@ -30,7 +31,11 @@ class DeleteAction extends AbstractOgBootController $this->entityManager->remove($data); $this->entityManager->flush(); - $content = $this->createRequest('DELETE', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxe-templates/'.$data->getName()); + $content = $this->createRequest('DELETE', '/ogboot/v1/pxe-templates/'.$data->getName()); + + if (isset($content['error']) && $content['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('An error occurred: ' . $content['error']); + } $defaultTemplateEntity = $this->entityManager->getRepository(PxeTemplate::class)->findOneBy(['isDefault' => true]); diff --git a/src/Controller/OgBoot/PxeTemplate/GetAction.php b/src/Controller/OgBoot/PxeTemplate/GetAction.php index 6a61c17..cc94c2c 100644 --- a/src/Controller/OgBoot/PxeTemplate/GetAction.php +++ b/src/Controller/OgBoot/PxeTemplate/GetAction.php @@ -25,13 +25,9 @@ class GetAction extends AbstractOgBootController */ public function __invoke(PxeTemplate $template, HttpClientInterface $httpClient): JsonResponse { - try { - $response = $httpClient->request('GET', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxe-templates/'.$template->getName(), [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { + $response = $this->createRequest('GET', '/ogboot/v1/pxe-templates/'.$template->getName()); + + if ($response->getStatusCode() !== Response::HTTP_OK) { return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } diff --git a/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php b/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php index 623c8d3..936f2b4 100644 --- a/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php +++ b/src/Controller/OgBoot/PxeTemplate/GetCollectionAction.php @@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; @@ -24,18 +25,12 @@ class GetCollectionAction extends AbstractOgBootController */ public function __invoke(HttpClientInterface $httpClient): JsonResponse { - try { - $response = $httpClient->request('GET', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxe-templates', [ - 'headers' => [ - 'accept' => 'application/json', - ], - ]); - } catch (TransportExceptionInterface $e) { - return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); + $content = $this->createRequest('GET', '/ogboot/v1/pxe-templates'); + + if (isset($content['error']) && $content['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('An error occurred: ' . $content['error']); } - $data = json_decode($response->getContent(), true); - - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse( data: $content, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeTemplate/PostAction.php b/src/Controller/OgBoot/PxeTemplate/PostAction.php index 2ad106c..42ff754 100644 --- a/src/Controller/OgBoot/PxeTemplate/PostAction.php +++ b/src/Controller/OgBoot/PxeTemplate/PostAction.php @@ -9,6 +9,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; @@ -33,7 +34,11 @@ class PostAction extends AbstractOgBootController ] ]; - $content = $this->createRequest('POST', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/pxe-templates' , $params); + $content = $this->createRequest('POST', '/ogboot/v1/pxe-templates' , $params); + + if (isset($content['error']) && $content['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('An error occurred: ' . $content['error']); + } $data->setSynchronized(true); $this->entityManager->persist($data); diff --git a/src/Controller/OgBoot/PxeTemplate/SyncAction.php b/src/Controller/OgBoot/PxeTemplate/SyncAction.php index 4988b84..1d9e2b6 100644 --- a/src/Controller/OgBoot/PxeTemplate/SyncAction.php +++ b/src/Controller/OgBoot/PxeTemplate/SyncAction.php @@ -28,7 +28,7 @@ class SyncAction extends AbstractOgBootController */ public function __invoke(): JsonResponse { - $content = $this->createRequest('GET', 'http://' . $this->ogBootApiUrl . '/ogboot/v1/pxe-templates'); + $content = $this->createRequest('GET', '/ogboot/v1/pxe-templates'); $templateNamesFromApi = $content['message']; $existingTemplates = $this->entityManager->getRepository(PxeTemplate::class)->findAll(); @@ -49,7 +49,11 @@ class SyncAction extends AbstractOgBootController $templateEntity->setName($templateName); } - $templateContent = $this->createRequest('GET', 'http://' . $this->ogBootApiUrl . '/ogboot/v1/pxe-templates/' . $templateEntity->getName()); + $templateContent = $this->createRequest('GET', '/ogboot/v1/pxe-templates/' . $templateEntity->getName()); + + if (isset($templateContent['error']) && $templateContent['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('An error occurred: ' . $templateContent['error']); + } $templateEntity->setTemplateContent($templateContent['template_content']); $templateEntity->setSynchronized(true); diff --git a/src/Dto/Input/BootClientsInput.php b/src/Dto/Input/BootClientsInput.php new file mode 100644 index 0000000..4bc68b0 --- /dev/null +++ b/src/Dto/Input/BootClientsInput.php @@ -0,0 +1,8 @@ + false, // Ignorar la verificación del certificado SSL - 'verify_host' => false, // Ignorar la verificación del nombre del host + 'verify_peer' => false, + 'verify_host' => false, ]); try { - $response = $httpClient->request('GET', 'http://'.$this->ogBootApiUrl.'/ogboot/v1/status', [ + $response = $httpClient->request('GET', 'http://'.$this->ogBootIp.':'.$this->ogBootApiPort.'/ogboot/v1/status', [ 'headers' => [ 'accept' => 'application/json', ], From 9be2f5fd9b1e428f4b002f6230de6b1befab6c0e Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 23 May 2025 09:23:41 +0200 Subject: [PATCH 03/16] refs #2085. Boot OS --- config/api_platform/Client.yaml | 6 +++--- src/Controller/OgAgent/LoginAction.php | 18 ++++++++++++------ src/Controller/OgAgent/RebootAction.php | 2 +- src/Dto/Input/BootClientsInput.php | 19 ++++++++++++++++--- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/config/api_platform/Client.yaml b/config/api_platform/Client.yaml index ad274d0..fb4a8c3 100644 --- a/config/api_platform/Client.yaml +++ b/config/api_platform/Client.yaml @@ -50,11 +50,11 @@ resources: uriTemplate: /clients/server/{uuid}/get-pxe controller: App\Controller\OgBoot\PxeBootFile\GetAction - login_client: + boot_client: class: ApiPlatform\Metadata\Post method: POST - input: App\Dto\Input\MultipleClientsInput - uriTemplate: /clients/server/login-client + input: App\Dto\Input\BootClientsInput + uriTemplate: /clients/server/boot-client controller: App\Controller\OgAgent\LoginAction reboot_client: diff --git a/src/Controller/OgAgent/LoginAction.php b/src/Controller/OgAgent/LoginAction.php index a6d798f..260f02f 100644 --- a/src/Controller/OgAgent/LoginAction.php +++ b/src/Controller/OgAgent/LoginAction.php @@ -4,10 +4,12 @@ declare(strict_types=1); namespace App\Controller\OgAgent; +use App\Dto\Input\BootClientsInput; use App\Dto\Input\MultipleClientsInput; use App\Entity\Client; use App\Entity\Command; use App\Entity\Image; +use App\Entity\Partition; use App\Entity\Trace; use App\Model\ClientStatus; use App\Model\CommandTypes; @@ -35,8 +37,11 @@ class LoginAction extends AbstractOgAgentController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(MultipleClientsInput $input): JsonResponse + public function __invoke(BootClientsInput $input): JsonResponse { + /** @var Partition $partition */ + $partition = $input->partition->getEntity(); + foreach ($input->clients as $clientEntity) { /** @var Client $client */ $client = $clientEntity->getEntity(); @@ -52,17 +57,18 @@ class LoginAction extends AbstractOgAgentController $data = [ 'nfn' => 'IniciarSesion', - 'dsk' => '1', - 'par' => '1', + 'dsk' => (string) $partition->getDiskNumber(), + 'par' => (string) $partition->getPartitionNumber(), 'ids' => '0' ]; $response = $this->createRequest( method: 'POST', - url: 'http://'.$client->getIp().':8000/opengnsys/IniciarSesion', + url: 'https://'.$client->getIp().':8000/opengnsys/IniciarSesion', params: [ 'json' => $data, - ] + ], + token: $client->getToken(), ); if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) { @@ -77,7 +83,7 @@ class LoginAction extends AbstractOgAgentController $this->entityManager->persist($client); $this->entityManager->flush(); - $this->createService->__invoke($client, CommandTypes::REBOOT, TraceStatus::SUCCESS, $jobId, []); + $this->createService->__invoke($client, CommandTypes::LOGIN, TraceStatus::SUCCESS, $jobId, []); } return new JsonResponse(data: [], status: Response::HTTP_OK); diff --git a/src/Controller/OgAgent/RebootAction.php b/src/Controller/OgAgent/RebootAction.php index b83af31..410bba6 100644 --- a/src/Controller/OgAgent/RebootAction.php +++ b/src/Controller/OgAgent/RebootAction.php @@ -55,7 +55,7 @@ class RebootAction extends AbstractOgAgentController $response = $this->createRequest( method: 'POST', - url: 'http://'.$client->getIp().':8000/'.$endpoint, + url: 'https://'.$client->getIp().':8000/'.$endpoint, params: [ 'json' => $data, ], diff --git a/src/Dto/Input/BootClientsInput.php b/src/Dto/Input/BootClientsInput.php index 4bc68b0..a430967 100644 --- a/src/Dto/Input/BootClientsInput.php +++ b/src/Dto/Input/BootClientsInput.php @@ -2,7 +2,20 @@ namespace App\Dto\Input; -class BootClientInput -{ +use App\Dto\Output\ClientOutput; +use App\Dto\Output\PartitionOutput; +use Symfony\Component\Serializer\Annotation\Groups; + +final class BootClientsInput +{ + /** + * @var ClientOutput[] + */ + #[Groups(['client:write'])] + public array $clients = []; + + + #[Groups(['client:write'])] + public ?PartitionOutput $partition = null; +} -} \ No newline at end of file From ee76f7aa1197ad0a29732702bd3015075b44980a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 23 May 2025 09:24:05 +0200 Subject: [PATCH 04/16] refs #2085. RemoveCacheImage --- src/Controller/OgAgent/RemoveCacheImageAction.php | 8 ++++++++ src/Dto/Output/ImageImageRepositoryOutput.php | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/Controller/OgAgent/RemoveCacheImageAction.php diff --git a/src/Controller/OgAgent/RemoveCacheImageAction.php b/src/Controller/OgAgent/RemoveCacheImageAction.php new file mode 100644 index 0000000..b34e977 --- /dev/null +++ b/src/Controller/OgAgent/RemoveCacheImageAction.php @@ -0,0 +1,8 @@ + Date: Tue, 27 May 2025 13:37:12 +0200 Subject: [PATCH 05/16] Published validate endpoint --- config/packages/security.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 8d142bc..6466847 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -33,6 +33,7 @@ security: - { path: ^/og-repository/webhook, roles: PUBLIC_ACCESS } - { path: ^/og-lives/install/webhook, roles: PUBLIC_ACCESS } - { path: ^/auth/refresh, roles: PUBLIC_ACCESS } + - { path: ^/validate, roles: PUBLIC_ACCESS } - { path: ^/menu-browser, roles: PUBLIC_ACCESS } - { path: ^/menu/, roles: PUBLIC_ACCESS } - { path: ^/, roles: IS_AUTHENTICATED_FULLY } From b86e51e1bb81e2b3a6d209ca6cb8c78c993bf259 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 30 May 2025 08:37:36 +0200 Subject: [PATCH 06/16] refs #2137. Removed subred fixed --- src/Controller/OgDhcp/Subnet/DeleteAction.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Controller/OgDhcp/Subnet/DeleteAction.php b/src/Controller/OgDhcp/Subnet/DeleteAction.php index 6a184f3..4d1b5db 100644 --- a/src/Controller/OgDhcp/Subnet/DeleteAction.php +++ b/src/Controller/OgDhcp/Subnet/DeleteAction.php @@ -7,6 +7,7 @@ use App\Entity\Subnet; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; +use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; @@ -29,11 +30,23 @@ class DeleteAction extends AbstractOgDhcpController throw new ValidatorException('Data Id is required'); } - $content = $this->createRequest('DELETE', 'http://'.$this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$data->getServerId()); + try { + $content = $this->createRequest( + 'DELETE', + 'http://' . $this->ogDhcpApiUrl . '/ogdhcp/v1/subnets/' . $data->getServerId() + ); + } catch (HttpException $e) { + if ($e->getStatusCode() === 404) { + $content = ['message' => 'Subnet not found on external API, proceeding with local deletion']; + } else { + throw $e; + } + } $this->entityManager->remove($data); $this->entityManager->flush(); return new JsonResponse(data: $content, status: Response::HTTP_OK); } + } \ No newline at end of file From de33d665dea225faa96f893c47efc95f6ac06178 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 30 May 2025 12:14:36 +0200 Subject: [PATCH 07/16] refs #2120. Remove pxe template when removed client --- src/State/Processor/ClientProcessor.php | 43 ++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/State/Processor/ClientProcessor.php b/src/State/Processor/ClientProcessor.php index 76605ff..550a8a3 100644 --- a/src/State/Processor/ClientProcessor.php +++ b/src/State/Processor/ClientProcessor.php @@ -9,18 +9,32 @@ use ApiPlatform\Metadata\Post; use ApiPlatform\Metadata\Put; use ApiPlatform\State\ProcessorInterface; use ApiPlatform\Validator\ValidatorInterface; +use App\Controller\OgBoot\PxeBootFile\DeleteAction; +use App\Controller\OgDhcp\Subnet\DeleteHostAction; use App\Dto\Input\ClientInput; use App\Dto\Output\ClientOutput; use App\Dto\Output\UserGroupOutput; +use App\Entity\OgLive; +use App\Entity\PxeTemplate; use App\Repository\ClientRepository; use App\Repository\MenuRepository; +use App\Repository\OgLiveRepository; +use App\Repository\PxeTemplateRepository; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; readonly class ClientProcessor implements ProcessorInterface { public function __construct( - private ClientRepository $clientRepository, - private MenuRepository $menuRepository, - private ValidatorInterface $validator + private ClientRepository $clientRepository, + private MenuRepository $menuRepository, + private PxeTemplateRepository $pxeTemplateRepository, + private OgLiveRepository $ogLiveRepository, + private ValidatorInterface $validator, + private DeleteHostAction $deleteHostAction, + private DeleteAction $deletePxeAction, ) { } @@ -55,6 +69,8 @@ readonly class ClientProcessor implements ProcessorInterface } $defaultMenu = $this->menuRepository->findOneBy(['isDefault' => true]); + $defaultPxe = $this->pxeTemplateRepository->findOneBy(['isDefault' => true]); + $defaultPxeOgLive = $this->ogLiveRepository->findOneBy(['isDefault' => true]); $client = $data->createOrUpdateEntity($entity); @@ -62,16 +78,33 @@ readonly class ClientProcessor implements ProcessorInterface $client->setMenu($defaultMenu); } + if ($defaultPxe) { + $client->setTemplate($defaultPxe); + } + + if ($defaultPxeOgLive) { + $client->setOgLive($defaultPxeOgLive); + } + $this->validator->validate($client); $this->clientRepository->save($client); return new ClientOutput($client); } + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null { - $user = $this->clientRepository->findOneByUuid($uriVariables['uuid']); - $this->clientRepository->delete($user); + $client = $this->clientRepository->findOneByUuid($uriVariables['uuid']); + $this->clientRepository->delete($client); + + $this->deleteHostAction->__invoke($client->getSubnet(), $client->getUuid()); + $this->deletePxeAction->__invoke($client->getUuid()); return null; } From b2684a71ad11f469b3878dc8f11afffdb87d1537 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Fri, 30 May 2025 12:28:21 +0200 Subject: [PATCH 08/16] refs #2132. 1 minute disconnected --- src/Command/CheckClientAvailability.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Command/CheckClientAvailability.php b/src/Command/CheckClientAvailability.php index bf7419e..1244437 100644 --- a/src/Command/CheckClientAvailability.php +++ b/src/Command/CheckClientAvailability.php @@ -20,7 +20,7 @@ use Symfony\Component\Mercure\Update; #[AsCommand(name: 'opengnsys:check-client-availability', description: 'Check client availability')] class CheckClientAvailability extends Command { - const int THRESHOLD_MINUTES = 3; + const int THRESHOLD_MINUTES = 1; public function __construct( private readonly HubInterface $hub, From d99493ac4639029c6a7d519b4905845a6209ff65 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 2 Jun 2025 00:08:57 +0200 Subject: [PATCH 09/16] refs #2085. RemoveCacheImage --- config/api_platform/Client.yaml | 9 ++- .../OgAgent/RemoveCacheImageAction.php | 81 ++++++++++++++++++- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/config/api_platform/Client.yaml b/config/api_platform/Client.yaml index fb4a8c3..3882d9c 100644 --- a/config/api_platform/Client.yaml +++ b/config/api_platform/Client.yaml @@ -32,7 +32,7 @@ resources: class: ApiPlatform\Metadata\Post method: POST input: App\Dto\Input\ChangeOrganizationalUnitInput - uriTemplate: /clients/change-organizational-units + uriTemplate: /clients/change-organizational-unit controller: App\Controller\ChangeOrganizationalUnitAction agent_status: @@ -57,6 +57,13 @@ resources: uriTemplate: /clients/server/boot-client controller: App\Controller\OgAgent\LoginAction + remove_cache_image: + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\BootClientsInput + uriTemplate: /clients/server/remove-cache-image + controller: App\Controller\OgAgent\RemoveCacheImageAction + reboot_client: class: ApiPlatform\Metadata\Post method: POST diff --git a/src/Controller/OgAgent/RemoveCacheImageAction.php b/src/Controller/OgAgent/RemoveCacheImageAction.php index b34e977..769453f 100644 --- a/src/Controller/OgAgent/RemoveCacheImageAction.php +++ b/src/Controller/OgAgent/RemoveCacheImageAction.php @@ -2,7 +2,84 @@ namespace App\Controller\OgAgent; -class RemoveCacheImageAction -{ +use App\Dto\Input\BootClientsInput; +use App\Entity\Client; +use App\Entity\Partition; +use App\Model\ClientStatus; +use App\Model\CommandTypes; +use App\Model\TraceStatus; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Validator\Exception\ValidatorException; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +class RemoveCacheImageAction extends AbstractOgAgentController +{ + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ + public function __invoke(BootClientsInput $input): JsonResponse + { + /** @var Partition $partition */ + $partition = $input->partition->getEntity(); + + foreach ($input->clients as $clientEntity) { + /** @var Client $client */ + $client = $clientEntity->getEntity(); + + if (!$partition->getImage()) { + throw new ValidatorException('Image is required'); + } + + 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'); + } + + $script = `rm%20-r%20/opt/opengnsys/cache/opt/opengnsys/images/{$partition->getImage()->getName()}.*@'`; + + $data = [ + 'nfn' => 'EjecutarScript', + 'scp' => base64_encode($script), + 'ids' => '0' + ]; + + $response = $this->createRequest( + method: 'POST', + url: 'https://'.$client->getIp().':8000/opengnsys/EjecutarScript', + params: [ + 'json' => $data, + ], + token: $client->getToken(), + ); + + if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) { + throw new ValidatorException('Error logging in: '.$response['error']); + } + + $this->logger->info('Login client', ['client' => $client->getId()]); + + $jobId = $response['job_id']; + + $this->entityManager->persist($client); + $this->entityManager->flush(); + + $inputData = [ + 'script' => $script, + ]; + + $this->createService->__invoke($client, CommandTypes::RUN_SCRIPT, TraceStatus::SUCCESS, $jobId, $inputData); + } + + return new JsonResponse(data: [], status: Response::HTTP_OK); + } } \ No newline at end of file From 1bd32a32bb9c21f198b35198a53a4408ee5ad3d7 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 2 Jun 2025 00:10:47 +0200 Subject: [PATCH 10/16] refs #2098. Move clients to different OU --- .../ChangeOrganizationalUnitAction.php | 24 +++++++++++++------ .../Input/ChangeOrganizationalUnitInput.php | 7 +++--- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Controller/ChangeOrganizationalUnitAction.php b/src/Controller/ChangeOrganizationalUnitAction.php index 4d0609b..66e3ec1 100644 --- a/src/Controller/ChangeOrganizationalUnitAction.php +++ b/src/Controller/ChangeOrganizationalUnitAction.php @@ -2,7 +2,9 @@ namespace App\Controller; +use App\Controller\OgBoot\PxeBootFile\PostAction; use App\Dto\Input\ChangeOrganizationalUnitInput; +use App\Dto\Output\ClientOutput; use App\Entity\Client; use App\Repository\ClientRepository; use Doctrine\ORM\EntityManagerInterface; @@ -10,29 +12,37 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; class ChangeOrganizationalUnitAction extends AbstractController { public function __construct( - private readonly ClientRepository $clientRepository, - private readonly EntityManagerInterface $entityManager + private readonly EntityManagerInterface $entityManager, + private PostAction $postAction, ) { } + /** + * @throws TransportExceptionInterface + * @throws ServerExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ClientExceptionInterface + */ public function __invoke(ChangeOrganizationalUnitInput $input): JsonResponse { foreach ($input->clients as $client) { - /** @var Client $client */ - $clientEntity = $this->clientRepository->find($client->getEntity()->getId()); - if (!$clientEntity) { - throw new NotFoundHttpException('Client not found'); - } + /** @var Client $clientEntity */ + $clientEntity = $client->getEntity(); $organizationalUnit = $input->organizationalUnit->getEntity(); $clientEntity->setOrganizationalUnit($organizationalUnit); $this->entityManager->persist($clientEntity); + $this->postAction->__invoke($clientEntity, $clientEntity->getTemplate()); } $this->entityManager->flush(); diff --git a/src/Dto/Input/ChangeOrganizationalUnitInput.php b/src/Dto/Input/ChangeOrganizationalUnitInput.php index 71f8cec..922e66b 100644 --- a/src/Dto/Input/ChangeOrganizationalUnitInput.php +++ b/src/Dto/Input/ChangeOrganizationalUnitInput.php @@ -2,6 +2,7 @@ namespace App\Dto\Input; +use App\Dto\Output\ClientOutput; use App\Dto\Output\OrganizationalUnitOutput; use App\Entity\Client; use Symfony\Component\Serializer\Annotation\Groups; @@ -10,13 +11,13 @@ use Symfony\Component\Validator\Constraints as Assert; final class ChangeOrganizationalUnitInput { /** - * @var Client[] + * @var ClientOutput[] */ #[Assert\GreaterThan(1)] - #[Groups(['user-group:write'])] + #[Groups(['client:write'])] public ?array $clients = []; #[Assert\NotNull] - #[Groups(['user-group:write'])] + #[Groups(['client:write'])] public ?OrganizationalUnitOutput $organizationalUnit = null; } \ No newline at end of file From f9a00fd3190dee056d027c402923f40050df116a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 2 Jun 2025 00:13:20 +0200 Subject: [PATCH 11/16] refs #2120. Remove pxe template when removed client --- src/Controller/OgBoot/OgLive/SyncAction.php | 4 +++- src/Controller/OgBoot/PxeBootFile/DeleteAction.php | 6 ++---- src/Controller/OgBoot/PxeBootFile/GetAction.php | 6 ++---- src/Controller/OgBoot/PxeBootFile/PostAction.php | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Controller/OgBoot/OgLive/SyncAction.php b/src/Controller/OgBoot/OgLive/SyncAction.php index d2c8df1..13d790c 100644 --- a/src/Controller/OgBoot/OgLive/SyncAction.php +++ b/src/Controller/OgBoot/OgLive/SyncAction.php @@ -80,7 +80,9 @@ class SyncAction extends AbstractOgBootController */ private function extracted(OgLive $ogLiveEntity, mixed $ogLive): void { - $ogLiveEntity->setName($this->simplifyOgLiveFilenameService->__invoke(str_replace(self::OG_BOOT_DIRECTORY, '', $ogLive['directory']))); + $name = $this->simplifyOgLiveFilenameService->__invoke(str_replace(self::OG_BOOT_DIRECTORY, '', $ogLive['directory'])); + + $ogLiveEntity->setName($name ? $name : str_replace(self::OG_BOOT_DIRECTORY, '', $ogLive['directory'])); $ogLiveEntity->setDate(new \DateTime($this->extractOgLiveFilenameDateService->__invoke(str_replace(self::OG_BOOT_DIRECTORY, '', $ogLive['directory'])))); $ogLiveEntity->setInstalled(true); $ogLiveEntity->setArchitecture($ogLive['architecture']); diff --git a/src/Controller/OgBoot/PxeBootFile/DeleteAction.php b/src/Controller/OgBoot/PxeBootFile/DeleteAction.php index 4852896..0a957a8 100644 --- a/src/Controller/OgBoot/PxeBootFile/DeleteAction.php +++ b/src/Controller/OgBoot/PxeBootFile/DeleteAction.php @@ -25,13 +25,11 @@ class DeleteAction extends AbstractOgBootController { $response = $this->createRequest('DELETE', '/ogboot/v1/pxes/'.$mac); - if ($response->getStatusCode() !== Response::HTTP_OK) { + if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) { return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } - $data = json_decode($response->getContent(), true); - - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse( data: [], status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeBootFile/GetAction.php b/src/Controller/OgBoot/PxeBootFile/GetAction.php index 9639f52..7c35630 100644 --- a/src/Controller/OgBoot/PxeBootFile/GetAction.php +++ b/src/Controller/OgBoot/PxeBootFile/GetAction.php @@ -28,12 +28,10 @@ class GetAction extends AbstractOgBootController { $response = $this->createRequest('GET', '/ogboot/v1/pxes/'.$client->getName()); - if ($response->getStatusCode() !== Response::HTTP_OK) { + if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) { return new JsonResponse( data: 'An error occurred', status: Response::HTTP_INTERNAL_SERVER_ERROR); } - $data = json_decode($response->getContent(), true); - - return new JsonResponse( data: $data, status: Response::HTTP_OK); + return new JsonResponse( data: $response, status: Response::HTTP_OK); } } \ No newline at end of file diff --git a/src/Controller/OgBoot/PxeBootFile/PostAction.php b/src/Controller/OgBoot/PxeBootFile/PostAction.php index 71f892c..28e4b8a 100644 --- a/src/Controller/OgBoot/PxeBootFile/PostAction.php +++ b/src/Controller/OgBoot/PxeBootFile/PostAction.php @@ -34,7 +34,7 @@ class PostAction extends AbstractOgBootController ?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getRepository()?->getIp(); $ogLive = $client->getOgLive()?->getFilename() - ?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getOgLive()->getFilename(); + ?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getOgLive()?->getFilename(); $params = [ 'json' => [ From 7d59467accb1cdec5cd4c11b1e15ef90a07bc14d Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 2 Jun 2025 00:15:03 +0200 Subject: [PATCH 12/16] refs #2120. Remove pxe template when removed client --- config/api_platform/Partition.yaml | 1 + src/Command/CheckClientAvailability.php | 10 ++++--- .../OgDhcp/Subnet/PostHostAction.php | 4 +-- .../OgDhcp/Subnet/PutHostAction.php | 29 +++++++++---------- src/EventSubscriber/MercureSubscriber.php | 28 ++++++++++++------ 5 files changed, 40 insertions(+), 32 deletions(-) diff --git a/config/api_platform/Partition.yaml b/config/api_platform/Partition.yaml index 7ec5daa..200c8e3 100644 --- a/config/api_platform/Partition.yaml +++ b/config/api_platform/Partition.yaml @@ -4,6 +4,7 @@ resources: input: App\Dto\Input\PartitionPostInput output: App\Dto\Output\PartitionOutput order: + diskNumber: 'ASC' partitionNumber: 'ASC' normalizationContext: groups: ['default', 'partition:read'] diff --git a/src/Command/CheckClientAvailability.php b/src/Command/CheckClientAvailability.php index 1244437..fb63a9f 100644 --- a/src/Command/CheckClientAvailability.php +++ b/src/Command/CheckClientAvailability.php @@ -34,16 +34,18 @@ class CheckClientAvailability extends Command { $io = new SymfonyStyle($input, $output); $threshold = (new \DateTime())->modify(' - '.self::THRESHOLD_MINUTES . ' minutes'); - $startQueryTime = microtime(true); + $validStatuses = [ClientStatus::OG_LIVE, ClientStatus::WINDOWS, ClientStatus::LINUX, ClientStatus::MACOS]; + $query = $this->entityManager->createQuery( 'UPDATE App\Entity\Client c - SET c.status = :status - WHERE c.status = :currentStatus AND c.updatedAt < :threshold' + SET c.status = :status + WHERE c.status IN (:currentStatuses) + AND c.updatedAt < :threshold' ); $query->setParameter('status', ClientStatus::DISCONNECTED); - $query->setParameter('currentStatus', ClientStatus::OG_LIVE); + $query->setParameter('currentStatuses', $validStatuses); $query->setParameter('threshold', $threshold); $updatedCount = $query->execute(); diff --git a/src/Controller/OgDhcp/Subnet/PostHostAction.php b/src/Controller/OgDhcp/Subnet/PostHostAction.php index ba9de31..4b518c2 100644 --- a/src/Controller/OgDhcp/Subnet/PostHostAction.php +++ b/src/Controller/OgDhcp/Subnet/PostHostAction.php @@ -53,17 +53,15 @@ class PostHostAction extends AbstractOgDhcpController $params ); - // Guardar resultado exitoso $success[] = [ 'client' => $clientEntity->getName(), 'response' => $content ]; - // Persistir solo si la llamada fue exitosa $subnet->addClient($clientEntity); $this->entityManager->persist($subnet); $this->entityManager->flush(); - } catch (\Throwable $e) { // Capturar cualquier error sin interrumpir + } catch (\Throwable $e) { $errors[] = [ 'client' => $clientEntity->getName(), 'error' => $e->getMessage() diff --git a/src/Controller/OgDhcp/Subnet/PutHostAction.php b/src/Controller/OgDhcp/Subnet/PutHostAction.php index cf89d25..d7b6a45 100644 --- a/src/Controller/OgDhcp/Subnet/PutHostAction.php +++ b/src/Controller/OgDhcp/Subnet/PutHostAction.php @@ -24,26 +24,23 @@ class PutHostAction extends AbstractOgDhcpController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(SubnetAddHostInput $input, Subnet $subnet): JsonResponse + public function __invoke(string $mac, Client $client): JsonResponse { - $clients = $input->clients; + $subnet = $client->getSubnet(); - foreach ($clients as $client) { - /** @var Client $clientEntity */ - $clientEntity = $client->getEntity(); - $data = [ - 'host' => $clientEntity->getName(), - 'oldMacAddress' => '', - 'macAddress' => '', - 'address' => '', - ]; + /** @var Client $clientEntity */ + $data = [ + 'hostname' => $client->getName(), + 'oldMacAddress' => strtolower($mac), + 'macAddress' => strtolower($client->getMac()), + 'address' => $client->getIp(), + ]; - $params = [ - 'json' => $data - ]; + $params = [ + 'json' => $data + ]; - $content = $this->createRequest('PUT', 'http://'.$this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getId().'/hosts', $params); - } + $content = $this->createRequest('PUT', 'http://'.$this->ogDhcpApiUrl.'/ogdhcp/v1/subnets/'.$subnet->getServerId().'/hosts', $params); return new JsonResponse(status: Response::HTTP_OK); } diff --git a/src/EventSubscriber/MercureSubscriber.php b/src/EventSubscriber/MercureSubscriber.php index aaf1b3d..fe44ef7 100644 --- a/src/EventSubscriber/MercureSubscriber.php +++ b/src/EventSubscriber/MercureSubscriber.php @@ -63,15 +63,25 @@ class MercureSubscriber implements EventSubscriberInterface 'status' => $client->getStatus(), ]; - $update = new Update( - 'clients', - json_encode($data) - ); - $this->hub->publish($update); + try { + $update = new Update( + 'clients', + json_encode($data) + ); - $this->logger->info('Evento Mercure disparado', [ - 'method' => $method, - 'path' => $request->getPathInfo() - ]); + $this->hub->publish($update); + + $this->logger->info('Evento Mercure disparado', [ + 'method' => $method, + 'path' => $request->getPathInfo() + ]); + } catch (\Exception $e) { + $this->logger->error('Error setting method for Mercure update', [ + 'method' => $method, + 'path' => $request->getPathInfo(), + 'error' => $e->getMessage(), + ]); + return; + } } } From 64eeb5c88e8e75820b497a12e46eaf1b165688aa Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 2 Jun 2025 00:17:42 +0200 Subject: [PATCH 13/16] refs #2133. CHanged PXE template --- src/EventSubscriber/ClientSubscriber.php | 14 ++++++++++++-- .../OrganizationalUnitMulticastPortValidator.php | 13 +++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/EventSubscriber/ClientSubscriber.php b/src/EventSubscriber/ClientSubscriber.php index 7c652c2..ed08088 100644 --- a/src/EventSubscriber/ClientSubscriber.php +++ b/src/EventSubscriber/ClientSubscriber.php @@ -4,8 +4,11 @@ namespace App\EventSubscriber; use ApiPlatform\Symfony\EventListener\EventPriorities; use App\Controller\OgBoot\PxeBootFile\PostAction; +use App\Controller\OgDhcp\Subnet\PutHostAction; use App\Dto\Output\ClientOutput; use App\Entity\Client; +use App\Entity\PxeTemplate; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\ViewEvent; @@ -18,7 +21,9 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; final readonly class ClientSubscriber implements EventSubscriberInterface { public function __construct( + private readonly EntityManagerInterface $entityManager, private PostAction $postAction, + private PutHostAction $putHostAction, ) { @@ -49,14 +54,19 @@ final readonly class ClientSubscriber implements EventSubscriberInterface /** @var Client $client */ $client = $clientOutput->getEntity(); - $template = !$client->getTemplate() ? + $template = $client->getTemplate() ? $client->getTemplate() : $client->getOrganizationalUnit()?->getNetworkSettings()?->getPxeTemplate(); if ($template === null) { - return; + $template = $this->entityManager->getRepository(PxeTemplate::class)->findOneBy(['isDefault' => true]); + + if ($template === null) { + return; + } } $this->postAction->__invoke($client, $template); + $this->putHostAction->__invoke($client->getMac(), $client); } } \ No newline at end of file diff --git a/src/Validator/Constraints/OrganizationalUnitMulticastPortValidator.php b/src/Validator/Constraints/OrganizationalUnitMulticastPortValidator.php index 6baabda..38ef7ab 100644 --- a/src/Validator/Constraints/OrganizationalUnitMulticastPortValidator.php +++ b/src/Validator/Constraints/OrganizationalUnitMulticastPortValidator.php @@ -9,8 +9,8 @@ use Symfony\Component\Validator\ConstraintValidator; class OrganizationalUnitMulticastPortValidator extends ConstraintValidator { - CONST int minPort = 9000; - CONST int maxPort = 9050; + public const int minPort = 9000; + public const int maxPort = 9098; public function validate($value, Constraint $constraint): void { @@ -18,9 +18,14 @@ class OrganizationalUnitMulticastPortValidator extends ConstraintValidator return; } - if (!(self::minPort <= $value) && ($value <= self::maxPort)) { + if ($value % 2 !== 0) { + $this->context->buildViolation('El puerto debe ser un número par y encontrarse entre el 9000 y el 9098.')->addViolation(); + return; + } + + if ($value < self::minPort || $value > self::maxPort) { $this->context->buildViolation($constraint->message)->addViolation(); return; } } -} \ No newline at end of file +} From d13e24cc233a6bfc6130d3380e41fbd437cb95c1 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 2 Jun 2025 07:35:27 +0200 Subject: [PATCH 14/16] refs #2120. Remove pxe template when removed client --- src/EventListener/ClientMacListener.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EventListener/ClientMacListener.php b/src/EventListener/ClientMacListener.php index dc21532..0d079dd 100644 --- a/src/EventListener/ClientMacListener.php +++ b/src/EventListener/ClientMacListener.php @@ -4,6 +4,7 @@ namespace App\EventListener; use App\Controller\OgBoot\PxeBootFile\DeleteAction; use App\Controller\OgBoot\PxeBootFile\PostAction; +use App\Controller\OgDhcp\Subnet\PutHostAction; use App\Entity\Client; use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener; use Doctrine\ORM\Event\PreUpdateEventArgs; @@ -18,6 +19,7 @@ readonly class ClientMacListener { public function __construct( private DeleteAction $deleteAction, + private PutHostAction $putHostAction, ) { @@ -45,7 +47,7 @@ readonly class ClientMacListener return; } + $this->putHostAction->__invoke($oldMac, $client); $this->deleteAction->__invoke($oldMac); } - } \ No newline at end of file From b7da3bd63930cb804e92385986e18aad855b1945 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 2 Jun 2025 07:39:00 +0200 Subject: [PATCH 15/16] refs #2120. Fixed test --- src/State/Processor/ClientProcessor.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/State/Processor/ClientProcessor.php b/src/State/Processor/ClientProcessor.php index 550a8a3..69c07e4 100644 --- a/src/State/Processor/ClientProcessor.php +++ b/src/State/Processor/ClientProcessor.php @@ -20,6 +20,7 @@ use App\Repository\ClientRepository; use App\Repository\MenuRepository; use App\Repository\OgLiveRepository; use App\Repository\PxeTemplateRepository; +use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; @@ -35,6 +36,7 @@ readonly class ClientProcessor implements ProcessorInterface private ValidatorInterface $validator, private DeleteHostAction $deleteHostAction, private DeleteAction $deletePxeAction, + private KernelInterface $kernel, ) { } @@ -103,8 +105,10 @@ readonly class ClientProcessor implements ProcessorInterface $client = $this->clientRepository->findOneByUuid($uriVariables['uuid']); $this->clientRepository->delete($client); - $this->deleteHostAction->__invoke($client->getSubnet(), $client->getUuid()); - $this->deletePxeAction->__invoke($client->getUuid()); + if ($this->kernel->getEnvironment() !== 'test') { + $this->deleteHostAction->__invoke($client->getSubnet(), $client->getUuid()); + $this->deletePxeAction->__invoke($client->getUuid()); + } return null; } From 0b2faa1222c2d71e096c7107474cfbc26a50e7e8 Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Mon, 2 Jun 2025 07:44:25 +0200 Subject: [PATCH 16/16] Added changelog --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f57f4cc..edbd977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,19 @@ # Changelog +## [0.14.0] - 2025-06-02 +### Added +- Se ha añadido la funcionalidad de mover equipos entre aulas y grupos. +- Se ha añadido la funcionalidad para eliminar imagen cache. +- Se ha añadido la funcionalidad para iniciar sesion. + +### Improved +- Se ha cambiado la restriccion que comprobaba los puertos de MULTICAST. +- Se ha modificado el tiempo en el script, que se encarga de comprobar el estado de los equipos. Ahora es de 1 min en lugar de 3. + +### Fixed +- Se ha corregido un bug que hacia que al modificar un cliente o eliminarlo, no se actualizaba su estado en la subred. +- Se ha corregido un bug que hacia que al modificar un cliente, no se actualizara su fichero de arranque. + +--- ## [0.13.1] - 2025-05-23 ### Fixed - Variable de entorno "SSl_ENABLED" desactivada por defecto.