refs #1087. CreateImage. First commit
testing/ogcore-api/pipeline/head There was a failure building this commit Details

pull/13/head
Manuel Aranda Rosales 2024-11-07 15:04:38 +01:00
parent 3d0b4f25e2
commit 0545add493
30 changed files with 564 additions and 241 deletions

View File

@ -13,6 +13,7 @@ resources:
filters:
- 'api_platform.filter.image.order'
- 'api_platform.filter.image.search'
- 'api_platform.filter.image.boolean'
ApiPlatform\Metadata\Get:
provider: App\State\Provider\ImageProvider
ApiPlatform\Metadata\Put:

View File

@ -64,6 +64,11 @@ services:
arguments: [ { 'id': 'exact', 'name': 'partial', } ]
tags: [ 'api_platform.filter' ]
api_platform.filter.image.boolean:
parent: 'api_platform.doctrine.orm.boolean_filter'
arguments: [ { 'created': ~ } ]
tags: [ 'api_platform.filter' ]
api_platform.filter.og_live.order:
parent: 'api_platform.doctrine.orm.order_filter'
arguments:
@ -211,7 +216,7 @@ services:
api_platform.filter.trace.search:
parent: 'api_platform.doctrine.orm.search_filter'
arguments: [ { 'id': 'exact', 'command.id': 'exact', 'client.id': 'exact' } ]
arguments: [ { 'id': 'exact', 'command.id': 'exact', 'client.id': 'exact', status: 'exact' } ]
tags: [ 'api_platform.filter' ]
api_platform.filter.trace.order:

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241105070853 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE client ADD CONSTRAINT FK_C744045550C9D4F7 FOREIGN KEY (repository_id) REFERENCES image_repository (id)');
$this->addSql('ALTER TABLE image DROP FOREIGN KEY FK_C53D045FFB84408A');
$this->addSql('DROP INDEX IDX_C53D045FFB84408A ON image');
$this->addSql('ALTER TABLE image ADD partition_info VARCHAR(255) DEFAULT NULL, CHANGE organizational_unit_id parent_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE image ADD CONSTRAINT FK_C53D045F727ACA70 FOREIGN KEY (parent_id) REFERENCES image (id)');
$this->addSql('CREATE INDEX IDX_C53D045F727ACA70 ON image (parent_id)');
$this->addSql('ALTER TABLE network_settings ADD CONSTRAINT FK_48869B5450C9D4F7 FOREIGN KEY (repository_id) REFERENCES image_repository (id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE network_settings DROP FOREIGN KEY FK_48869B5450C9D4F7');
$this->addSql('ALTER TABLE image DROP FOREIGN KEY FK_C53D045F727ACA70');
$this->addSql('DROP INDEX IDX_C53D045F727ACA70 ON image');
$this->addSql('ALTER TABLE image DROP partition_info, CHANGE parent_id organizational_unit_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE image ADD CONSTRAINT FK_C53D045FFB84408A FOREIGN KEY (organizational_unit_id) REFERENCES organizational_unit (id)');
$this->addSql('CREATE INDEX IDX_C53D045FFB84408A ON image (organizational_unit_id)');
$this->addSql('ALTER TABLE client DROP FOREIGN KEY FK_C744045550C9D4F7');
}
}

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241105085054 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE image ADD client_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE image ADD CONSTRAINT FK_C53D045F19EB6921 FOREIGN KEY (client_id) REFERENCES client (id)');
$this->addSql('CREATE INDEX IDX_C53D045F19EB6921 ON image (client_id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE image DROP FOREIGN KEY FK_C53D045F19EB6921');
$this->addSql('DROP INDEX IDX_C53D045F19EB6921 ON image');
$this->addSql('ALTER TABLE image DROP client_id');
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241106133332 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE trace ADD job_id VARCHAR(255) DEFAULT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE trace DROP job_id');
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241107074832 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE image ADD created TINYINT(1) DEFAULT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE image DROP created');
}
}

