refs #1353. Added multiple commands logic. Updated endpoints
testing/ogcore-api/pipeline/head This commit looks good Details

pull/20/head
Manuel Aranda Rosales 2025-01-27 13:59:37 +01:00
parent 25d6da01fc
commit 20c243fb52
16 changed files with 326 additions and 222 deletions

View File

@ -52,15 +52,15 @@ resources:
reboot_client:
class: ApiPlatform\Metadata\Post
method: POST
input: false
uriTemplate: /clients/server/{uuid}/reboot
input: App\Dto\Input\MultipleClientsInput
uriTemplate: /clients/server/reboot
controller: App\Controller\OgAgent\RebootAction
power_off_client:
class: ApiPlatform\Metadata\Post
method: POST
input: false
uriTemplate: /clients/server/{uuid}/power-off
input: App\Dto\Input\MultipleClientsInput
uriTemplate: /clients/server/power-off
controller: App\Controller\OgAgent\PowerOffAction

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller;
use ApiPlatform\Validator\ValidatorInterface;
use App\Dto\Input\DeployImageInput;
use App\Entity\Command;
use App\Entity\Image;
@ -29,6 +30,7 @@ class DeployImageAction extends AbstractController
protected readonly EntityManagerInterface $entityManager,
protected readonly HttpClientInterface $httpClient,
protected readonly CreateService $createService,
protected readonly ValidatorInterface $validator,
public readonly \App\Controller\OgAgent\DeployImageAction $deployImageOgAgentAction,
public readonly \App\Controller\OgRepository\Image\DeployImageAction $deployImageOgRepositoryAction,
)
@ -42,67 +44,71 @@ class DeployImageAction extends AbstractController
*/
public function __invoke(DeployImageInput $input, Image $image): JsonResponse
{
/** @var Partition $partition */
$partition = $input->partition->getEntity();
$this->validator->validate($input);
switch ($input->method){
case DeployMethodTypes::UNICAST:
case DeployMethodTypes::UNICAST_DIRECT:
$inputData = [
'method' => $input->method,
'client' => $input->client->getEntity()->getUuid(),
'image' => $image->getUuid(),
'numDisk' => (string) $partition->getDiskNumber(),
'numPartition' => (string) $partition->getPartitionNumber(),
];
foreach ($input->clients as $client) {
$inputData = [
'method' => $input->method,
'client' => $client->getEntity()->getUuid(),
'image' => $image->getUuid(),
'numDisk' => (string) $input->diskNumber,
'numPartition' => (string) $input->partitionNumber,
];
$agentJobId = $this->deployImageOgAgentAction->__invoke($image, $input, DeployMethodTypes::UNICAST);
$this->createService->__invoke($input->client->getEntity(), CommandTypes::DEPLOY_IMAGE, TraceStatus::IN_PROGRESS, $agentJobId, $inputData);
$agentJobId = $this->deployImageOgAgentAction->__invoke($image, $input, $client->getEntity(), DeployMethodTypes::UNICAST);
$this->createService->__invoke($client->getEntity(), CommandTypes::DEPLOY_IMAGE, TraceStatus::IN_PROGRESS, $agentJobId, $inputData);
}
break;
break;
case DeployMethodTypes::MULTICAST_UFTP:
case DeployMethodTypes::MULTICAST_UDPCAST:
case DeployMethodTypes::MULTICAST:
case DeployMethodTypes::MULTICAST_UFTP_DIRECT:
case DeployMethodTypes::MULTICAST_UDPCAST:
case DeployMethodTypes::MULTICAST_UDPCAST_DIRECT:
case DeployMethodTypes::MULTICAST:
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,
];
$inputData = [
'method' => $input->method,
'client' => $input->client->getEntity()->getUuid(),
'image' => $image->getUuid(),
'mcastIp' => $input->mcastIp,
'mcastPort' => $input->mcastPort,
'mcastSpeed' => $input->mcastSpeed,
'mcastMode' => $input->mcastMode,
'numDisk' => (string) $partition->getDiskNumber(),
'numPartition' => (string) $partition->getPartitionNumber(),
];
try {
$this->deployImageOgRepositoryAction->__invoke($input, $image, $client->getEntity(), $this->httpClient);
} catch (\Exception $e) {
//return new JsonResponse(data: ['error' => $e->getMessage()], status: Response::HTTP_INTERNAL_SERVER_ERROR);
continue;
}
try {
$this->deployImageOgRepositoryAction->__invoke($input, $image, $this->httpClient);
} catch (\Exception $e) {
return new JsonResponse(data: ['error' => $e->getMessage()], status: Response::HTTP_INTERNAL_SERVER_ERROR);
$agentJobId = $this->deployImageOgAgentAction->__invoke($image, $input, $client->getEntity(), DeployMethodTypes::MULTICAST);
$this->createService->__invoke($client->getEntity(), CommandTypes::DEPLOY_IMAGE, TraceStatus::IN_PROGRESS, $agentJobId, $inputData);
}
$agentJobId = $this->deployImageOgAgentAction->__invoke($image, $input, DeployMethodTypes::MULTICAST);
$this->createService->__invoke($input->client->getEntity(), CommandTypes::DEPLOY_IMAGE, TraceStatus::IN_PROGRESS, $agentJobId, $inputData);
break;
break;
case DeployMethodTypes::TORRENT:
$inputData = [
'method' => $input->method,
'client' => $input->client->getEntity()->getUuid(),
'image' => $image->getUuid(),
'p2pMode' => $input->p2pMode,
'p2pTime' => $input->p2pTime,
'numDisk' => (string) $partition->getDiskNumber(),
'numPartition' => (string) $partition->getPartitionNumber(),
];
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,
];
$agentJobId = $this->deployImageOgAgentAction->__invoke($image, $input, DeployMethodTypes::TORRENT);
$this->createService->__invoke($input->client->getEntity(), CommandTypes::DEPLOY_IMAGE, TraceStatus::IN_PROGRESS, $agentJobId, $inputData);
$agentJobId = $this->deployImageOgAgentAction->__invoke($image, $input, $client->getEntity(), DeployMethodTypes::TORRENT);
$this->createService->__invoke($client->getEntity(), CommandTypes::DEPLOY_IMAGE, TraceStatus::IN_PROGRESS, $agentJobId, $inputData);
}
break;
}

