diff --git a/CHANGELOG.md b/CHANGELOG.md index 0258bd7..f634b22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,26 +1,38 @@ # Changelog +## [0.10.0] - 2025-03-25 +### Added +- Nuevo endpoint ogRepository. Convertir imagen en imagen virtual. +- Nuevo endpoint ogRepository. Importar imágenes externas al sistema. +- Nuevo método para desplegar imagenes sin cache. + +--- ## [0.9.5] - 2025-03-19 ### Added - Jenkinsfile updated to publish in repo +--- ## [0.9.4] - 2025-03-17 ### Fixed - Mercure service behind nginx server for containers, expose port in docker compose for nginx +--- ## [0.9.3] - 2025-03-17 ### Fixed - Mercure service behind nginx server for containers +--- ## [0.9.2] - 2025-03-12 ### Fixed - Added mercure service in docker compose file for deployments. +--- ## [0.9.1] - 2025-03-12 -### 🐛 Fixed +### Fixed - Corrección en la cancelacion de transmisiones p2p. +--- ## [0.9.0] - 2025-03-04 -### 🔹 Added +### Added - Nueva funcionalidad para tener notificaciones en tiempo real. Instalación de bundle "Mercure". - Creacion de EventListener en Symfony, para publicar mensajes en Mercure, cuando se realicen cambios en la base de datos ( cambio de estado en lo equipos, y trazas). - Nuevo endpoint "backup image". Integracion con ogRepository. @@ -30,24 +42,24 @@ - Nueva funcionalidad para cancelar despliegues de imagenes. - Añadido nuevo campo "cancelado" en trazas. -### ⚡ Changed +### Changed - Cambios en logs. Cambios en salida (stderror -> file.log) - Modulo DHCP. Añadir equipos, ahora se gestiona con una unica llamada a la API. - Acciones masivas en equipos. Se ha cambiado la respuesta para que no fallen las peticiones si uno o mas equipos no da respuesta. --- ## [0.8.1] - 2025-02-25 -### 🐛 Fixed +### Fixed - Corrección de bug en el deploy de imágenes --- ## [0.8.0] - 2025-01-10 -### 🔹 Added +### Added - Nuevos campos en "aulas" para la jerarquia en clientes. - Nueva funcionalidad "imagen global". Integracion con ogRepository. -### ⚡ Changed +### Changed - Limpieza en campos "name" y "date" de ogLive. Es necesario parsear el campo "filename" para facilitar el uso al usuario en la web. ### 🐛 Fixed - Corrección de bug que impedia borrar un cliente si tenia una traza enlazada. @@ -55,7 +67,7 @@ ## [0.7.3] - 2025-01-03 -### 🔹 Added +### Added - Adaptados cambios en los endpoints para multiseleccion de clientes. - Se agregó la funcionalidad de importar/exportar. Integración con ogRepository. - Se agregó la funcionalidad de borrar imágenes. Integración con ogRepository. @@ -70,4 +82,5 @@ - **Added**: Secciones con nuevas características. - **Fixed**: Corrección de errores y bugs. - **Changed**: Modificaciones o mejoras en funcionalidades existentes. +- **Improved**: Mejoras en funcionalidades existentes. - **Removed**: Funcionalidades o dependencias eliminadas. diff --git a/config/api_platform/ImageImageRepository.yaml b/config/api_platform/ImageImageRepository.yaml index 05fd121..279d72a 100644 --- a/config/api_platform/ImageImageRepository.yaml +++ b/config/api_platform/ImageImageRepository.yaml @@ -56,6 +56,14 @@ resources: uriTemplate: /image-image-repositories/{uuid}/backup-image controller: App\Controller\OgRepository\Image\BackupImageAction + convert_image_to_virtual_image_ogrepository: + shortName: OgRepository Server + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\ConvertImageToVirtualInput + uriTemplate: /image-image-repositories/{uuid}/convert-image-to-virtual + controller: App\Controller\OgRepository\Image\ConvertImageToVirtualAction + trash_delete_image_ogrepository: shortName: OgRepository Server description: Delete Image in OgRepository @@ -88,7 +96,7 @@ resources: description: Export Image in OgRepository class: ApiPlatform\Metadata\Post method: POST - input: App\Dto\Input\ExportImportImageRepositoryInput + input: App\Dto\Input\TransferGlobalImageInput uriTemplate: /image-image-repositories/{uuid}/transfer-image controller: App\Controller\OgRepository\Image\TransferAction @@ -101,6 +109,15 @@ resources: uriTemplate: /image-image-repositories/server/{uuid}/status controller: App\Controller\OgRepository\Image\GetStatusAction + transfer_global_image_repository: + shortName: OgRepository Server + description: Transfer Global Image in OgRepository + class: ApiPlatform\Metadata\Post + method: POST + input: false + uriTemplate: /image-image-repositories/server/{uuid}/transfer-global + controller: App\Controller\OgRepository\Image\TransferGlobalAction + properties: App\Entity\ImageImageRepository: id: diff --git a/config/api_platform/ImageRepository.yaml b/config/api_platform/ImageRepository.yaml index e57d0f9..97cd0b9 100644 --- a/config/api_platform/ImageRepository.yaml +++ b/config/api_platform/ImageRepository.yaml @@ -57,14 +57,23 @@ resources: uriTemplate: /image-repositories/server/{uuid}/status controller: App\Controller\OgRepository\StatusAction - export_image_ogrepository: + import_image_ogrepository: shortName: OgRepository Server description: Export Image in OgRepository class: ApiPlatform\Metadata\Post method: POST - input: App\Dto\Input\ExportImportImageRepositoryInput - uriTemplate: /image-repositories/{uuid}/export-image - controller: App\Controller\OgRepository\Image\ExportAction + input: App\Dto\Input\ImportImageRepositoryInput + uriTemplate: /image-repositories/{uuid}/import-image + controller: App\Controller\OgRepository\Image\ImportAction + + convert_image_ogrepository: + shortName: OgRepository Server + description: Convert Image in OgRepository + class: ApiPlatform\Metadata\Post + method: POST + input: App\Dto\Input\ConvertImageRepositoryInput + uriTemplate: /image-repositories/{uuid}/convert-image + controller: App\Controller\OgRepository\Image\ConvertAction properties: App\Entity\ImageRepository: diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 5b303be..b6cfd9e 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -12,6 +12,7 @@ framework: handler_id: null cookie_secure: auto cookie_samesite: lax + storage_factory_id: session.storage.factory.native #esi: true #fragments: true diff --git a/config/packages/web_profiler.yaml b/config/packages/web_profiler.yaml index b946111..948758c 100644 --- a/config/packages/web_profiler.yaml +++ b/config/packages/web_profiler.yaml @@ -15,3 +15,11 @@ when@test: framework: profiler: { collect: false } + +when@prod: + web_profiler: + toolbar: false + intercept_redirects: false + + framework: + profiler: { collect: false } \ No newline at end of file diff --git a/config/services/api_platform.yaml b/config/services/api_platform.yaml index 8ec0610..eabd61c 100644 --- a/config/services/api_platform.yaml +++ b/config/services/api_platform.yaml @@ -82,7 +82,7 @@ services: api_platform.filter.og_live.search: parent: 'api_platform.doctrine.orm.search_filter' - arguments: [ { 'id': 'exact', 'name': 'partial', } ] + arguments: [ { 'id': 'exact', 'name': 'partial', 'status': 'exact' } ] tags: [ 'api_platform.filter' ] api_platform.filter.og_live.boolean: diff --git a/src/Controller/OgAgent/Webhook/OgAgentController.php b/src/Controller/OgAgent/Webhook/AgentSessionController.php similarity index 98% rename from src/Controller/OgAgent/Webhook/OgAgentController.php rename to src/Controller/OgAgent/Webhook/AgentSessionController.php index dfa8b04..67d6a6e 100644 --- a/src/Controller/OgAgent/Webhook/OgAgentController.php +++ b/src/Controller/OgAgent/Webhook/AgentSessionController.php @@ -15,7 +15,7 @@ use Symfony\Component\HttpKernel\Attribute\AsController; use Symfony\Component\Routing\Annotation\Route; #[AsController] -class OgAgentController extends AbstractController +class AgentSessionController extends AbstractController { public function __construct( protected readonly EntityManagerInterface $entityManager diff --git a/src/Controller/OgBoot/AbstractOgBootController.php b/src/Controller/OgBoot/AbstractOgBootController.php index d39cfd7..c85e0fd 100644 --- a/src/Controller/OgBoot/AbstractOgBootController.php +++ b/src/Controller/OgBoot/AbstractOgBootController.php @@ -7,7 +7,7 @@ namespace App\Controller\OgBoot; use App\Controller\OgBoot\PxeBootFile\PostAction; use App\Service\Trace\CreateService; use App\Service\Utils\ExtractOgLiveFilenameDateService; -use App\Service\Utils\SymflipyOgLiveFilenameService; +use App\Service\Utils\SimplifyOgLiveFilenameService; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\DependencyInjection\Attribute\Autowire; @@ -34,7 +34,7 @@ abstract class AbstractOgBootController extends AbstractController protected readonly EntityManagerInterface $entityManager, protected readonly HttpClientInterface $httpClient, protected readonly CreateService $createService, - protected readonly SymflipyOgLiveFilenameService $symflipyOgLiveFilenameService, + protected readonly SimplifyOgLiveFilenameService $simplifyOgLiveFilenameService, protected readonly ExtractOgLiveFilenameDateService $extractOgLiveFilenameDateService, ) { diff --git a/src/Controller/OgBoot/OgLive/GetIsosAction.php b/src/Controller/OgBoot/OgLive/GetIsosAction.php index d284859..f451d88 100644 --- a/src/Controller/OgBoot/OgLive/GetIsosAction.php +++ b/src/Controller/OgBoot/OgLive/GetIsosAction.php @@ -30,7 +30,7 @@ class GetIsosAction extends AbstractOgBootController } $isos = array_map(function ($iso) { - $filename = $this->symflipyOgLiveFilenameService->__invoke($iso['filename']); + $filename = $this->simplifyOgLiveFilenameService->__invoke($iso['filename']); return [ 'id' => $iso['id'], diff --git a/src/Controller/OgBoot/OgLive/InstallAction.php b/src/Controller/OgBoot/OgLive/InstallAction.php index bbc1e06..f736f13 100644 --- a/src/Controller/OgBoot/OgLive/InstallAction.php +++ b/src/Controller/OgBoot/OgLive/InstallAction.php @@ -49,7 +49,7 @@ class InstallAction extends AbstractOgBootController $this->createService->__invoke(null, CommandTypes::INSTALL_OGLIVE, TraceStatus::IN_PROGRESS, 'InstallOgLive_'.$data->getUuid(), $inputData); - $data->setName($this->symflipyOgLiveFilenameService->__invoke($data->getFilename())); + $data->setName($this->simplifyOgLiveFilenameService->__invoke($data->getFilename())); $data->setDate(new \DateTime()); $data->setStatus(OgLiveStatus::PENDING); $this->entityManager->persist($data); diff --git a/src/Controller/OgBoot/OgLive/SyncAction.php b/src/Controller/OgBoot/OgLive/SyncAction.php index 286f1b8..69cabd0 100644 --- a/src/Controller/OgBoot/OgLive/SyncAction.php +++ b/src/Controller/OgBoot/OgLive/SyncAction.php @@ -20,16 +20,21 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; class SyncAction extends AbstractOgBootController { const string OG_BOOT_DIRECTORY = '/opt/opengnsys/ogboot/tftpboot//'; + /** * @throws TransportExceptionInterface * @throws ServerExceptionInterface * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface + * @throws \Exception */ public function __invoke(): JsonResponse { $content = $this->createRequest('GET', 'http://'.$this->ogBootApiUrl . '/ogboot/v1/oglives'); + $allOgLives = $this->entityManager->getRepository(OgLive::class)->findAll(); + $apiChecksums = array_map(fn($ogLive) => $ogLive['id'], $content['message']['installed_ogLives']); + foreach ($content['message']['installed_ogLives'] as $ogLive) { $ogLiveEntity = $this->entityManager->getRepository(OgLive::class)->findOneBy(['checksum' => $ogLive['id']]); @@ -43,6 +48,17 @@ class SyncAction extends AbstractOgBootController $this->extracted($ogLiveEntity, $ogLive); $this->entityManager->persist($ogLiveEntity); } + + foreach ($allOgLives as $localOgLive) { + if ($localOgLive->getStatus() === OgLiveStatus::PENDING ) { + continue; + } + + if (!in_array($localOgLive->getChecksum(), $apiChecksums)) { + $this->entityManager->remove($localOgLive); + } + } + $this->entityManager->flush(); if (isset($content['message']['default_oglive'])) { @@ -60,7 +76,7 @@ class SyncAction extends AbstractOgBootController */ private function extracted(OgLive $ogLiveEntity, mixed $ogLive): void { - $ogLiveEntity->setName($this->symflipyOgLiveFilenameService->__invoke(str_replace(self::OG_BOOT_DIRECTORY, '', $ogLive['directory']))); + $ogLiveEntity->setName($this->simplifyOgLiveFilenameService->__invoke(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']); @@ -75,6 +91,13 @@ class SyncAction extends AbstractOgBootController private function serDefaultOgLive(string $defaultOgLive): void { + $oldDefaultOgLive = $this->entityManager->getRepository(OgLive::class)->findAll(); + + foreach ($oldDefaultOgLive as $oldOgLive) { + $oldOgLive->setIsDefault(false); + $this->entityManager->persist($oldOgLive); + } + $ogLiveEntity = $this->entityManager->getRepository(OgLive::class)->findOneBy(['filename' => $defaultOgLive]); if (!$ogLiveEntity) { diff --git a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php index 5a12077..645fe8f 100644 --- a/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php +++ b/src/Controller/OgBoot/OgLive/Webhook/InstallOgLiveResponseAction.php @@ -6,7 +6,9 @@ use App\Controller\OgBoot\AbstractOgBootController; use App\Entity\OgLive; use App\Entity\Trace; use App\Model\OgLiveStatus; +use App\Model\TraceStatus; use App\Service\Utils\ExtractOgLiveFilenameDateService; +use App\Service\Utils\SimplifyOgLiveFilenameService; use App\Service\Utils\SymflipyOgLiveFilenameService; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; @@ -33,7 +35,7 @@ class InstallOgLiveResponseAction extends AbstractController public function __construct( protected readonly EntityManagerInterface $entityManager, protected readonly LoggerInterface $logger, - protected readonly SymflipyOgLiveFilenameService $symflipyOgLiveFilenameService, + protected readonly SimplifyOgLiveFilenameService $simplifyOgLiveFilenameService, protected readonly ExtractOgLiveFilenameDateService $extractOgLiveFilenameDateService, ) { @@ -51,6 +53,8 @@ class InstallOgLiveResponseAction extends AbstractController return new JsonResponse(['error' => 'Invalid JSON data'], Response::HTTP_BAD_REQUEST); } + $this->logger->info('OgLive Webhook data received: '.json_encode($data)); + $data = $data['webhookData']; if ($data === null || !isset($data['message'], $data['ogCoreId'], $data['status'])) { @@ -69,8 +73,9 @@ class InstallOgLiveResponseAction extends AbstractController } if ($trace) { - $trace->setStatus($status === self::OG_LIVE_INSTALL_SUCCESS ? 'success' : 'failure'); + $trace->setStatus($status === self::OG_LIVE_INSTALL_SUCCESS ? TraceStatus::SUCCESS : TraceStatus::FAILED); $this->entityManager->persist($trace); + $this->entityManager->flush(); } if ($ogLive->getStatus() === OgLiveStatus::ACTIVE) { @@ -86,12 +91,12 @@ class InstallOgLiveResponseAction extends AbstractController /** * @throws \Exception */ - private function updateOgLive (OgLive $ogLive, array $details, string $status): void + private function updateOgLive (OgLive $ogLive, mixed $details, string $status): void { - if ($status === self::OG_LIVE_INSTALL_SUCCESS) { - $ogLive->setName($this->symflipyOgLiveFilenameService->__invoke($details['filename'])); - $ogLive->setDate(new \DateTime($this->extractOgLiveFilenameDateService->__invoke($details['filename']))); - $ogLive->setFilename(str_replace(self::OG_BOOT_DIRECTORY, '', $ogLive['directory'])); + if ( is_array($details) && $status === self::OG_LIVE_INSTALL_SUCCESS) { + $ogLive->setName($this->simplifyOgLiveFilenameService->__invoke($details['directory'])); + $ogLive->setDate(new \DateTime($this->extractOgLiveFilenameDateService->__invoke($details['directory']))); + $ogLive->setFilename(str_replace(self::OG_BOOT_DIRECTORY, '', $details['directory'])); $ogLive->setInstalled(true); $ogLive->setSynchronized(true); $ogLive->setChecksum($details['id']); @@ -100,21 +105,20 @@ class InstallOgLiveResponseAction extends AbstractController $ogLive->setArchitecture($details['architecture']); $ogLive->setRevision($details['revision']); $ogLive->setDirectory($details['directory']); + + $oldDefaultOgLive = $this->entityManager->getRepository(OgLive::class)->findAll(); + foreach ($oldDefaultOgLive as $oldOgLive) { + $oldOgLive->setIsDefault(false); + $this->entityManager->persist($oldOgLive); + } + + $ogLive->setIsDefault(true); + $this->entityManager->persist($ogLive); } $ogLive->setStatus($status === self::OG_LIVE_INSTALL_SUCCESS ? OgLiveStatus::ACTIVE : OgLiveStatus::FAILED); $ogLive->setInstalled($status === self::OG_LIVE_INSTALL_SUCCESS); - $oldDefaultOgLive = $this->entityManager->getRepository(OgLive::class)->findBy(['isDefault' => true]); - - foreach ($oldDefaultOgLive as $oldOgLive) { - $oldOgLive->setIsDefault(false); - $this->entityManager->persist($oldOgLive); - } - - $ogLive->setIsDefault(true); - $this->entityManager->persist($ogLive); - $this->entityManager->flush(); } } \ No newline at end of file diff --git a/src/Controller/OgRepository/Image/BackupImageAction.php b/src/Controller/OgRepository/Image/BackupImageAction.php index 549a46b..8f8662d 100644 --- a/src/Controller/OgRepository/Image/BackupImageAction.php +++ b/src/Controller/OgRepository/Image/BackupImageAction.php @@ -55,7 +55,7 @@ class BackupImageAction extends AbstractOgRepositoryController $inputData = [ 'imageName' => $image->getName(), 'repositoryUuid' => $repository->getUuid(), - 'imageUuid' => $imageImageRepository->getUuid(), + 'imageImageRepositoryUuid' => $imageImageRepository->getUuid(), 'ID_img' => $imageImageRepository->getImageFullsum(), 'repo_ip' => $input->repoIp, 'remote_path' => $input->remotePath diff --git a/src/Controller/OgRepository/Image/CancelTransmissionAction.php b/src/Controller/OgRepository/Image/CancelTransmissionAction.php index 9d79b51..c7b84a8 100644 --- a/src/Controller/OgRepository/Image/CancelTransmissionAction.php +++ b/src/Controller/OgRepository/Image/CancelTransmissionAction.php @@ -59,7 +59,7 @@ class CancelTransmissionAction extends AbstractOgRepositoryController $content = $this->createRequest('DELETE', 'http://'.$image->getRepository()->getIp().':8006/ogrepository/v1/'.$method.'/images/'.$image->getImageFullsum()); } - if (isset($content['error']) && $content['error'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { throw new ValidatorException('Error cancelling transmission'); } diff --git a/src/Controller/OgRepository/Image/ConvertAction.php b/src/Controller/OgRepository/Image/ConvertAction.php new file mode 100644 index 0000000..a41589a --- /dev/null +++ b/src/Controller/OgRepository/Image/ConvertAction.php @@ -0,0 +1,87 @@ +filesystem; + $image = pathinfo($input->name, PATHINFO_FILENAME); + + $imageEntity = $this->entityManager->getRepository(Image::class)->findOneBy(['name' => $image]); + + if (!$imageEntity){ + $imageEntity = new Image(); + $imageEntity->setName($image); + $imageEntity->setRemotePc(false); + $imageEntity->setIsGlobal(false); + + $this->entityManager->persist($imageEntity); + } + + $imageImageRepositoryEntity = $this->entityManager->getRepository(ImageImageRepository::class)->findOneBy(['image' => $imageEntity, 'repository' => $repository]); + + if ($imageImageRepositoryEntity){ + throw new ValidatorException('This image already exists in this repository'); + } + + $imageImageRepositoryEntity = new ImageImageRepository(); + $imageImageRepositoryEntity->setStatus(ImageStatus::PENDING); + $imageImageRepositoryEntity->setImage($imageEntity); + $imageImageRepositoryEntity->setRepository($repository); + + $this->entityManager->persist($imageImageRepositoryEntity); + $this->entityManager->flush(); + + $this->logger->info('Converting image', ['image' => $image]); + + $params = [ + 'json' => [ + 'filesystem' => $fileSystem, + 'virtual_image' => $input->name + ] + ]; + + $content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/images/virtual', $params); + + if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('Error converting image'); + } + + $inputData = [ + 'imageName' => $image, + 'imageImageRepositoryUuid' => $imageImageRepositoryEntity->getUuid(), + ]; + + $this->createService->__invoke(null, CommandTypes::CONVERT_IMAGE, TraceStatus::IN_PROGRESS, $content['job_id'], $inputData); + + return new JsonResponse(data: [], status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgRepository/Image/ConvertImageToVirtualAction.php b/src/Controller/OgRepository/Image/ConvertImageToVirtualAction.php new file mode 100644 index 0000000..cf3545c --- /dev/null +++ b/src/Controller/OgRepository/Image/ConvertImageToVirtualAction.php @@ -0,0 +1,66 @@ +getImage(); + + if (!$image->getName()) { + throw new ValidatorException('Name is required'); + } + + $params = [ + 'json' => [ + 'ID_img' => $imageImageRepository->getImageFullsum(), + 'vm_extension' => $input->extension, + ] + ]; + + $this->logger->info('Convert image to virtual', ['image' => $image->getName()]); + + $repository = $imageImageRepository->getRepository(); + + $content = $this->createRequest('PUT', 'http://'.$repository->getIp().':8006/ogrepository/v1/images/virtual', $params); + + $inputData = [ + 'imageName' => $image->getName(), + 'repositoryUuid' => $repository->getUuid(), + 'imageImageRepositoryUuid' => $imageImageRepository->getUuid(), + 'ID_img' => $imageImageRepository->getImageFullsum(), + ]; + + $this->createService->__invoke($image->getClient(), CommandTypes::CONVERT_IMAGE_TO_VIRTUAL, TraceStatus::IN_PROGRESS, $content['job_id'], $inputData); + + $imageImageRepository->setStatus(ImageStatus::TRANSFERRING); + $this->entityManager->persist($imageImageRepository); + $this->entityManager->flush(); + + return new JsonResponse(data: $content, status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgRepository/Image/CreateAuxFilesAction.php b/src/Controller/OgRepository/Image/CreateAuxFilesAction.php index 06c09dd..d5ebd1f 100644 --- a/src/Controller/OgRepository/Image/CreateAuxFilesAction.php +++ b/src/Controller/OgRepository/Image/CreateAuxFilesAction.php @@ -51,7 +51,7 @@ class CreateAuxFilesAction extends AbstractOgRepositoryController $inputData = [ 'imageName' => $image->getName(), - 'imageUuid' => $data->getUuid(), + 'imageImageRepositoryUuid' => $data->getUuid(), ]; $this->createService->__invoke($image->getClient(), CommandTypes::CREATE_IMAGE_AUX_FILE, TraceStatus::IN_PROGRESS, $content['job_id'], $inputData); diff --git a/src/Controller/OgRepository/Image/ExportAction.php b/src/Controller/OgRepository/Image/ExportAction.php deleted file mode 100644 index ef449e3..0000000 --- a/src/Controller/OgRepository/Image/ExportAction.php +++ /dev/null @@ -1,69 +0,0 @@ -images; - - foreach ($images as $imageEntity) { - /** @var Image $image */ - $image = $imageEntity->getEntity(); - - if (!$image->getImageFullsum()) { - throw new ValidatorException('Fullsum is required'); - } - - $params = [ - 'json' => [ - 'ID_img' => $image->getImageFullsum(), - 'repo_ip' => $repository->getIp(), - 'user' => 'opengnsys', - ] - ]; - - $this->logger->info('Exporting image', ['image' => $image->getName(), 'repository' => $repository->getIp()]); - - $content = $this->createRequest('PUT', 'http://'.$image->getRepository()->getIp().':8006/ogrepository/v1/repo/images', $params); - - $inputData = [ - 'imageName' => $image->getName(), - 'imageUuid' => $image->getUuid(), - 'repositoryUuid' => $repository->getUuid(), - ]; - - $this->createService->__invoke($image->getClient(), CommandTypes::EXPORT_IMAGE, TraceStatus::IN_PROGRESS, $content['job_id'], $inputData); - - $image->setStatus(ImageStatus::TRANSFERING); - $this->entityManager->persist($image); - $this->entityManager->flush(); - } - - return new JsonResponse(data: [], status: Response::HTTP_OK); - } -} \ No newline at end of file diff --git a/src/Controller/OgRepository/Image/ImportAction.php b/src/Controller/OgRepository/Image/ImportAction.php new file mode 100644 index 0000000..cb28a67 --- /dev/null +++ b/src/Controller/OgRepository/Image/ImportAction.php @@ -0,0 +1,84 @@ +name; + + $imageEntity = $this->entityManager->getRepository(Image::class)->findOneBy(['name' => $image]); + + if (!$imageEntity){ + $imageEntity = new Image(); + $imageEntity->setName($image); + $imageEntity->setRemotePc(false); + $imageEntity->setIsGlobal(false); + + $this->entityManager->persist($imageEntity); + } + + $imageImageRepositoryEntity = $this->entityManager->getRepository(ImageImageRepository::class)->findOneBy(['image' => $imageEntity, 'repository' => $repository]); + + if ($imageImageRepositoryEntity){ + throw new ValidatorException('This image already exists in this repository'); + } + + $imageImageRepositoryEntity = new ImageImageRepository(); + $imageImageRepositoryEntity->setStatus(ImageStatus::AUX_FILES_PENDING); + $imageImageRepositoryEntity->setImage($imageEntity); + $imageImageRepositoryEntity->setRepository($repository); + + $this->entityManager->persist($imageImageRepositoryEntity); + $this->entityManager->flush(); + + $this->logger->info('Creating aux files', ['image' => $image]); + + $params = [ + 'json' => [ + 'image' => $image.'.img' + ] + ]; + + $content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/images/torrentsum', $params); + + if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { + throw new ValidatorException('Error importing image'); + } + + $inputData = [ + 'imageName' => $image, + 'imageUuid' => $imageImageRepositoryEntity->getUuid(), + ]; + + $this->createService->__invoke(null, CommandTypes::CREATE_IMAGE_AUX_FILE, TraceStatus::IN_PROGRESS, $content['job_id'], $inputData); + + return new JsonResponse(data: [], status: Response::HTTP_OK); + } +} \ No newline at end of file diff --git a/src/Controller/OgRepository/Image/TransferAction.php b/src/Controller/OgRepository/Image/TransferAction.php index a0eadac..c0513bb 100644 --- a/src/Controller/OgRepository/Image/TransferAction.php +++ b/src/Controller/OgRepository/Image/TransferAction.php @@ -4,6 +4,7 @@ namespace App\Controller\OgRepository\Image; use App\Controller\OgRepository\AbstractOgRepositoryController; use App\Dto\Input\ExportImportImageRepositoryInput; +use App\Dto\Input\TransferGlobalImageInput; use App\Entity\Image; use App\Entity\ImageImageRepository; use App\Entity\ImageRepository; @@ -28,7 +29,7 @@ class TransferAction extends AbstractOgRepositoryController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(ExportImportImageRepositoryInput $input, ImageImageRepository $imageImageRepository): JsonResponse + public function __invoke(TransferGlobalImageInput $input, ImageImageRepository $imageImageRepository): JsonResponse { $repositories = $input->repositories; @@ -45,7 +46,7 @@ class TransferAction extends AbstractOgRepositoryController $params = [ 'json' => [ 'image' => $image->getName().'.img', - 'repo_ip' => $image->getClient()->getRepository()->getIp(), + 'repo_ip' => $imageImageRepository->getRepository()->getIp(), 'user' => 'opengnsys', ] ]; diff --git a/src/Controller/OgRepository/Image/TransferIsGlobalAction.php b/src/Controller/OgRepository/Image/TransferGlobalAction.php similarity index 68% rename from src/Controller/OgRepository/Image/TransferIsGlobalAction.php rename to src/Controller/OgRepository/Image/TransferGlobalAction.php index d1d6c7f..c69ff48 100644 --- a/src/Controller/OgRepository/Image/TransferIsGlobalAction.php +++ b/src/Controller/OgRepository/Image/TransferGlobalAction.php @@ -21,7 +21,7 @@ use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; #[AsController] -class TransferIsGlobalAction extends AbstractOgRepositoryController +class TransferGlobalAction extends AbstractOgRepositoryController { /** * @throws TransportExceptionInterface @@ -29,26 +29,25 @@ class TransferIsGlobalAction extends AbstractOgRepositoryController * @throws RedirectionExceptionInterface * @throws ClientExceptionInterface */ - public function __invoke(?array $repositories = [], Image $image): JsonResponse + public function __invoke(ImageImageRepository $imageImageRepository): JsonResponse { + $repositories = $this->entityManager->getRepository(ImageRepository::class)->findAll(); + foreach ($repositories as $repository) { - try { - $imageImageRepository = $this->entityManager->getRepository(ImageImageRepository::class)->findOneBy(['image' => $image, 'repository' => $repository]); - if ($imageImageRepository) { - $content = $this->createRequest('GET', 'http://'.$repository->getIp().':8006/ogrepository/v1/images/'.$imageImageRepository->getImageFullsum()); - - $this->logger->info('Image already exists', ['image' => $imageImageRepository->getImage()->getName(), 'repository' => $repository->getIp()]); - continue; - } - } catch ( \Exception $e) { + $hasImage = $this->entityManager->getRepository(ImageImageRepository::class) + ->findOneBy(['image' => $imageImageRepository->getImage(), 'repository' => $repository]); + if ($hasImage) { + continue; } + $image = $imageImageRepository->getImage(); + $params = [ 'json' => [ 'image' => $image->getName().'.img', - 'repo_ip' => $image->getClient()->getRepository()->getIp(), + 'repo_ip' => $imageImageRepository->getRepository()->getIp(), 'user' => 'opengnsys', ] ]; @@ -64,14 +63,18 @@ class TransferIsGlobalAction extends AbstractOgRepositoryController $inputData = [ 'imageName' => $image->getName(), 'imageUuid' => $image->getUuid(), - //'imageImageRepositoryUuid' => $imageImageRepository?->getUuid(), + 'imageImageRepositoryUuid' => $imageImageRepository?->getUuid(), 'repositoryUuid' => $repository->getUuid(), ]; $this->createService->__invoke($image->getClient(), CommandTypes::TRANSFER_IMAGE, TraceStatus::IN_PROGRESS, $content['job_id'], $inputData); - //$imageImageRepository->setStatus(ImageStatus::TRANSFERRING); + $image->setIsGlobal(true); $this->entityManager->persist($image); + + $imageImageRepository->setStatus(ImageStatus::TRANSFERRING); + $this->entityManager->persist($imageImageRepository); + $this->entityManager->flush(); } diff --git a/src/Controller/OgRepository/SyncAction.php b/src/Controller/OgRepository/SyncAction.php index 10ff6b9..f4fd765 100644 --- a/src/Controller/OgRepository/SyncAction.php +++ b/src/Controller/OgRepository/SyncAction.php @@ -25,14 +25,24 @@ class SyncAction extends AbstractOgRepositoryController */ public function __invoke(ImageRepository $input): JsonResponse { - $content = $this->createRequest('GET', 'http://'.$input->getIp(). ':8006/ogrepository/v1/images'); + $content = $this->createRequest('GET', 'http://' . $input->getIp() . ':8006/ogrepository/v1/images'); if (!isset($content['output']['REPOSITORY']['images'])) { return new JsonResponse(data: 'No images found', status: Response::HTTP_NOT_FOUND); } + $repository = $this->entityManager->getRepository(ImageImageRepository::class); + + $existingImages = $repository->findBy(['repository' => $input]); + + $newImageFullsums = array_column($content['output']['REPOSITORY']['images'], 'fullsum'); + foreach ($content['output']['REPOSITORY']['images'] as $image) { - $imageImageRepositoryEntity = $this->entityManager->getRepository(ImageImageRepository::class)->findOneBy(['imageFullsum' => $image['fullsum'], 'repository' => $input]); + $imageImageRepositoryEntity = $repository->findOneBy([ + 'imageFullsum' => $image['fullsum'], + 'repository' => $input + ]); + $imageEntity = $this->entityManager->getRepository(Image::class)->findOneBy(['name' => $image['name']]); if (!$imageEntity) { @@ -53,10 +63,17 @@ class SyncAction extends AbstractOgRepositoryController $this->entityManager->persist($imageImageRepositoryEntity); } - } + + foreach ($existingImages as $existingImage) { + if (!in_array($existingImage->getImageFullsum(), $newImageFullsums)) { + $this->entityManager->remove($existingImage); + } + } + $this->entityManager->flush(); return new JsonResponse(data: $content, status: Response::HTTP_OK); } + } \ No newline at end of file diff --git a/src/Controller/OgRepository/Webhook/ResponseController.php b/src/Controller/OgRepository/Webhook/ResponseController.php index ae66f39..b4900f7 100644 --- a/src/Controller/OgRepository/Webhook/ResponseController.php +++ b/src/Controller/OgRepository/Webhook/ResponseController.php @@ -37,6 +37,8 @@ class ResponseController extends AbstractOgRepositoryController str_starts_with($action, "CreateAuxiliarFiles_") => $this->handleImageRepositoryAction($data, true), str_starts_with($action, "TransferImage_"), str_starts_with($action, "ExportImage_") => $this->processImageAction($data), str_starts_with($action, "BackupImage_") => $this->handleImageRepositoryAction($data), + str_starts_with($action, "ConvertImageToVirtual") => $this->handleImageRepositoryAction($data), + str_starts_with($action, "ConvertImageFromVirtual") => $this->handleImageRepositoryAction($data), default => $this->jsonResponseError('Invalid action', Response::HTTP_BAD_REQUEST), }; } @@ -65,15 +67,21 @@ class ResponseController extends AbstractOgRepositoryController $trace = $this->getTrace($data['job_id']); if (!$trace) return $this->jsonResponseError('Trace not found'); + $image = $this->getImage($trace); + $repository = $this->getRepository($trace); + $originImageImageRepository = $this->getImageImageRepository($trace); + if (!$image) return $this->jsonResponseError('Image not found', Response::HTTP_NOT_FOUND, $trace); + if (!$repository) return $this->jsonResponseError('Repository not found', Response::HTTP_NOT_FOUND, $trace); + if (!$originImageImageRepository) return $this->jsonResponseError('ImageImageRepository not found', Response::HTTP_NOT_FOUND, $trace); + + $originImageImageRepository->setStatus(ImageStatus::SUCCESS); + $this->entityManager->persist($originImageImageRepository); + $this->entityManager->flush(); + if ($data['success'] !== true) { return $this->jsonResponseError('Action failed', Response::HTTP_BAD_REQUEST, $trace); } - $image = $this->getImage($trace); - $repository = $this->getRepository($trace); - if (!$image) return $this->jsonResponseError('Image not found', Response::HTTP_NOT_FOUND, $trace); - if (!$repository) return $this->jsonResponseError('Repository not found', Response::HTTP_NOT_FOUND, $trace); - $newImageRepo = new ImageImageRepository(); $newImageRepo->setImage($image); $newImageRepo->setRepository($repository); @@ -127,7 +135,7 @@ class ResponseController extends AbstractOgRepositoryController private function getImageImageRepository(Trace $trace): ?ImageImageRepository { return $this->entityManager->getRepository(ImageImageRepository::class) - ->findOneBy(['uuid' => $trace->getInput()['imageUuid']]); + ->findOneBy(['uuid' => $trace->getInput()['imageImageRepositoryUuid']]); } private function jsonResponseError(string $message, int $status = Response::HTTP_BAD_REQUEST, ?Trace $trace = null): JsonResponse diff --git a/src/Dto/Input/ConvertImageRepositoryInput.php b/src/Dto/Input/ConvertImageRepositoryInput.php new file mode 100644 index 0000000..d23b49d --- /dev/null +++ b/src/Dto/Input/ConvertImageRepositoryInput.php @@ -0,0 +1,19 @@ +name = $imageRepository->getName(); $this->ip = $imageRepository->getIp(); $this->comments = $imageRepository->getComments(); - $this->status = $status; $this->createdAt = $imageRepository->getCreatedAt(); $this->createdBy = $imageRepository->getCreatedBy(); } diff --git a/src/EventSubscriber/ImageRepositorySubscriber.php b/src/EventSubscriber/ImageRepositorySubscriber.php index 41f6f1b..cd685dd 100644 --- a/src/EventSubscriber/ImageRepositorySubscriber.php +++ b/src/EventSubscriber/ImageRepositorySubscriber.php @@ -3,7 +3,7 @@ namespace App\EventSubscriber; use ApiPlatform\Symfony\EventListener\EventPriorities; -use App\Controller\OgRepository\Image\TransferIsGlobalAction; +use App\Controller\OgRepository\Image\TransferGlobalAction; use App\Dto\Output\ImageRepositoryOutput; use App\Dto\Output\OrganizationalUnitOutput; use App\Entity\Image; @@ -21,8 +21,8 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; final readonly class ImageRepositorySubscriber implements EventSubscriberInterface { public function __construct( - private EntityManagerInterface $entityManager, - private readonly TransferIsGlobalAction $transferIsGlobalAction, + private EntityManagerInterface $entityManager, + private readonly TransferGlobalAction $transferIsGlobalAction, ) { @@ -54,7 +54,7 @@ final readonly class ImageRepositorySubscriber implements EventSubscriberInterfa $imagesToImport = $this->entityManager->getRepository(Image::class)->findBy(['isGlobal' => true]); foreach($imagesToImport as $imageToImport) { - $this->transferIsGlobalAction->__invoke([$imageToImport], $imageRepositoryOutput->getEntity()); + //$this->transferIsGlobalAction->__invoke($imageRepositoryOutput->getEntity()); } } } \ No newline at end of file diff --git a/src/Model/CommandTypes.php b/src/Model/CommandTypes.php index de04265..d86c277 100644 --- a/src/Model/CommandTypes.php +++ b/src/Model/CommandTypes.php @@ -7,10 +7,12 @@ final class CommandTypes public const string DEPLOY_IMAGE = 'deploy-image'; public const string RESTORE_IMAGE = 'restore-image'; public const string CREATE_IMAGE = 'create-image'; + public const string CONVERT_IMAGE = 'convert-image'; public const string CREATE_IMAGE_AUX_FILE = 'create-image-aux-file'; public const string BACKUP_IMAGE = 'backup-image'; public const string IMPORT_IMAGE = 'import-image'; public const string EXPORT_IMAGE = 'export-image'; + public const string CONVERT_IMAGE_TO_VIRTUAL = 'convert-image-to-virtual'; public const string TRANSFER_IMAGE = 'transfer-image'; public const string POWER_ON = 'power-on'; public const string REBOOT = 'reboot'; @@ -18,17 +20,18 @@ final class CommandTypes public const string LOGIN = 'login'; public const string LOGOUT = 'logout'; public const string PARTITION_AND_FORMAT = 'partition-and-format'; - public const string INSTALL_OGLIVE = 'install-oglive'; private const array COMMAND_TYPES = [ self::DEPLOY_IMAGE => 'Deploy Image', self::RESTORE_IMAGE => 'Update Cache', self::CREATE_IMAGE => 'Create Image', - self::CREATE_IMAGE_AUX_FILE => 'Crear fichero auxiliar en repositorio', - self::BACKUP_IMAGE => 'Crear backup de imagen', - self::IMPORT_IMAGE => 'Importar imagen', - self::EXPORT_IMAGE => 'Exportar imagen', + self::CONVERT_IMAGE => 'Convert Image', + self::CONVERT_IMAGE_TO_VIRTUAL => 'Convert Image to Virtual', + self::CREATE_IMAGE_AUX_FILE => 'Create Image Aux File', + self::BACKUP_IMAGE => 'Backup Image', + self::IMPORT_IMAGE => 'Import image', + self::EXPORT_IMAGE => 'Export image', self::POWER_ON => 'Encender', self::REBOOT => 'Reiniciar', self::SHUTDOWN => 'Apagar', diff --git a/src/Model/DeployMethodTypes.php b/src/Model/DeployMethodTypes.php index 053a009..4c48e55 100644 --- a/src/Model/DeployMethodTypes.php +++ b/src/Model/DeployMethodTypes.php @@ -8,7 +8,7 @@ final class DeployMethodTypes public const string MULTICAST_UFTP = 'uftp'; public const string MULTICAST_UFTP_DIRECT = 'uftp-direct'; public const string MULTICAST_UDPCAST = 'udpcast'; - public const string MULTICAST_UDPCAST_DIRECT = 'udp-direct'; + public const string MULTICAST_UDPCAST_DIRECT = 'udpcast-direct'; public const string UNICAST = 'unicast'; public const string UNICAST_DIRECT = 'unicast-direct'; public const string TORRENT = 'p2p'; diff --git a/src/Model/OgLiveStatus.php b/src/Model/OgLiveStatus.php index 2b9e744..f4b1dbf 100644 --- a/src/Model/OgLiveStatus.php +++ b/src/Model/OgLiveStatus.php @@ -8,13 +8,12 @@ final class OgLiveStatus public const string ACTIVE = 'active'; public const string INACTIVE = 'inactive'; public const string DELETED = 'deleted'; - public const string FAILED = 'failed'; private const array OG_LIVE_STATUSES = [ - self::PENDING => 'Pendiente', - self::ACTIVE => 'Activo', - self::INACTIVE => 'Inactivo', + self::PENDING => 'Instalando', + self::ACTIVE => 'Instalada', + self::INACTIVE => 'Sin instalar', self::DELETED => 'Eliminado', self::FAILED => 'Fallido', ]; diff --git a/src/Service/Utils/SimplifyOgLiveFilenameService.php b/src/Service/Utils/SimplifyOgLiveFilenameService.php new file mode 100644 index 0000000..2ac5d0c --- /dev/null +++ b/src/Service/Utils/SimplifyOgLiveFilenameService.php @@ -0,0 +1,28 @@ +source !== 'input') { $response = $this->createImageActionController->__invoke($image); - } else { - if ($data->isGlobal === true) { - $repositories = $this->imageRepositoryRepository->findAll(); - $this->transferActionController->__invoke($repositories, $image); - } } $this->imageRepository->save($image); diff --git a/src/State/Provider/ImageRepositoryProvider.php b/src/State/Provider/ImageRepositoryProvider.php index 5e45ff1..4230b45 100644 --- a/src/State/Provider/ImageRepositoryProvider.php +++ b/src/State/Provider/ImageRepositoryProvider.php @@ -9,23 +9,17 @@ use ApiPlatform\Metadata\Patch; use ApiPlatform\Metadata\Put; use ApiPlatform\State\Pagination\TraversablePaginator; use ApiPlatform\State\ProviderInterface; -use App\Controller\OgRepository\StatusAction; use App\Dto\Input\ImageInput; use App\Dto\Input\ImageRepositoryInput; use App\Dto\Output\ImageOutput; use App\Dto\Output\ImageRepositoryOutput; 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; readonly class ImageRepositoryProvider implements ProviderInterface { public function __construct( - private ProviderInterface $collectionProvider, - private ProviderInterface $itemProvider, - private StatusAction $statusAction + private ProviderInterface $collectionProvider, + private ProviderInterface $itemProvider ) { } @@ -43,23 +37,13 @@ readonly class ImageRepositoryProvider implements ProviderInterface } } - /** - * @throws TransportExceptionInterface - * @throws ServerExceptionInterface - * @throws RedirectionExceptionInterface - * @throws ClientExceptionInterface - */ private function provideCollection(Operation $operation, array $uriVariables = [], array $context = []): object { $paginator = $this->collectionProvider->provide($operation, $uriVariables, $context); $items = new \ArrayObject(); foreach ($paginator->getIterator() as $item){ - $statusResponse = $this->statusAction->__invoke($item); - $content = json_decode($statusResponse->getContent(), true); - $status = !isset($content['error']); - - $items[] = new ImageRepositoryOutput($item, $status); + $items[] = new ImageRepositoryOutput($item); } return new TraversablePaginator($items, $paginator->getCurrentPage(), $paginator->getItemsPerPage(), $paginator->getTotalItems());