View File

@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace App\Controller\OgAgent;
use App\Entity\Client;
use App\Entity\Command;
use App\Entity\Image;
use App\Entity\Trace;
use App\Model\ClientStatus;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
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;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class CreateImageActionController extends AbstractController
{
public function __construct(
protected readonly EntityManagerInterface $entityManager,
protected readonly HttpClientInterface $httpClient
)
{
}
public function __invoke(Image $image): JsonResponse
{
if (!$image->getClient()->getIp()) {
throw new ValidatorException('IP is required');
}
$partitionInfo = json_decode($image->getPartitionInfo(), true);
$data = [
'dsk' => (string) $partitionInfo['numDisk'],
'par' => (string) $partitionInfo['numPartition'],
'cpt' => "83",
'idi' => $image->getUuid(),
'nci' => $image->getName(),
'ipr' => $image->getRepository()->getIp(),
'nfn' => 'CrearImagen',
'ids' => '0'
];
try {
$response = $this->httpClient->request('POST', 'https://localhost:4444/CloningEngine/CrearImagen', [
'verify_peer' => false,
'verify_host' => false,
'headers' => [
'Content-Type' => 'application/json',
],
'json' => $data,
]);
} catch (TransportExceptionInterface $e) {
return new JsonResponse(
data: ['error' => $e->getMessage()],
status: Response::HTTP_INTERNAL_SERVER_ERROR
);
}
$jobId = json_decode($response->getContent(), true)['job_id'];
$command = $this->entityManager->getRepository(Command::class)->findOneBy(['name' => 'Crear Imagen']);
$client = $image->getClient();
$client->setStatus(ClientStatus::BUSY);
$this->entityManager->persist($client);
$trace = new Trace();
$trace->setClient($image->getClient());
$trace->setCommand($command ?? null);
$trace->setStatus('pending');
$trace->setJobId($jobId);
$trace->setExecutedAt(new \DateTime());
$this->entityManager->persist($trace);
$this->entityManager->flush();
return new JsonResponse(status: Response::HTTP_OK);
}
}

View File

