From e51e1e13ea1d65e93e81d6494a4157843a16543a Mon Sep 17 00:00:00 2001 From: Manuel Aranda Date: Thu, 26 Jun 2025 17:04:50 +0200 Subject: [PATCH] refs #1975. Integration ogGit. Crete image and deploy --- .../ChangeOrganizationalUnitAction.php | 9 +- src/Controller/DeployImageAction.php | 197 ++++++----- .../OgAgent/AbstractOgAgentController.php | 3 +- src/Controller/OgAgent/CreateImageAction.php | 305 +++++++++++++----- src/Controller/OgAgent/DeployImageAction.php | 16 +- .../Webhook/AgentSessionController.php | 12 +- .../OgAgent/Webhook/StatusController.php | 35 +- .../AbstractOgRepositoryController.php | 6 +- src/Dto/Input/DeployImageInput.php | 24 +- src/Dto/Input/ImageInput.php | 9 +- src/Model/DeployMethodTypes.php | 2 + 11 files changed, 424 insertions(+), 194 deletions(-) diff --git a/src/Controller/ChangeOrganizationalUnitAction.php b/src/Controller/ChangeOrganizationalUnitAction.php index 66e3ec1..ebf76b5 100644 --- a/src/Controller/ChangeOrganizationalUnitAction.php +++ b/src/Controller/ChangeOrganizationalUnitAction.php @@ -42,7 +42,14 @@ class ChangeOrganizationalUnitAction extends AbstractController $clientEntity->setOrganizationalUnit($organizationalUnit); $this->entityManager->persist($clientEntity); - $this->postAction->__invoke($clientEntity, $clientEntity->getTemplate()); + + $template = $clientEntity->getTemplate() ?? $clientEntity->getOrganizationalUnit()->getNetworkSettings()?->getTemplate(); + + if (!$template) { + throw new BadRequestHttpException('No template found for client'); + } + + $this->postAction->__invoke($clientEntity, $template); } $this->entityManager->flush(); diff --git a/src/Controller/DeployImageAction.php b/src/Controller/DeployImageAction.php index 4d49607..2a6ef6b 100644 --- a/src/Controller/DeployImageAction.php +++ b/src/Controller/DeployImageAction.php @@ -8,6 +8,7 @@ use ApiPlatform\Validator\ValidatorInterface; use App\Dto\Input\DeployImageInput; use App\Entity\Command; use App\Entity\Image; +use App\Model\ClientStatus; use App\Entity\ImageImageRepository; use App\Entity\OrganizationalUnit; use App\Entity\Partition; @@ -31,11 +32,10 @@ class DeployImageAction extends AbstractController protected readonly EntityManagerInterface $entityManager, protected readonly HttpClientInterface $httpClient, protected readonly CreateService $createService, - protected readonly ValidatorInterface $validator, + protected readonly ValidatorInterface $validator, public readonly \App\Controller\OgAgent\DeployImageAction $deployImageOgAgentAction, public readonly \App\Controller\OgRepository\Image\DeployImageAction $deployImageOgRepositoryAction, - ) - { + ) { } /** @@ -47,90 +47,117 @@ class DeployImageAction extends AbstractController { $this->validator->validate($input); - switch ($input->method){ - case DeployMethodTypes::UNICAST: - case DeployMethodTypes::UNICAST_DIRECT: - foreach ($input->clients as $client) { - $inputData = [ - 'method' => $input->method, - 'client' => $client->getEntity()->getUuid(), - 'image' => $image->getUuid(), - 'imageName' => $image->getName(), - 'numDisk' => (string) $input->diskNumber, - 'numPartition' => (string) $input->partitionNumber, - ]; - - $agentJobId = $this->deployImageOgAgentAction->__invoke($image, $input, $client->getEntity(), DeployMethodTypes::UNICAST); - if (!$agentJobId){ - continue; - } - - $this->createService->__invoke($client->getEntity(), CommandTypes::DEPLOY_IMAGE, TraceStatus::IN_PROGRESS, $agentJobId, $inputData); - } - break; - - - case DeployMethodTypes::MULTICAST_UFTP: - case DeployMethodTypes::MULTICAST_UFTP_DIRECT: - case DeployMethodTypes::MULTICAST_UDPCAST: - case DeployMethodTypes::MULTICAST_UDPCAST_DIRECT: - foreach ($input->clients as $client) { - $inputData = [ - 'method' => $input->method, - 'client' => $client->getEntity()->getUuid(), - 'image' => $image->getUuid(), - 'mcastIp' => $input->mcastIp, - 'mcastPort' => $input->mcastPort, - 'mcastSpeed' => $input->mcastSpeed, - 'mcastMode' => $input->mcastMode, - 'numDisk' => (string) $input->diskNumber, - 'numPartition' => (string) $input->partitionNumber, - ]; - - try { - $this->deployImageOgRepositoryAction->__invoke($input, $image, $client->getEntity()); - } catch (\Exception $e) { - continue; - } - - $agentJobId = $this->deployImageOgAgentAction->__invoke($image, $input, $client->getEntity(), DeployMethodTypes::MULTICAST); - if (!$agentJobId){ - continue; - } - - $this->createService->__invoke($client->getEntity(), CommandTypes::DEPLOY_IMAGE, TraceStatus::IN_PROGRESS, $agentJobId, $inputData); - } - break; - - case DeployMethodTypes::TORRENT: - foreach ($input->clients as $client) { - $inputData = [ - 'method' => $input->method, - 'client' => $client->getEntity()->getUuid(), - 'image' => $image->getUuid(), - 'p2pMode' => $input->p2pMode, - 'p2pTime' => $input->p2pTime, - 'numDisk' => (string) $input->diskNumber, - 'numPartition' => (string) $input->partitionNumber, - ]; - - try { - $this->deployImageOgRepositoryAction->__invoke($input, $image, $client->getEntity(), $this->httpClient); - } catch (\Exception $e) { - continue; - } - - $agentJobId = $this->deployImageOgAgentAction->__invoke($image, $input, $client->getEntity(), DeployMethodTypes::TORRENT); - if (!$agentJobId){ - continue; - } - - $this->createService->__invoke($client->getEntity(), CommandTypes::DEPLOY_IMAGE, TraceStatus::IN_PROGRESS, $agentJobId, $inputData); - } - - break; + if ($input->type === 'monolithic') { + $this->handleMonolithicDeployment($input, $image); } return new JsonResponse(data: [], status: Response::HTTP_OK); } + + private function handleMonolithicDeployment(DeployImageInput $input, ImageImageRepository $image): void + { + switch ($input->method) { + case DeployMethodTypes::UNICAST: + case DeployMethodTypes::UNICAST_DIRECT: + $this->handleUnicastDeployment($input, $image); + break; + case DeployMethodTypes::MULTICAST_UFTP: + case DeployMethodTypes::MULTICAST_UFTP_DIRECT: + case DeployMethodTypes::MULTICAST_UDPCAST: + case DeployMethodTypes::MULTICAST_UDPCAST_DIRECT: + $this->handleMulticastDeployment($input, $image); + break; + case DeployMethodTypes::TORRENT: + $this->handleTorrentDeployment($input, $image); + break; + } + } + + private function handleUnicastDeployment(DeployImageInput $input, ImageImageRepository $image): void + { + foreach ($input->clients as $client) { + $inputData = $this->createInputData($input, $image, $client->getEntity()); + $this->processDeployment($client->getEntity(), $input, $image, $inputData, DeployMethodTypes::UNICAST); + } + } + + private function handleMulticastDeployment(DeployImageInput $input, ImageImageRepository $image): void + { + foreach ($input->clients as $client) { + $inputData = $this->createMulticastInputData($input, $image, $client->getEntity()); + + try { + $this->deployImageOgRepositoryAction->__invoke($input, $image, $client->getEntity()); + } catch (\Exception $e) { + continue; + } + + $this->processDeployment($client->getEntity(), $input, $image, $inputData, DeployMethodTypes::MULTICAST); + } + } + + private function handleTorrentDeployment(DeployImageInput $input, ImageImageRepository $image): void + { + foreach ($input->clients as $client) { + $inputData = $this->createTorrentInputData($input, $image, $client->getEntity()); + + try { + $response = $this->deployImageOgRepositoryAction->__invoke($input, $image, $client->getEntity(), $this->httpClient); + + if (is_array($response) && isset($response['code']) && $response['code'] === 500) { + throw new \Exception('Error del servidor OgRepository: ' . ($response['error'] ?? 'Error desconocido') . ' - ' . ($response['details'] ?? '')); + } + + } catch (\Exception $e) { + throw $e; + } + + $this->processDeployment($client->getEntity(), $input, $image, $inputData, DeployMethodTypes::TORRENT); + } + } + + private function processDeployment($client, DeployImageInput $input, ImageImageRepository $image, array $inputData, string $deployType): void + { + $agentJobId = $this->deployImageOgAgentAction->__invoke($image, $input, $client, $deployType); + + if (!$agentJobId) { + if ($input->queue) { + $this->createService->__invoke($client, CommandTypes::DEPLOY_IMAGE, TraceStatus::PENDING, null, $inputData); + } + return; + } + + $this->createService->__invoke($client, CommandTypes::DEPLOY_IMAGE, TraceStatus::IN_PROGRESS, $agentJobId, $inputData); + } + + private function createInputData(DeployImageInput $input, ImageImageRepository $image, $client): array + { + return [ + 'method' => $input->method, + 'client' => $client->getUuid(), + 'image' => $image->getUuid(), + 'imageName' => $image->getName(), + 'numDisk' => (string) $input->diskNumber, + 'numPartition' => (string) $input->partitionNumber, + 'type' => $input->type, + ]; + } + + private function createMulticastInputData(DeployImageInput $input, ImageImageRepository $image, $client): array + { + return array_merge($this->createInputData($input, $image, $client), [ + 'mcastIp' => $input->mcastIp, + 'mcastPort' => $input->mcastPort, + 'mcastSpeed' => $input->mcastSpeed, + 'mcastMode' => $input->mcastMode, + ]); + } + + private function createTorrentInputData(DeployImageInput $input, ImageImageRepository $image, $client): array + { + return array_merge($this->createInputData($input, $image, $client), [ + 'p2pMode' => $input->p2pMode, + 'p2pTime' => $input->p2pTime, + ]); + } } diff --git a/src/Controller/OgAgent/AbstractOgAgentController.php b/src/Controller/OgAgent/AbstractOgAgentController.php index 5739ef2..d89b684 100644 --- a/src/Controller/OgAgent/AbstractOgAgentController.php +++ b/src/Controller/OgAgent/AbstractOgAgentController.php @@ -4,6 +4,7 @@ namespace App\Controller\OgAgent; use App\Controller\OgRepository\Git\CreateRepositoryAction; use App\Controller\OgRepository\Git\CreateTagAction; +use App\Controller\OgRepository\Git\SshKeyAction; use App\Service\CreatePartitionService; use App\Service\Trace\CreateService; use Doctrine\ORM\EntityManagerInterface; @@ -28,7 +29,7 @@ abstract class AbstractOgAgentController extends AbstractController protected readonly LoggerInterface $logger, protected readonly CreateService $createService, protected readonly CreateRepositoryAction $createRepositoryAction, - protected readonly CreateTagAction $createTagAction, + protected readonly SshKeyAction $sshKeyAction, #[Autowire(env: 'SSL_ENABLED')] private readonly string $sslEnabled, ) diff --git a/src/Controller/OgAgent/CreateImageAction.php b/src/Controller/OgAgent/CreateImageAction.php index 6a3f032..32b96a6 100644 --- a/src/Controller/OgAgent/CreateImageAction.php +++ b/src/Controller/OgAgent/CreateImageAction.php @@ -5,10 +5,9 @@ declare(strict_types=1); namespace App\Controller\OgAgent; use App\Controller\OgRepository\Git\CreateRepositoryAction; -use App\Controller\OgRepository\Git\CreateTagAction; use App\Entity\Client; use App\Entity\Command; -use App\Entity\GitImageRepository; +use App\Entity\GitRepository; use App\Entity\Image; use App\Entity\ImageImageRepository; use App\Entity\ImageRepository; @@ -43,9 +42,11 @@ class CreateImageAction extends AbstractOgAgentController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(Image $image, ?Partition $partition = null, ?Client $client = null): JsonResponse + public function __invoke(bool $queue, Image $image, ?Partition $partition = null, ?Client $client = null, ?string $gitRepositoryName = null): JsonResponse { - if (!$image->getClient()->getIp()) { + $client = $client ?? $image->getClient(); + + if (!$client->getIp()) { throw new BadRequestHttpException('IP is required'); } @@ -76,25 +77,13 @@ class CreateImageAction extends AbstractOgAgentController $this->entityManager->persist($imageImageRepository); - return $this->createMonolithicImage($imageImageRepository, $partitionInfo, $image, $repository, $client); + return $this->createMonolithicImage($imageImageRepository, $partitionInfo, $image, $repository, $client, $queue); } else { $repository = $image->getClient()->getRepository(); - $latestImageRepo = $this->entityManager->getRepository(GitImageRepository::class)->findLatestVersionByImageAndRepository($image, $repository); - - $gitImageRepository = new GitImageRepository(); - $gitImageRepository->setName($image->getName().'_v'.($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1)); - $gitImageRepository->setImage($image); - $gitImageRepository->setRepository($repository); - $gitImageRepository->setStatus(ImageStatus::IN_PROGRESS); - $gitImageRepository->setVersion($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1); - $gitImageRepository->setTag('v'.($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1)); - $gitImageRepository->setCreated(false); - - $this->entityManager->persist($gitImageRepository); - $this->entityManager->persist($image); - $this->entityManager->flush(); - - return $this->createGitImage($gitImageRepository, $partitionInfo, $image, $repository); + + // Para imágenes Git, no necesitamos crear entidades en la base de datos + // ya que los repositorios Git son datos externos + return $this->createGitImage($image, $partitionInfo, $repository, $queue, $gitRepositoryName); } } @@ -109,9 +98,19 @@ class CreateImageAction extends AbstractOgAgentController array $partitionInfo, Image $image, ImageRepository $repository, - ?Client $client = null + ?Client $client = null, + bool $queue = false ): JsonResponse { + if (!isset($partitionInfo['numDisk'], $partitionInfo['numPartition'], $partitionInfo['partitionCode'], $partitionInfo['filesystem'])) { + throw new BadRequestHttpException('Missing required partition information'); + } + + $client = $client ?? $image->getClient(); + if (!$client->getIp() || !$client->getToken()) { + throw new BadRequestHttpException('Client IP or token is missing'); + } + $data = [ 'dsk' => (string) $partitionInfo['numDisk'], 'par' => (string) $partitionInfo['numPartition'], @@ -134,85 +133,219 @@ class CreateImageAction extends AbstractOgAgentController $data['cpt'] = dechex($partitionTypeCode); } else { - throw new Exception("El tipo de partición '$partitionCode' no se encontró en la lista."); + throw new BadRequestHttpException("Invalid partition code: {$partitionCode}"); } - $client = $client ?? $image->getClient(); $this->logger->info('Creating image', ['image' => $image->getId()]); - $response = $this->createRequest( - method: 'POST', - url: 'https://'.$client->getIp().':8000/opengnsys/CrearImagen', - params: [ - 'json' => $data, - ], - token: $client->getToken(), - ); + try { + $response = $this->createRequest( + method: 'POST', + url: 'https://'.$client->getIp().':8000/opengnsys/CrearImagen', + params: [ + 'json' => $data, + ], + token: $client->getToken(), + ); - $this->logger->info('Creating image', ['image' => $imageImageRepository->getName(), 'repository' => $repository->getIp()]); + $this->logger->info('Creating image', ['image' => $imageImageRepository->getName(), 'repository' => $repository->getIp()]); - if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) { - throw new BadRequestHttpException('Error creating image'); + if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) { + if ($queue) { + $inputData = [ + 'method' => 'CrearImagen', + 'type' => 'monolithic', + 'client' => $client->getUuid(), + 'image' => $image->getUuid(), + 'partitionCode' => $partitionInfo['partitionCode'], + 'partitionType' => $partitionInfo['filesystem'], + 'repository' => $repository->getIp(), + 'name' => $image->getName().'_v'.$imageImageRepository->getVersion(), + ]; + $this->createService->__invoke($client, CommandTypes::CREATE_IMAGE, TraceStatus::PENDING, null, $inputData); + return new JsonResponse(data: [], status: Response::HTTP_OK); + } + + throw new BadRequestHttpException('Error creating image: ' . ($response['message'] ?? 'Unknown error')); + } + + if (!isset($response['job_id'])) { + throw new BadRequestHttpException('No job ID received from server'); + } + + $jobId = $response['job_id']; + + try { + $client->setStatus(ClientStatus::BUSY); + $imageImageRepository->setStatus(ImageStatus::IN_PROGRESS); + + $this->entityManager->persist($client); + $this->entityManager->persist($imageImageRepository); + $this->entityManager->flush(); + + $inputData = [ + 'method' => 'CrearImagen', + 'type' => 'monolithic', + 'client' => $client->getUuid(), + 'image' => $image->getUuid(), + 'partitionCode' => $partitionInfo['partitionCode'], + 'partitionType' => $partitionInfo['filesystem'], + 'repository' => $repository->getIp(), + 'name' => $image->getName().'_v'.$imageImageRepository->getVersion(), + ]; + + $this->createService->__invoke( + $image->getClient(), + CommandTypes::CREATE_IMAGE, + TraceStatus::IN_PROGRESS, + $jobId, + $inputData + ); + + return new JsonResponse(data: $image, status: Response::HTTP_OK); + } catch (Exception $e) { + $client->setStatus(ClientStatus::OG_LIVE); + $this->entityManager->persist($client); + $this->entityManager->flush(); + throw $e; + } + } catch (Exception $e) { + $this->logger->error('Error in monolithic image creation process', [ + 'repository' => $repository->getId(), + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + + return new JsonResponse( + data: ['error' => $e->getMessage()], + status: Response::HTTP_INTERNAL_SERVER_ERROR + ); } - - $jobId = $response['job_id']; - - $client->setStatus(ClientStatus::BUSY); - $this->entityManager->persist($client); - $this->entityManager->flush(); - - $inputData = [ - 'method' => 'CrearImagen', - 'type' => 'monolithic', - 'client' => $client->getUuid(), - 'image' => $image->getUuid(), - 'partitionCode' => $partitionInfo['partitionCode'], - 'partitionType' => $partitionInfo['filesystem'], - 'repository' => $repository->getIp(), - 'name' => $image->getName().'_v'.$imageImageRepository->getVersion(), - ]; - - $this->createService->__invoke($image->getClient(), CommandTypes::CREATE_IMAGE, TraceStatus::IN_PROGRESS, $jobId, $inputData); - - return new JsonResponse(data: $image, status: Response::HTTP_OK); } public function createGitImage( - GitImageRepository $gitImageRepository, - array $partitionInfo, Image $image, - ImageRepository $repository + array $partitionInfo, + ImageRepository $repository, + bool $queue = false, + ?string $gitRepositoryName = null ): JsonResponse { - if (!isset($partitonInfo)) { - try { - $this->createRepositoryAction->__invoke($image, $repository); - } catch (Exception $e) { - $this->logger->error('Error creating repository', ['repository' => $repository->getId(), 'error' => $e->getMessage()]); - - return new JsonResponse( - data: ['error' => $e->getMessage()], - status: Response::HTTP_INTERNAL_SERVER_ERROR - ); - } - } else { - try { - $this->createTagAction->__invoke($image, $repository, $gitImageRepository); - } catch (Exception $e) { - $this->logger->error('Error creating tag', ['repository' => $repository->getId(), 'error' => $e->getMessage()]); - - return new JsonResponse( - data: ['error' => $e->getMessage()], - status: Response::HTTP_INTERNAL_SERVER_ERROR - ); - } + if (!isset($partitionInfo['numDisk'], $partitionInfo['numPartition'], $partitionInfo['partitionCode'], $partitionInfo['filesystem'])) { + throw new BadRequestHttpException('Missing required partition information'); } - return new JsonResponse( - data: ['message' => 'Repository created successfully'], - status: Response::HTTP_OK - ); + $client = $image->getClient(); + if (!$client->getIp() || !$client->getToken()) { + throw new BadRequestHttpException('Client IP or token is missing'); + } - //TODO: llamar al endpoint del agente. + try { + $data = [ + 'dsk' => (string) $partitionInfo['numDisk'], + 'par' => (string) $partitionInfo['numPartition'], + 'cpt' => null, + 'idi' => $gitRepositoryName ?: $image->getUuid(), + 'nci' => $gitRepositoryName ?: $image->getName(), + 'ipr' => $repository->getIp(), + 'nfn' => 'CrearImagenGit', + 'tag' => '', + 'ids' => '0' + ]; + + $partitionTypes = PartitionTypes::getPartitionTypes(); + $partitionCode = $partitionInfo['partitionCode']; + $cptKey = array_search($partitionCode, array_column($partitionTypes, 'name'), true); + + if ($cptKey !== false) { + $keys = array_keys($partitionTypes); + $partitionTypeCode = $keys[$cptKey]; + $data['cpt'] = dechex($partitionTypeCode); + } else { + throw new BadRequestHttpException("Invalid partition code: {$partitionCode}"); + } + + //$this->sshKeyAction->__invoke($repository, $client); + + $response = $this->createRequest( + method: 'POST', + url: 'https://'.$client->getIp().':8000/opengnsys/CrearImagenGit', + params: [ + 'json' => $data, + ], + token: $client->getToken(), + ); + + if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) { + if ($queue) { + $inputData = [ + 'method' => 'CrearImagenGit', + 'type' => 'git', + 'client' => $client->getUuid(), + 'image' => $image->getUuid(), + 'partitionCode' => $partitionInfo['partitionCode'], + 'partitionType' => $partitionInfo['filesystem'], + 'repository' => $repository->getIp(), + 'name' => $image->getName(), + ]; + + $this->createService->__invoke($client, CommandTypes::CREATE_IMAGE_GIT, TraceStatus::PENDING, null, $inputData); + return new JsonResponse(data: [], status: Response::HTTP_OK); + } + + throw new BadRequestHttpException('Error creating image: ' . ($response['message'] ?? 'Unknown error')); + } + + if (!isset($response['job_id'])) { + throw new BadRequestHttpException('No job ID received from server'); + } + + $jobId = $response['job_id']; + + try { + $client->setStatus(ClientStatus::BUSY); + + $this->entityManager->persist($client); + $this->entityManager->flush(); + + $inputData = [ + 'method' => 'CrearImagenGit', + 'type' => 'git', + 'client' => $client->getUuid(), + 'image' => $image->getUuid(), + 'partitionCode' => $partitionInfo['partitionCode'], + 'partitionType' => $partitionInfo['filesystem'], + 'repository' => $repository->getIp(), + 'name' => $image->getName(), + ]; + + $this->createService->__invoke( + $image->getClient(), + CommandTypes::CREATE_IMAGE_GIT, + TraceStatus::IN_PROGRESS, + $jobId, + $inputData + ); + + return new JsonResponse(data: $image, status: Response::HTTP_OK); + } catch (Exception $e) { + $client->setStatus(ClientStatus::OG_LIVE); + $this->entityManager->persist($client); + $this->entityManager->flush(); + throw $e; + } + + } catch (Exception $e) { + $this->logger->error('Error in git image creation process', [ + 'repository' => $repository->getId(), + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + + return new JsonResponse( + data: ['error' => $e->getMessage()], + status: Response::HTTP_INTERNAL_SERVER_ERROR + ); + } } } diff --git a/src/Controller/OgAgent/DeployImageAction.php b/src/Controller/OgAgent/DeployImageAction.php index 6bebf9e..dbc4085 100644 --- a/src/Controller/OgAgent/DeployImageAction.php +++ b/src/Controller/OgAgent/DeployImageAction.php @@ -38,7 +38,7 @@ class DeployImageAction extends AbstractOgAgentController * @throws TransportExceptionInterface * @throws ServerExceptionInterface */ - public function __invoke(ImageImageRepository $imageImageRepository, DeployImageInput $input, Client $client, string $method) + public function __invoke(ImageImageRepository $imageImageRepository, DeployImageInput $input, Client $client, ?string $method = null) { $image = $imageImageRepository->getImage(); @@ -46,6 +46,10 @@ class DeployImageAction extends AbstractOgAgentController throw new BadRequestHttpException('IP is required'); } + if ($input->type === 'git') { + throw new BadRequestHttpException('Use DeployGitImageAction for Git images'); + } + $method = match ($input->method) { DeployMethodTypes::MULTICAST_UFTP_DIRECT, DeployMethodTypes::MULTICAST_UDPCAST_DIRECT, => 'multicast-direct', DeployMethodTypes::MULTICAST, DeployMethodTypes::MULTICAST_UFTP, DeployMethodTypes::MULTICAST_UDPCAST => 'multicast', @@ -83,16 +87,22 @@ class DeployImageAction extends AbstractOgAgentController 'ids' => '0' ]; + $url = 'https://'.$client->getIp().':8000/opengnsys/RestaurarImagen'; + $response = $this->createRequest( method: 'POST', - url: 'https://'.$client->getIp().':8000/opengnsys/RestaurarImagen', + url: $url, params: [ 'json' => $data, ], token: $client->getToken(), ); - $this->logger->info('Deploying image', [ 'image' => $imageImageRepository->getName(), 'repository' => $repository->getIp()]); + $this->logger->info('Deploying image', [ + 'image' => $imageImageRepository->getName(), + 'repository' => $repository->getIp(), + 'client' => $client->getIp() + ]); if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) { throw new BadRequestHttpException('Error deploying image'); diff --git a/src/Controller/OgAgent/Webhook/AgentSessionController.php b/src/Controller/OgAgent/Webhook/AgentSessionController.php index 6723ea8..8c3e72a 100644 --- a/src/Controller/OgAgent/Webhook/AgentSessionController.php +++ b/src/Controller/OgAgent/Webhook/AgentSessionController.php @@ -166,14 +166,16 @@ class AgentSessionController extends AbstractController return new JsonResponse(['message' => 'Client not found'], Response::HTTP_NOT_FOUND); } - switch ($data['ostype']) { - case 'Linux': + $status = $client->getStatus(); + + switch ($status) { + case ClientStatus::LINUX_SESSION: $client->setStatus(ClientStatus::LINUX); break; - case 'Windows': + case ClientStatus::WINDOWS_SESSION: $client->setStatus(ClientStatus::WINDOWS); break; - case 'MacOS': + case ClientStatus::MACOS_SESSION: $client->setStatus(ClientStatus::MACOS); break; @@ -187,7 +189,7 @@ class AgentSessionController extends AbstractController $this->logger->info('Client logged out', [ 'ip' => $data['ip'], 'user' => $data['user'], - 'ostype' => $data['ostype'], + 'status' => $status, ]); return new JsonResponse([], Response::HTTP_OK); diff --git a/src/Controller/OgAgent/Webhook/StatusController.php b/src/Controller/OgAgent/Webhook/StatusController.php index b4df0f1..75a998c 100644 --- a/src/Controller/OgAgent/Webhook/StatusController.php +++ b/src/Controller/OgAgent/Webhook/StatusController.php @@ -1,5 +1,7 @@ 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) { @@ -81,11 +84,37 @@ class StatusController extends AbstractController if (isset($data['progress'])){ $trace = $this->entityManager->getRepository(Trace::class)->findOneBy(['jobId' => $data['job_id']]); if ($trace){ - $trace->setProgress($data['progress'] * 100); + $trace->setProgress((int)($data['progress'] * 100)); $this->entityManager->persist($trace); $this->entityManager->flush(); } } + + if (isset($data['nfn']) && $data['nfn'] === self::CREATE_IMAGE_GIT) { + $trace = $this->entityManager->getRepository(Trace::class)->findOneBy(['jobId' => $data['job_id']]); + + if (!$trace) { + $this->logger->error('Trace not found', $data); + return new JsonResponse(['message' => 'Trace not found'], Response::HTTP_NOT_FOUND); + } + + if ($data['res'] === 1) { + $trace->setStatus(TraceStatus::SUCCESS); + $trace->setFinishedAt(new \DateTime()); + } else { + $trace->setStatus(TraceStatus::FAILED); + $trace->setFinishedAt(new \DateTime()); + $trace->setOutput($data['der']); + } + + $client = $trace->getClient(); + $client->setStatus(ClientStatus::OG_LIVE); + $this->entityManager->persist($client); + $this->entityManager->persist($trace); + $this->entityManager->flush(); + $this->logger->info('Git image creation completed.', ['job_id' => $data['job_id'], 'success' => $data['res'] === 1]); + } + if (isset($data['nfn']) && $data['nfn'] === self::CREATE_IMAGE) { $trace = $this->entityManager->getRepository(Trace::class)->findOneBy(['jobId' => $data['job_id']]); @@ -142,7 +171,7 @@ class StatusController extends AbstractController $this->logger->info('Image updated. Success.', ['image' => (string) $imageImageRepository->getUuid()]); } - if (isset($data['nfn']) && ($data['nfn'] === self::RESTORE_IMAGE || $data['nfn'] === self::CONFIGURE_IMAGE)) { + if (isset($data['nfn']) && ($data['nfn'] === self::RESTORE_IMAGE || $data['nfn'] === self::CONFIGURE_IMAGE || $data['nfn'] === self::RESTORE_IMAGE_GIT)) { $trace = $this->entityManager->getRepository(Trace::class)->findOneBy(['jobId' => $data['job_id']]); $client = $trace->getClient(); diff --git a/src/Controller/OgRepository/AbstractOgRepositoryController.php b/src/Controller/OgRepository/AbstractOgRepositoryController.php index eb61f33..80824e2 100644 --- a/src/Controller/OgRepository/AbstractOgRepositoryController.php +++ b/src/Controller/OgRepository/AbstractOgRepositoryController.php @@ -45,7 +45,7 @@ abstract class AbstractOgRepositoryController extends AbstractController 'accept' => 'application/json', 'Content-Type' => 'application/json' ], - 'timeout' => 10, + 'timeout' => 30, ]); try { @@ -56,7 +56,7 @@ abstract class AbstractOgRepositoryController extends AbstractController return [ 'code' => Response::HTTP_INTERNAL_SERVER_ERROR, - 'error' => 'Client/Server error', + 'error' => $e->getMessage(), 'details' => $e->getMessage(), ]; } catch (TransportExceptionInterface $e) { @@ -64,7 +64,7 @@ abstract class AbstractOgRepositoryController extends AbstractController return [ 'code' => Response::HTTP_INTERNAL_SERVER_ERROR, - 'error' => 'Transport error', + 'error' => $e->getMessage(), 'details' => $e->getMessage(), ]; } diff --git a/src/Dto/Input/DeployImageInput.php b/src/Dto/Input/DeployImageInput.php index 8f194cc..bb4fc9a 100644 --- a/src/Dto/Input/DeployImageInput.php +++ b/src/Dto/Input/DeployImageInput.php @@ -4,8 +4,6 @@ namespace App\Dto\Input; use ApiPlatform\Metadata\ApiProperty; use App\Dto\Output\ClientOutput; -use App\Dto\Output\ImageOutput; -use App\Dto\Output\PartitionOutput; use App\Validator\Constraints\ClientsHaveSamePartitionCount; use App\Validator\Constraints\OrganizationalUnitMulticastMode; use App\Validator\Constraints\OrganizationalUnitMulticastPort; @@ -17,49 +15,63 @@ class DeployImageInput { #[Groups(['image-image-repository:write'])] #[ApiProperty(description: 'The type of the image deployment', example: "")] - public ?string $type = null; + public ?string $type = 'monolithic'; #[Groups(['image-image-repository:write'])] - #[ApiProperty(description: 'The type of the image deployment', example: "")] + #[ApiProperty(description: 'The deployment method', example: "unicast")] public ?string $method = null; /** * @var ClientOutput[] */ #[Groups(['image-image-repository:write'])] - #[ApiProperty(description: 'The client to deploy the image')] + #[ApiProperty(description: 'The clients to deploy the image to')] public ?array $clients = []; #[Groups(['image-image-repository:write'])] + #[ApiProperty(description: 'The disk number to deploy to', example: 1)] public ?int $diskNumber = null; #[Groups(['image-image-repository:write'])] + #[ApiProperty(description: 'The partition number to deploy to', example: 1)] public ?int $partitionNumber = null; #[OrganizationalUnitP2PMode] #[Groups(['image-image-repository:write'])] + #[ApiProperty(description: 'P2P mode for torrent deployment', example: "seed")] public ?string $p2pMode = null; #[Groups(['image-image-repository:write'])] + #[ApiProperty(description: 'P2P time for torrent deployment', example: 3600)] public ?int $p2pTime = null; #[Groups(['image-image-repository:write'])] + #[ApiProperty(description: 'Multicast IP address', example: "239.255.255.250")] public ?string $mcastIp = null; #[Groups(['image-image-repository:write'])] + #[ApiProperty(description: 'Multicast speed in Mbps', example: 100)] public ?int $mcastSpeed = null; #[OrganizationalUnitMulticastPort] #[Groups(['image-image-repository:write'])] + #[ApiProperty(description: 'Multicast port', example: 8000)] public ?int $mcastPort = null; #[OrganizationalUnitMulticastMode] #[Groups(['image-image-repository:write'])] + #[ApiProperty(description: 'Multicast mode', example: "full")] public ?string $mcastMode = null; #[Groups(['image-image-repository:write'])] + #[ApiProperty(description: 'Maximum number of clients for multicast', example: 50)] public ?int $maxClients = null; #[Groups(['image-image-repository:write'])] + #[ApiProperty(description: 'Maximum time for deployment in seconds', example: 3600)] public ?int $maxTime = null; -} \ No newline at end of file + + #[Groups(['image-image-repository:write'])] + #[ApiProperty(description: 'Whether to queue the deployment', example: false)] + public bool $queue = false; +} diff --git a/src/Dto/Input/ImageInput.php b/src/Dto/Input/ImageInput.php index f2dd9de..f472a78 100644 --- a/src/Dto/Input/ImageInput.php +++ b/src/Dto/Input/ImageInput.php @@ -21,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; @@ -73,6 +72,14 @@ final class ImageInput #[ApiProperty(description: 'The global property of the image')] public ?bool $isGlobal = false; + #[Groups(['image:write'])] + #[ApiProperty(description: 'The queue property of the image')] + public bool $queue = false; + + #[Groups(['image:write'])] + #[ApiProperty(description: 'The name of the Git repository to use for this image', example: "mi-repositorio")] + public ?string $gitRepository = null; + public function __construct(?Image $image = null) { if (!$image) { diff --git a/src/Model/DeployMethodTypes.php b/src/Model/DeployMethodTypes.php index 4c48e55..ef22ab3 100644 --- a/src/Model/DeployMethodTypes.php +++ b/src/Model/DeployMethodTypes.php @@ -12,11 +12,13 @@ final class DeployMethodTypes public const string UNICAST = 'unicast'; public const string UNICAST_DIRECT = 'unicast-direct'; public const string TORRENT = 'p2p'; + public const string GIT = 'git'; private const array DEPLOYMENT_METHOD_TYPES = [ self::MULTICAST => 'Multicast', self::UNICAST => 'Unicast', self::TORRENT => 'Torrent', + self::GIT => 'Git', ]; public static function getDeploymentMethodTypes(): array