View File

@ -38,7 +38,7 @@ class DeployImageAction extends AbstractController
{
}
public function __invoke(Image $image, DeployImageInput $input, string $method)
public function __invoke(Image $image, DeployImageInput $input, Client $client, string $method)
{
if (!$image->getClient()->getIp()) {
throw new ValidatorException('IP is required');
@ -46,12 +46,6 @@ class DeployImageAction extends AbstractController
$partitionInfo = json_decode($image->getPartitionInfo(), true);
/** @var Client $client */
$client = $input->client->getEntity();
/** @var Partition $partition */
$partition = $input->partition->getEntity();
$method = match ($input->method) {
DeployMethodTypes::MULTICAST_UFTP_DIRECT, DeployMethodTypes::MULTICAST_UDPCAST_DIRECT, => 'multicast-direct',
DeployMethodTypes::MULTICAST, DeployMethodTypes::MULTICAST_UFTP, DeployMethodTypes::MULTICAST_UDPCAST => 'multicast',
@ -61,7 +55,7 @@ class DeployImageAction extends AbstractController
default => throw new ValidatorException('Invalid method'),
};
$ptcMulticastValue = "$method $input->mcastPort:$input->mcastMode:$input->mcastIp:$input->mcastSpeed:$input->maxClients:$input->maxTime";
$ptcMulticastValue = "$method $input->mcastPort:$input->mcastMode.'-duplex'.:$input->mcastIp:$input->mcastSpeed.'M'.:$input->maxClients:$input->maxTime";
$ptcTorrentValue = "$method $input->p2pMode:$input->p2pTime";
$ptcUnicastValue = $method;
@ -73,8 +67,8 @@ class DeployImageAction extends AbstractController
};
$data = [
'dsk' => (string) $partition->getDiskNumber(),
'par' => (string) $partition->getPartitionNumber(),
'dsk' => (string) $input->diskNumber,
'par' => (string) $input->partitionNumber,
'ifs' => "1",
'idi' => $image->getUuid(),
'nci' => $image->getName(),
@ -97,10 +91,6 @@ class DeployImageAction extends AbstractController
} catch (TransportExceptionInterface $e) {
$this->logger->error('Error deploying image', ['image' => $image->getId(), 'error' => $e->getMessage()]);
return new JsonResponse(
data: ['error' => $e->getMessage()],
status: Response::HTTP_INTERNAL_SERVER_ERROR
);
}
$jobId = json_decode($response->getContent(), true)['job_id'];

View File

@ -8,6 +8,7 @@ use App\Dto\Input\PartitionPostInput;
use App\Entity\Client;
use App\Entity\Command;
use App\Entity\Image;
use App\Entity\Partition;
use App\Entity\Trace;
use App\Model\ClientStatus;
use App\Model\CommandTypes;
@ -46,79 +47,88 @@ class PartitionAssistantAction extends AbstractController
throw new ValidatorException('Partitions is required');
}
/** @var Client $client */
$client = $input->partitions[0]->client->getEntity();
foreach ($input->clients as $clientInput) {
$client = $clientInput->getEntity();
$disks = [];
foreach ($partitions as $partition) {
$diskNumber = $partition->diskNumber;
$disks = [];
foreach ($partitions as $partition) {
$diskNumber = $partition->diskNumber;
if (!isset($disks[$diskNumber])) {
$disks[$diskNumber] = [
'diskData' => [],
'partitionData' => []
];
}
if ($partition->filesystem === 'CACHE') {
$disks[$diskNumber]['diskData'] = [
'dis' => (string) $diskNumber,
'che' => "0",
'tch' => (string) ($partition->size * 1024),
];
}
$disks[$diskNumber]['partitionData'][] = [
'par' => (string) $partition->partitionNumber,
'cpt' => $partition->partitionCode,
'sfi' => $partition->filesystem,
'tam' => (string) (integer) ($partition->size * 1024),
'ope' => $partition->format ? "1" : "0",
];
}
foreach ($disks as $diskNumber => $diskInfo) {
$data = [];
if (!empty($diskInfo['diskData'])) {
$data[] = $diskInfo['diskData'];
}
$data = array_merge($data, $diskInfo['partitionData']);
$result = [
"nfn" => "Configurar",
"dsk" => (string) $diskNumber,
"cfg" => $data,
"ids" => "0"
];
try {
$response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/CloningEngine/Configurar', [
'verify_peer' => false,
'verify_host' => false,
'headers' => [
'Content-Type' => 'application/json',
],
'json' => $result,
$partitionEntity = $this->entityManager->getRepository(Partition::class)->findOneBy([
'client' => $client,
'partitionNumber' => $partition->partitionNumber,
'diskNumber' => $partition->diskNumber,
]);
$this->logger->info('Partitioning disk', ['client' => $client->getId(), 'disk' => $diskNumber]);
} catch (TransportExceptionInterface $e) {
$this->logger->error('Error partitioning disk', ['client' => $client->getId(), 'disk' => $diskNumber, 'error' => $e->getMessage()]);
return new JsonResponse(
data: ['error' => "Error en disco $diskNumber: " . $e->getMessage()],
status: Response::HTTP_INTERNAL_SERVER_ERROR
);
if ($partitionEntity) {
$partitionEntity->setClient($client);
$this->entityManager->persist($partitionEntity);
}
if (!isset($disks[$diskNumber])) {
$disks[$diskNumber] = [
'diskData' => [],
'partitionData' => []
];
}
if ($partition->filesystem === 'CACHE') {
$disks[$diskNumber]['diskData'] = [
'dis' => (string) $diskNumber,
'che' => "0",
'tch' => (string) ($partition->size * 1024),
];
}
$disks[$diskNumber]['partitionData'][] = [
'par' => (string) $partition->partitionNumber,
'cpt' => $partition->partitionCode,
'sfi' => $partition->filesystem,
'tam' => (string) (integer) ($partition->size * 1024),
'ope' => $partition->format ? "1" : "0",
];
}
$jobId = json_decode($response->getContent(), true)['job_id'];
foreach ($disks as $diskNumber => $diskInfo) {
$data = [];
if (!empty($diskInfo['diskData'])) {
$data[] = $diskInfo['diskData'];
}
$data = array_merge($data, $diskInfo['partitionData']);
$client->setStatus(ClientStatus::BUSY);
$this->entityManager->persist($client);
$this->entityManager->flush();
$result = [
"nfn" => "Configurar",
"dsk" => (string) $diskNumber,
"cfg" => $data,
"ids" => "0"
];
$this->createService->__invoke($client, CommandTypes::PARTITION_AND_FORMAT, TraceStatus::IN_PROGRESS, $jobId, []);
try {
$response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/CloningEngine/Configurar', [
'verify_peer' => false,
'verify_host' => false,
'headers' => [
'Content-Type' => 'application/json',
],
'json' => $result,
]);
$this->logger->info('Partitioning disk', ['client' => $client->getId(), 'disk' => $diskNumber]);
} catch (TransportExceptionInterface $e) {
$this->logger->error('Error partitioning disk', ['client' => $client->getId(), 'disk' => $diskNumber, 'error' => $e->getMessage()]);
continue;
}
$jobId = json_decode($response->getContent(), true)['job_id'];
$client->setStatus(ClientStatus::BUSY);
$this->entityManager->persist($client);
$this->entityManager->flush();
$this->createService->__invoke($client, CommandTypes::PARTITION_AND_FORMAT, TraceStatus::IN_PROGRESS, $jobId, []);
}
}
return new JsonResponse(data: $client, status: Response::HTTP_OK);
return new JsonResponse(data: [], status: Response::HTTP_OK);
}
}

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller\OgAgent;
use App\Dto\Input\MultipleClientsInput;
use App\Entity\Client;
use App\Entity\Command;
use App\Entity\Image;
@ -37,44 +38,45 @@ class PowerOffAction extends AbstractController
{
}
public function __invoke(Client $client): JsonResponse
public function __invoke(MultipleClientsInput $input): JsonResponse
{
if (!$client->getIp()) {
throw new ValidatorException('IP is required');
foreach ($input->clients as $clientEntity) {
$client = $clientEntity->getEntity();
if (!$client->getIp()) {
throw new ValidatorException('IP is required');
}
$data = [
'nfn' => 'Apagar',
'ids' => '0'
];
try {
$response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/ogAdmClient/Apagar', [
'verify_peer' => false,
'verify_host' => false,
'headers' => [
'Content-Type' => 'application/json',
],
'json' => $data,
]);
$this->logger->info('Powering off client', ['client' => $client->getId()]);
} catch (TransportExceptionInterface $e) {
$this->logger->error('Error powering off client', ['client' => $client->getId(), 'error' => $e->getMessage()]);
continue;
}
$jobId = json_decode($response->getContent(), true)['job_id'];
$client->setStatus(ClientStatus::OFF);
$this->entityManager->persist($client);
$this->entityManager->flush();
$this->createService->__invoke($client, CommandTypes::SHUTDOWN, TraceStatus::SUCCESS, $jobId, []);
}
$data = [
'nfn' => 'Apagar',
'ids' => '0'
];
try {
$response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/ogAdmClient/Apagar', [
'verify_peer' => false,
'verify_host' => false,
'headers' => [
'Content-Type' => 'application/json',
],
'json' => $data,
]);
$this->logger->info('Powering off client', ['client' => $client->getId()]);
} catch (TransportExceptionInterface $e) {
$this->logger->error('Error powering off client', ['client' => $client->getId(), 'error' => $e->getMessage()]);
return new JsonResponse(
data: ['error' => $e->getMessage()],
status: Response::HTTP_INTERNAL_SERVER_ERROR
);
}
$jobId = json_decode($response->getContent(), true)['job_id'];
$client->setStatus(ClientStatus::OFF);
$this->entityManager->persist($client);
$this->entityManager->flush();
$this->createService->__invoke($client, CommandTypes::SHUTDOWN, TraceStatus::SUCCESS, $jobId, []);
return new JsonResponse(data: $client, status: Response::HTTP_OK);
}
}

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller\OgAgent;
use App\Dto\Input\MultipleClientsInput;
use App\Entity\Client;
use App\Entity\Command;
use App\Entity\Image;
@ -37,44 +38,49 @@ class RebootAction extends AbstractController
{
}
public function __invoke(Client $client): JsonResponse
public function __invoke(MultipleClientsInput $input): JsonResponse
{
if (!$client->getIp()) {
throw new ValidatorException('IP is required');
foreach ($input->clients as $clientEntity) {
$client = $clientEntity->getEntity();
if (!$client->getIp()) {
throw new ValidatorException('IP is required');
}
$data = [
'nfn' => 'Reiniciar',
'ids' => '0'
];
try {
$response = $this->httpClient->request('POST', 'https://' . $client->getIp() . ':8000/ogAdmClient/Reiniciar', [
'verify_peer' => false,
'verify_host' => false,
'headers' => [
'Content-Type' => 'application/json',
],
'json' => $data,
]);
$this->logger->info('Rebooting client', ['client' => $client->getId()]);
} catch (TransportExceptionInterface $e) {
$this->logger->error('Error rebooting client', ['client' => $client->getId(), 'error' => $e->getMessage()]);
return new JsonResponse(
data: ['error' => $e->getMessage()],
status: Response::HTTP_INTERNAL_SERVER_ERROR
);
}
$jobId = json_decode($response->getContent(), true)['job_id'];
$client->setStatus(ClientStatus::INITIALIZING);
$this->entityManager->persist($client);
$this->entityManager->flush();
$this->createService->__invoke($client, CommandTypes::REBOOT, TraceStatus::SUCCESS, $jobId, []);
}
$data = [
'nfn' => 'Reiniciar',
'ids' => '0'
];
try {
$response = $this->httpClient->request('POST', 'https://'.$client->getIp().':8000/ogAdmClient/Reiniciar', [
'verify_peer' => false,
'verify_host' => false,
'headers' => [
'Content-Type' => 'application/json',
],
'json' => $data,
]);
$this->logger->info('Rebooting client', ['client' => $client->getId()]);
} catch (TransportExceptionInterface $e) {
$this->logger->error('Error rebooting client', ['client' => $client->getId(), 'error' => $e->getMessage()]);
return new JsonResponse(
data: ['error' => $e->getMessage()],
status: Response::HTTP_INTERNAL_SERVER_ERROR
);
}
$jobId = json_decode($response->getContent(), true)['job_id'];
$client->setStatus(ClientStatus::INITIALIZING);
$this->entityManager->persist($client);
$this->entityManager->flush();
$this->createService->__invoke($client, CommandTypes::REBOOT, TraceStatus::SUCCESS, $jobId, []);
return new JsonResponse(data: $client, status: Response::HTTP_OK);
}
}