@ -3,7 +3,9 @@
namespace App\Controller\OgAgent;
use App\Entity\Client;
use App\Entity\OperativeSystem;
use App\Entity\Partition;
use App\Model\ClientStatus;
use App\Model\OgLiveStatus;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@ -40,20 +42,20 @@ class StatusAction extends AbstractController
}
try {
$response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/status', [
'verify_peer' => false, // Desactivar verificación del certificado
'verify_host' => false, // Desactivar verificación del nombre del host
'timeout' => 10, // Tiempo máximo de espera
$response = $this->httpClient->request('POST', 'https://localhost:4444/ogAdmClient/status', [
'verify_peer' => false,
'verify_host' => false,
'timeout' => 10,
'headers' => [
'Content-Type' => 'application/json', // Cabecera de tipo de contenido
'Content-Type' => 'application/json',
],
'json' => [], // Cuerpo de la solicitud como JSON
'json' => [],
]);
$statusCode = $response->getStatusCode();
$client->setStatus($statusCode === Response::HTTP_OK ? 'active' : 'off');
$client->setStatus($statusCode === Response::HTTP_OK ? ClientStatus::OG_LIVE : ClientStatus::OFF);
} catch (TransportExceptionInterface $e) {
$client->setStatus('off');
$client->setStatus(ClientStatus::OFF);
return new JsonResponse(
data: ['error' => $e->getMessage()],
@ -72,6 +74,18 @@ class StatusAction extends AbstractController
$partitionEntity = new Partition();
}
if (isset($cfg['soi']) && $cfg['soi'] !== '') {
$operativeSystem = $this->entityManager->getRepository(OperativeSystem::class)
->findOneBy(['name' => $cfg['soi']]);
if (!$operativeSystem) {
$operativeSystem = new OperativeSystem();
$operativeSystem->setName($cfg['soi']);
$this->entityManager->persist($operativeSystem);
}
$partitionEntity->setOperativeSystem($operativeSystem);
}
$partitionEntity->setClient($client);
$partitionEntity->setDiskNumber($cfg['disk']);
$partitionEntity->setPartitionNumber($cfg['par']);

View File

@ -2,12 +2,11 @@
declare(strict_types=1);
namespace App\Controller\OgAgent;
namespace App\Controller\OgAgent\Webhook;
use App\Entity\Client;
use App\Entity\OrganizationalUnit;
use App\Entity\Partition;
use App\Model\OrganizationalUnitTypes;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
@ -17,7 +16,7 @@ use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Attribute\Route;
#[AsController]
class OgAdmClientController extends AbstractController
class AgentController extends AbstractController
{
public function __construct(
protected readonly EntityManagerInterface $entityManager

View File

@ -0,0 +1,67 @@
<?php
namespace App\Controller\OgAgent\Webhook;
use App\Entity\Image;
use App\Entity\Trace;
use App\Model\ClientStatus;
use App\Model\TraceStatus;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Annotation\Route;
#[AsController]
class ClientsController extends AbstractController
{
public function __construct(
protected readonly EntityManagerInterface $entityManager
)
{
}
#[Route('/opengnsys/rest/clients/status/webhook', methods: ['POST'])]
public function index(Request $request): JsonResponse
{
$data = $request->toArray();
$requiredFields = ['nfn', 'idi', 'dsk', 'par', 'cpt', 'ipr', 'inv_sft', 'ids', 'res', 'der', 'job_id'];
foreach ($requiredFields as $field) {
if (!isset($data[$field])) {
return new JsonResponse(['message' => "Missing parameter: $field"], Response::HTTP_BAD_REQUEST);
}
}
if ($data['nfn'] === 'RESPUESTA_CrearImagen') {
$trace = $this->entityManager->getRepository(Trace::class)->findOneBy(['jobId' => $data['job_id']]);
$image = $this->entityManager->getRepository(Image::class)->findOneBy(['uuid' => $data['idi']]);
if ($data['res'] === 1) {
$trace->setStatus(TraceStatus::SUCCESS);
$trace->setFinishedAt(new \DateTime());
$image->setCreated(true);
} else {
$trace->setStatus(TraceStatus::FAILED);
$trace->setFinishedAt(new \DateTime());
$trace->setOutput($data['der']);
$image->setCreated(false);
}
$this->entityManager->persist($image);
$client = $trace->getClient();
$client->setStatus(ClientStatus::OG_LIVE);
$this->entityManager->persist($trace);
$this->entityManager->flush();
}
return new JsonResponse([], Response::HTTP_OK);
}
}

View File

@ -1,44 +0,0 @@
<?php
namespace App\Controller\OgAgent\Webhook;
use App\Controller\OgBoot\AbstractOgBootController;
use App\Entity\OgLive;
use App\Model\OgLiveStatus;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Annotation\Route;
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;
use Symfony\Contracts\HttpClient\HttpClientInterface;
#[AsController]
class GetStatusAction extends AbstractController
{
public function __construct(
protected readonly EntityManagerInterface $entityManager
)
{
}
#[Route('/clients/status/webhook', name: 'status', methods: ['POST'])]
public function installWebhook(Request $request): JsonResponse
{
$data = json_decode($request->getContent(), true);
if (!is_array($data)) {
return new JsonResponse(['error' => 'Invalid JSON data'], Response::HTTP_BAD_REQUEST);
}
return new JsonResponse(data: $data, status: Response::HTTP_OK);
}
}

View File

@ -2,18 +2,26 @@
declare(strict_types=1);
namespace App\Controller\OgAgent;
namespace App\Controller\OgAgent\Webhook;
use App\Entity\Client;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
#[AsController]
class OgAgentController extends AbstractController
{
public function __construct(
protected readonly EntityManagerInterface $entityManager
)
{
}
#[Route('/opengnsys/rest/ogagent/started', methods: ['POST'])]
public function agentStarted(Request $request): JsonResponse
{
@ -26,7 +34,7 @@ class OgAgentController extends AbstractController
}
}
// Procesar los datos recibidos si es necesario
$client = $this->entityManager->getRepository(Client::class)->findOneBy(['mac' => $data['mac']]);
return new JsonResponse([], Response::HTTP_OK);
}

View File

@ -4,12 +4,14 @@ namespace App\Dto\Input;
use ApiPlatform\Metadata\ApiProperty;
use App\Dto\Output\HardwareProfileOutput;
use App\Dto\Output\ImageRepositoryOutput;
use App\Dto\Output\MenuOutput;
use App\Dto\Output\OgLiveOutput;
use App\Dto\Output\OrganizationalUnitOutput;
use App\Dto\Output\PxeTemplateOutput;
use App\Entity\Client;
use App\Entity\OgRepository;
use App\Entity\ImageRepository;
use App\Model\ClientStatus;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
@ -64,7 +66,7 @@ final class ClientInput
description: 'El estado del cliente',
example: 'active'
)]
public ?string $status = 'off';
public ?string $status = ClientStatus::OFF;
#[Assert\NotNull(message: 'validators.organizational_unit.not_null')]
#[Groups(['client:write', 'client:patch'])]
@ -107,7 +109,7 @@ final class ClientInput
#[ApiProperty(
description: 'descriptions.client.validation'
)]
public ?OgRepository $repository = null;
public ?ImageRepositoryOutput $repository = null;
#[Groups(['client:write'])]
#[ApiProperty(
@ -147,6 +149,10 @@ final class ClientInput
if ($client->getHardwareProfile()) {
$this->hardwareProfile = new HardwareProfileOutput($client->getHardwareProfile());
}
if ($client->getRepository()) {
$this->repository = $client->getRepository();
}
}
public function createOrUpdateEntity(?Client $client = null): Client
@ -165,6 +171,7 @@ final class ClientInput
$client->setMenu($this->menu?->getEntity());
$client->setOgLive($this->ogLive?->getEntity());
$client->setHardwareProfile($this->hardwareProfile?->getEntity());
$client->setRepository($this->repository?->getEntity());
$client->setTemplate($this->template?->getEntity());
$client->setPosition($this->position);
$client->setStatus($this->status);

View File

@ -6,9 +6,11 @@ use ApiPlatform\Metadata\ApiProperty;
use App\Dto\Output\ClientOutput;
use App\Dto\Output\ImageRepositoryOutput;
use App\Dto\Output\OrganizationalUnitOutput;
use App\Dto\Output\PartitionOutput;
use App\Dto\Output\SoftwareProfileOutput;
use App\Entity\Image;
use App\Entity\OrganizationalUnit;
use App\Entity\Partition;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
@ -32,20 +34,8 @@ final class ImageInput
public ?string $type = null;
#[Groups(['image:write'])]
#[ApiProperty(description: 'The path of the image', example: "/path/to/image")]
public ?string $path = null;
#[Groups(['image:write'])]
#[ApiProperty(description: 'The revision of the image', example: "1.0")]
public ?string $revision = null;
#[Groups(['image:write'])]
#[ApiProperty(description: 'The info of the image', example: "Image 1 info")]
public ?string $info = null;
#[Groups(['image:write'])]
#[ApiProperty(description: 'The size of the image', example: 1024)]
public ?int $size = null;
#[ApiProperty(description: 'The type of the image', example: "Server")]
public ?string $source = 'input';
#[Groups(['image:write'])]
#[ApiProperty(description: 'The software profile of the image')]
@ -56,6 +46,19 @@ final class ImageInput
public ?ImageRepositoryOutput $imageRepository = null;
#[Groups(['image:write'])]
#[ApiProperty(description: 'The client of the image')]
public ?ClientOutput $client = null;
#[Groups(['image:write'])]
#[ApiProperty(description: 'The client of the image')]
public ?PartitionOutput $partition = null;
#[Groups(['image:write'])]
#[ApiProperty(description: 'The parent of the image')]
public ?self $parent = null;
#[Groups(['image:write'])]
#[ApiProperty(description: 'The remote pc of the image')]
public ?bool $remotePc = false;
public function __construct(?Image $image = null)
@ -68,10 +71,6 @@ final class ImageInput
$this->description = $image->getDescription();
$this->comments = $image->getComments();
$this->type = $image->getType();
$this->path = $image->getPath();
$this->revision = $image->getRevision();
$this->info = $image->getInfo();
$this->size = $image->getSize();
$this->remotePc = $image->isRemotePc();
if ($image->getSoftwareProfile()) {
@ -81,6 +80,14 @@ final class ImageInput
if ($image->getRepository()) {
$this->imageRepository = new ImageRepositoryOutput($image->getRepository());
}
if ($image->getClient()) {
$this->client = new ClientOutput($image->getClient());
}
if ($image->getParent()) {
$this->parent = new self($image->getParent());
}
}
public function createOrUpdateEntity(?Image $image = null): Image
@ -93,13 +100,37 @@ final class ImageInput
$image->setDescription($this->description);
$image->setComments($this->comments);
$image->setType($this->type);
$image->setPath($this->path);
$image->setRevision($this->revision);
$image->setInfo($this->info);
$image->setSize($this->size);
$image->setSoftwareProfile($this->softwareProfile->getEntity());
$image->setRepository($this->imageRepository->getEntity());
if ($this->softwareProfile) {
$image->setSoftwareProfile($this->softwareProfile->getEntity());
}
$image->setRepository($this->imageRepository ? $this->imageRepository->getEntity()
: $this->client->getEntity()->getRepository());
if ($this->client) {
$image->setClient($this->client->getEntity());
}
if ($this->parent) {
$image->setParent($this->parent->getEntity());
}
$image->setRemotePc($this->remotePc);
$image->setCreated(false);
$partitionInfo = [];
if ($this->partition) {
/** @var Partition $partition */
$partition = $this->partition->getEntity();
$partitionInfo["numDisk"] = $partition->getDiskNumber();
$partitionInfo["numPartition"] = $partition->getPartitionNumber();
$partitionInfo["partitionCode"] = $partition->getPartitionCode();
$partitionInfo["filesystem"] = $partition->getFilesystem();
$partitionInfo["osName"] = $partition->getOperativeSystem()?->getName();
$image->setPartitionInfo(json_encode($partitionInfo));
}
return $image;
}