View File

@ -53,7 +53,7 @@ class ClientsController extends AbstractController
public function index(Request $request): JsonResponse
{
$data = $request->toArray();
$requiredFields = ['nfn', 'ids', 'res', 'der', 'job_id'];
$requiredFields = ['res', 'der', 'job_id'];
foreach ($requiredFields as $field) {
if (!isset($data[$field])) {

View File

@ -30,11 +30,11 @@ class PostAction extends AbstractOgBootController
{
$ogRepoIp = $this->ogBootApiUrl;
if ($client->getRepository()) {
$ogRepoIp = $client->getRepository()->getIp();
} else if ($client->getOrganizationalUnit()->getNetworkSettings()->getRepository()) {
$ogRepoIp = $client->getOrganizationalUnit()->getNetworkSettings()->getRepository()->getIp();
}
$ogRepoIp = $client->getRepository()?->getIp()
?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getRepository()?->getIp();
$ogLive = $client->getOgLive()
?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getOgLive()->getFilename();
$params = [
'json' => [
@ -54,7 +54,7 @@ class PostAction extends AbstractOgBootController
'oglog' => $this->ogLogIp,
'ogshare' => $client->getOrganizationalUnit()->getNetworkSettings()?->getOgShare()
? $client->getOrganizationalUnit()->getNetworkSettings()?->getOgShare(): $this->ogBootApiUrl,
'oglivedir' => $client->getOgLive()->getFilename(),
'oglivedir' => $ogLive,
'ogprof' => 'false',
'hardprofile' => $client->getHardwareProfile() ? $client->getHardwareProfile()->getDescription() : 'default',
'ogntp' => $client->getOrganizationalUnit()->getNetworkSettings()?->getNtp(),

View File

@ -4,6 +4,7 @@ namespace App\Controller\OgRepository\Image;
use App\Controller\OgRepository\AbstractOgRepositoryController;
use App\Dto\Input\DeployImageInput;
use App\Entity\Client;
use App\Entity\Command;
use App\Entity\Image;
use App\Model\CommandTypes;
@ -28,10 +29,8 @@ class DeployImageAction extends AbstractOgRepositoryController
* @throws ClientExceptionInterface
* @throws TransportExceptionInterface
*/
public function __invoke(DeployImageInput $input, Image $data, HttpClientInterface $httpClient): JsonResponse
public function __invoke(DeployImageInput $input, Image $data, Client $client, HttpClientInterface $httpClient): JsonResponse
{
$client = $input->client;
$params = [
'json' => [
'ID_img' => $data->getImageFullsum(),

View File

@ -6,11 +6,13 @@ 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;
use App\Validator\Constraints\OrganizationalUnitP2PMode;
use Symfony\Component\Serializer\Annotation\Groups;
#[ClientsHaveSamePartitionCount]
class DeployImageInput
{
#[Groups(['image:write'])]
@ -21,19 +23,24 @@ class DeployImageInput
#[ApiProperty(description: 'The type of the image deployment', example: "")]
public ?string $method = null;
/**
* @var ClientOutput[]
*/
#[Groups(['image:write'])]
#[ApiProperty(description: 'The client to deploy the image')]
public ?ClientOutput $client = null;
public ?array $clients = [];
#[Groups(['image:write'])]
#[ApiProperty(description: 'The partition to deploy the image')]
public ?PartitionOutput $partition = null;
public ?int $diskNumber = null;
#[Groups(['image:write'])]
public ?int $partitionNumber = null;
#[OrganizationalUnitP2PMode]
#[Groups(['image:write'])]
public ?string $p2pMode = null;
#[Groups(['organizational-unit:write'])]
#[Groups(['image:write'])]
public ?int $p2pTime = null;
#[Groups(['image:write'])]

View File

@ -0,0 +1,16 @@
<?php
namespace App\Dto\Input;
use App\Dto\Output\ClientOutput;
use Symfony\Component\Serializer\Annotation\Groups;
final class MultipleClientsInput
{
/**
* @var ClientOutput[]
*/
#[Groups(['client:write'])]
public array $clients = [];
}

View File

@ -58,11 +58,6 @@ final class PartitionInput
#[ApiProperty(description: 'The operative system name of the partition', example: "Ubuntu")]
public ?OperativeSystemOutput $operativeSystem = null;
#[Assert\NotNull()]
#[Groups(['partition:write'])]
#[ApiProperty(description: 'The client of the partition')]
public ?ClientOutput $client = null;
#[Groups(['partition:write'])]
#[ApiProperty(description: 'The memory usage of the partition', example: 100)]
public ?int $memoryUsage = null;
@ -88,9 +83,6 @@ final class PartitionInput
$this->operativeSystem = new OperativeSystemOutput($partition->getOperativeSystem());
}
if ($partition->getClient()) {
$this->client = new ClientOutput($partition->getClient());
}
$this->memoryUsage = $partition->getMemoryUsage();
if ($partition->getImage()) {
@ -114,7 +106,6 @@ final class PartitionInput
if ($this->operativeSystem) {
$partition->setOperativeSystem($this->operativeSystem->getEntity());
}
$partition->setClient($this->client->getEntity());
$partition->setMemoryUsage($this->memoryUsage * 100);
if ($this->image) {

View File

@ -12,5 +12,11 @@ final class PartitionPostInput
*/
#[Groups(['partition:write'])]
public array $partitions = [];
/**
* @var ClientOutput[]
*/
#[Groups(['partition:write'])]
public array $clients = [];
}

View File

@ -103,10 +103,19 @@ 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;
$repository = $client->getRepository()
?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getRepository();
$this->repository = $repository ? new ImageRepositoryOutput($repository) : null;
$ogLive = $client->getOgLive()
?? $client->getOrganizationalUnit()?->getNetworkSettings()?->getOgLive();
$this->ogLive = $ogLive ? new OgLiveOutput($ogLive) : null;
$this->hardwareProfile = $client->getHardwareProfile() ? new HardwareProfileOutput($client->getHardwareProfile()) : null;
$this->subnet = $client->getSubnet()?->getIpAddress();
$this->ogLive = $client->getOgLive() ? new OgLiveOutput($client->getOgLive()) : null;
$this->status = $client->getStatus();
$this->createdAt = $client->getCreatedAt();
$this->createdBy = $client->getCreatedBy();

View File

@ -0,0 +1,23 @@
<?php
namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
#[\Attribute]
class ClientsHaveSamePartitionCount extends Constraint
{
public string $message;
public function __construct(mixed $options = null, ?array $groups = null, mixed $payload = null)
{
parent::__construct($options, $groups, $payload);
$this->message = 'All clients must have the same number of partitions.';
}
public function getTargets(): string
{
return self::CLASS_CONSTRAINT;
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Validator\Constraints;
use App\Dto\Input\DeployImageInput;
use App\Dto\Output\ClientOutput;
use App\Entity\Client;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Constraint;
class ClientsHaveSamePartitionCountValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint): void
{
if (!$value instanceof DeployImageInput) {
return;
}
if (isset($value->clients) && is_array($value->clients)) {
$partitionCounts = [];
foreach ($value->clients as $client) {
$partitionCount = $client->getEntity()->getPartitions()->count();
$partitionCounts[(string) $client->getEntity()->getIp()] = $partitionCount;
}
if (count(array_unique($partitionCounts)) > 1) {
$errorDetails = [];
foreach ($partitionCounts as $clientIp => $partitionCount) {
$errorDetails[] = "Cliente $clientIp tiene $partitionCount particiones.";
}
$detailedMessage = implode(" ", $errorDetails);
$this->context->buildViolation($constraint->message . ' Detalles: ' . $detailedMessage)
->addViolation();
}
}
}
}