View File

@ -48,6 +48,10 @@ final class ClientOutput extends AbstractOutput
#[ApiProperty(readableLink: true )]
public ?HardwareProfileOutput $hardwareProfile = null;
#[Groups(['client:read'])]
#[ApiProperty(readableLink: true )]
public ?ImageRepositoryOutput $repository = null;
#[Groups(['client:read', 'organizational-unit:read'])]
#[ApiProperty(readableLink: true )]
public ?PxeTemplateOutput $template = null;
@ -99,6 +103,7 @@ final class ClientOutput extends AbstractOutput
$this->menu = $client->getMenu() ? new MenuOutput($client->getMenu()) : null;
$this->position = $client->getPosition();
$this->template = $client->getTemplate() ? new PxeTemplateOutput($client->getTemplate()) : null;
$this->repository = $client->getRepository() ? new ImageRepositoryOutput($client->getRepository()) : null;
$this->hardwareProfile = $client->getHardwareProfile() ? new HardwareProfileOutput($client->getHardwareProfile()) : null;
$this->subnet = $client->getSubnet()?->getIpAddress();
$this->ogLive = $client->getOgLive() ? new OgLiveOutput($client->getOgLive()) : null;

View File

@ -36,12 +36,18 @@ final class ImageOutput extends AbstractOutput
#[Groups(['image:read'])]
public ?bool $remotePc = null;
#[Groups(['image:read'])]
public ?bool $created = null;
#[Groups(['image:read'])]
public ?SoftwareProfileOutput $softwareProfile = null;
#[Groups(['image:read'])]
public ?ImageRepositoryOutput $imageRepository = null;
#[Groups(['image:read'])]
public ?array $partitionInfo = null;
#[Groups(['image:read'])]
public \DateTime $createdAt;
@ -62,7 +68,9 @@ final class ImageOutput extends AbstractOutput
$this->size = $image->getSize();
$this->softwareProfile = $image->getSoftwareProfile() ? new SoftwareProfileOutput($image->getSoftwareProfile()) : null;
$this->imageRepository = $image->getRepository() ? new ImageRepositoryOutput($image->getRepository()) : null;
$this->partitionInfo = json_decode($image->getPartitionInfo(), true);
$this->remotePc = $image->isRemotePc();
$this->created = $image->isCreated();
$this->createdAt = $image->getCreatedAt();
$this->createdBy = $image->getCreatedBy();
}

View File

@ -9,7 +9,7 @@ use Symfony\Component\Serializer\Annotation\Groups;
#[Get(shortName: 'ImageRepository')]
class ImageRepositoryOutput extends AbstractOutput
{
#[Groups(['repository:read', 'image:read'])]
#[Groups(['repository:read', 'image:read', 'client:read'])]
public ?string $name = '';
#[Groups(['repository:read'])]

View File

@ -9,7 +9,7 @@ use Symfony\Component\Serializer\Annotation\Groups;
#[Get(shortName: 'OperativeSystem')]
final class OperativeSystemOutput extends AbstractOutput
{
#[Groups(['operative-system:read', 'partition:read', 'software-profile:read'])]
#[Groups(['operative-system:read', 'partition:read', 'software-profile:read', 'client:read'])]
public string $name;
#[Groups(['operative-system:read'])]

View File

@ -19,6 +19,9 @@ final class TraceOutput extends AbstractOutput
#[Groups(['trace:read'])]
public string $status;
#[Groups(['trace:read'])]
public ?string $jobId = null;
#[Groups(['trace:read'])]
public ?\DateTimeInterface $executedAt = null;
@ -41,6 +44,7 @@ final class TraceOutput extends AbstractOutput
$this->command = new CommandOutput($trace->getCommand());
$this->client = new ClientOutput($trace->getClient());
$this->status = $trace->getStatus();
$this->jobId = $trace->getJobId();
$this->executedAt = $trace->getExecutedAt();
$this->output = $trace->getOutput();
$this->finishedAt = $trace->getFinishedAt();

View File

@ -61,7 +61,7 @@ class Client extends AbstractEntity
private ?PxeTemplate $template = null;
#[ORM\ManyToOne(inversedBy: 'clients')]
private ?OgRepository $repository = null;
private ?ImageRepository $repository = null;
#[ORM\ManyToOne(inversedBy: 'clients')]
#[ORM\JoinColumn( onDelete: 'SET NULL')]
@ -260,12 +260,12 @@ class Client extends AbstractEntity
}
public function getRepository(): ?OgRepository
public function getRepository(): ?ImageRepository
{
return $this->repository;
}
public function setRepository(?OgRepository $repository): static
public function setRepository(?ImageRepository $repository): static
{
$this->repository = $repository;

View File

@ -37,12 +37,6 @@ class Image extends AbstractEntity
#[ORM\JoinColumn(nullable: true)]
private ?SoftwareProfile $softwareProfile = null;
/**
* @var Collection<int, Partition>
*/
#[ORM\OneToMany(mappedBy: 'image', targetEntity: Partition::class)]
private Collection $partitions;
#[ORM\Column]
private ?bool $remotePc = null;
@ -50,6 +44,18 @@ class Image extends AbstractEntity
#[ORM\JoinColumn(nullable: false)]
private ?\App\Entity\ImageRepository $repository = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $partitionInfo = null;
#[ORM\ManyToOne(targetEntity: self::class)]
private ?self $parent = null;
#[ORM\ManyToOne]
private ?Client $client = null;
#[ORM\Column(nullable: true)]
private ?bool $created = null;
public function __construct()
{
parent::__construct();
@ -152,36 +158,6 @@ class Image extends AbstractEntity
return $this;
}
/**
* @return Collection<int, Partition>
*/
public function getPartitions(): Collection
{
return $this->partitions;
}
public function addPartition(Partition $partition): static
{
if (!$this->partitions->contains($partition)) {
$this->partitions->add($partition);
$partition->setImage($this);
}
return $this;
}
public function removePartition(Partition $partition): static
{
if ($this->partitions->removeElement($partition)) {
// set the owning side to null (unless already changed)
if ($partition->getImage() === $this) {
$partition->setImage(null);
}
}
return $this;
}
public function isRemotePc(): ?bool
{
return $this->remotePc;
@ -205,4 +181,52 @@ class Image extends AbstractEntity
return $this;
}
public function getPartitionInfo(): ?string
{
return $this->partitionInfo;
}
public function setPartitionInfo(?string $partitionInfo): static
{
$this->partitionInfo = $partitionInfo;
return $this;
}
public function getParent(): ?self
{
return $this->parent;
}
public function setParent(?self $parent): static
{
$this->parent = $parent;
return $this;
}
public function getClient(): ?Client
{
return $this->client;
}
public function setClient(?Client $client): static
{
$this->client = $client;
return $this;
}
public function isCreated(): ?bool
{
return $this->created;
}
public function setCreated(?bool $created): static
{
$this->created = $created;
return $this;
}
}

View File

@ -67,7 +67,7 @@ class NetworkSettings extends AbstractEntity
private ?bool $validation = null;
#[ORM\ManyToOne]
private ?OgRepository $repository = null;
private ?ImageRepository $repository = null;
#[ORM\ManyToOne]
private ?OgLive $ogLive = null;
@ -298,12 +298,12 @@ class NetworkSettings extends AbstractEntity
return $this;
}
public function getRepository(): ?OgRepository
public function getRepository(): ?ImageRepository
{
return $this->repository;
}
public function setRepository(?OgRepository $repository): static
public function setRepository(?ImageRepository $repository): static
{
$this->repository = $repository;

View File

@ -1,86 +0,0 @@
<?php
namespace App\Entity;
use App\Repository\OgRepositoryRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: OgRepositoryRepository::class)]
class OgRepository extends AbstractEntity
{
use NameableTrait;
#[ORM\Column(length: 255)]
private ?string $ipAddress = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $description = null;
/**
* @var Collection<int, Client>
*/
#[ORM\OneToMany(mappedBy: 'repository', targetEntity: Client::class)]
private Collection $clients;
public function __construct()
{
parent::__construct();
$this->clients = new ArrayCollection();
}
public function getIpAddress(): ?string
{
return $this->ipAddress;
}
public function setIpAddress(string $ipAddress): static
{
$this->ipAddress = $ipAddress;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): static
{
$this->description = $description;
return $this;
}
/**
* @return Collection<int, Client>
*/
public function getClients(): Collection
{
return $this->clients;
}
public function addClient(Client $client): static
{
if (!$this->clients->contains($client)) {
$this->clients->add($client);
$client->setRepository($this);
}
return $this;
}
public function removeClient(Client $client): static
{
if ($this->clients->removeElement($client)) {
// set the owning side to null (unless already changed)
if ($client->getRepository() === $this) {
$client->setRepository(null);
}
}
return $this;
}
}

View File

@ -29,6 +29,9 @@ class Trace extends AbstractEntity
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
private ?\DateTimeInterface $finishedAt = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $jobId = null;
public function getClient(): ?Client
{
return $this->client;
@ -100,4 +103,16 @@ class Trace extends AbstractEntity
return $this;
}
public function getJobId(): ?string
{
return $this->jobId;
}
public function setJobId(?string $jobId): static
{
$this->jobId = $jobId;
return $this;
}
}

View File

@ -35,8 +35,6 @@ final class ImageFactory extends ModelFactory
return [
'createdAt' => self::faker()->dateTime(),
'name' => self::faker()->text(255),
'path' => self::faker()->text(255),
'size' => self::faker()->randomNumber(),
'softwareProfile' => SoftwareProfileFactory::new(),
'repository' => ImageRepositoryFactory::new(),
'updatedAt' => self::faker()->dateTime(),

View File

@ -0,0 +1,50 @@
<?php
namespace App\Model;
final class ClientStatus
{
public const string OFF = 'off';
public const string INITIALIZING = 'initializing';
public const string OG_LIVE = 'og-live';
public const string BUSY = 'busy';
public const string LINUX = 'linux';
public const string LINUX_SESSION = 'linux-session';
public const string MACOS = 'macos';
public const string WINDOWS = 'windows';
public const string WINDOWS_SESSION = 'windows-session';
private const array CLIENT_STATUSES = [
self::OFF => 'Apagado',
self::INITIALIZING => 'Inicializando',
self::OG_LIVE => 'OG Live',
self::BUSY => 'Ocupado',
self::LINUX => 'Linux',
self::LINUX_SESSION => 'Sesión Linux',
self::MACOS => 'MacOS',
self::WINDOWS => 'Windows',
self::WINDOWS_SESSION => 'Sesión Windows',
];
public static function getClientStatuses(): array
{
return self::CLIENT_STATUSES;
}
public static function getClientStatus(string $clientStatus): ?string
{
return self::CLIENT_STATUSES[$clientStatus] ?? null;
}
public static function getClientStatusKeys(): array
{
return array_keys(self::CLIENT_STATUSES);
}
}

View File

@ -6,13 +6,13 @@ final class TraceStatus
{
public const string PENDING = 'pending';
public const string IN_PROGRESS = 'in-progress';
public const string COMPLETED = 'completed';
public const string SUCCESS = 'success';
public const string FAILED = 'failed';
private const array STATUS = [
self::PENDING => 'Pendiente',
self::IN_PROGRESS => 'En progreso',
self::COMPLETED => 'Completado',
self::SUCCESS => 'Finalizado con éxito',
self::FAILED => 'Fallido',
];

View File

@ -1,18 +0,0 @@
<?php
namespace App\Repository;
use App\Entity\OgRepository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<OgRepositoryRepository>
*/
class OgRepositoryRepository extends AbstractRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, self::class);
}
}

View File

@ -9,6 +9,7 @@ use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use ApiPlatform\State\ProcessorInterface;
use ApiPlatform\Validator\ValidatorInterface;
use App\Controller\OgAgent\CreateImageActionController;
use App\Dto\Input\ImageInput;
use App\Dto\Output\ImageOutput;
use App\Repository\ImageRepository;
@ -17,7 +18,8 @@ readonly class ImageProcessor implements ProcessorInterface
{
public function __construct(
private ImageRepository $imageRepository,
private ValidatorInterface $validator
private ValidatorInterface $validator,
private CreateImageActionController $createImageActionController
)
{
}
@ -52,6 +54,11 @@ readonly class ImageProcessor implements ProcessorInterface
}
$image = $data->createOrUpdateEntity($entity);
if ($data->source !== 'input') {
$response = $this->createImageActionController->__invoke($image);
}
$this->validator->validate($image);
$this->imageRepository->save($image);

View File

@ -105,14 +105,12 @@ class ImageTest extends AbstractTest
$this->createClientWithCredentials()->request('PUT', $iri, ['json' => [
'name' => self::IMAGE_UPDATE,
'size' => 123
]]);
$this->assertResponseIsSuccessful();
$this->assertJsonContains([
'@id' => $iri,
'name' => self::IMAGE_UPDATE,
'size' => 123
]);
}