refs #2585. CommandTask schedule. Update several scripts with new logic
testing/ogcore-api/pipeline/head There was a failure building this commit
Details
testing/ogcore-api/pipeline/head There was a failure building this commit
Details
parent
c4870223e9
commit
f627f0a86d
|
@ -4,7 +4,21 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Controller\OgAgent\CreateImageAction;
|
||||
use App\Controller\OgAgent\DeployImageAction;
|
||||
use App\Controller\OgAgent\PartitionAssistantAction;
|
||||
use App\Controller\OgAgent\RunScriptAction;
|
||||
use App\Dto\Input\CommandExecuteInput;
|
||||
use App\Dto\Input\DeployImageInput;
|
||||
use App\Dto\Input\PartitionInput;
|
||||
use App\Dto\Input\PartitionPostInput;
|
||||
use App\Dto\Output\ClientOutput;
|
||||
use App\Entity\Client;
|
||||
use App\Entity\Image;
|
||||
use App\Entity\ImageImageRepository;
|
||||
use App\Entity\Partition;
|
||||
use App\Entity\Trace;
|
||||
use App\Model\CommandTypes;
|
||||
use App\Model\TraceStatus;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
@ -17,7 +31,11 @@ use Symfony\Component\Console\Style\SymfonyStyle;
|
|||
class ExecutePendingTracesCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly CreateImageAction $createImageAction,
|
||||
private readonly DeployImageAction $deployImageAction,
|
||||
private readonly PartitionAssistantAction $partitionAssistantAction,
|
||||
private readonly RunScriptAction $runScriptAction
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
@ -28,22 +46,271 @@ class ExecutePendingTracesCommand extends Command
|
|||
$startTime = microtime(true);
|
||||
|
||||
$traces = $this->entityManager->getRepository(Trace::class)
|
||||
->findBy(['status' => TraceStatus::PENDING]);
|
||||
->createQueryBuilder('t')
|
||||
->select('t')
|
||||
->where('t.status = :status')
|
||||
->setParameter('status', TraceStatus::PENDING)
|
||||
->andWhere('t.id IN (
|
||||
SELECT MIN(t2.id)
|
||||
FROM App\Entity\Trace t2
|
||||
WHERE t2.status = :status
|
||||
GROUP BY t2.client
|
||||
)')
|
||||
->orderBy('t.id', 'ASC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
|
||||
$count = count($traces);
|
||||
$io->info("Found $count pending traces");
|
||||
$io->info("Found $count pending trace(s) - processing the latest one");
|
||||
|
||||
$processedCount = 0;
|
||||
$errorCount = 0;
|
||||
|
||||
foreach ($traces as $trace) {
|
||||
$trace->setStatus(TraceStatus::IN_PROGRESS);
|
||||
$this->entityManager->persist($trace);
|
||||
try {
|
||||
$io->info("Processing trace {$trace->getUuid()} with command: {$trace->getCommand()}");
|
||||
|
||||
$result = $this->executeTrace($trace);
|
||||
|
||||
if ($result) {
|
||||
$processedCount++;
|
||||
$io->success("Successfully processed trace {$trace->getUuid()}");
|
||||
} else {
|
||||
$errorCount++;
|
||||
$io->error("Failed to process trace {$trace->getUuid()}");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$errorCount++;
|
||||
$io->error("Error processing trace {$trace->getUuid()}: " . $e->getMessage());
|
||||
|
||||
$trace->setStatus(TraceStatus::FAILED);
|
||||
$trace->setOutput($e->getMessage());
|
||||
$trace->setFinishedAt(new \DateTime());
|
||||
$this->entityManager->persist($trace);
|
||||
}
|
||||
}
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
$executionTime = microtime(true) - $startTime;
|
||||
$io->success("Updated $count traces to IN_PROGRESS status");
|
||||
$io->success("Processed $processedCount traces successfully, $errorCount failed");
|
||||
$io->note("Execution time: " . round($executionTime, 3) . "s");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function executeTrace(Trace $trace): bool
|
||||
{
|
||||
$command = $trace->getCommand();
|
||||
$input = $trace->getInput() ?? [];
|
||||
$client = $trace->getClient();
|
||||
|
||||
if (!$client) {
|
||||
throw new \Exception("No client associated with trace");
|
||||
}
|
||||
|
||||
$trace->setExecutedAt(new \DateTime());
|
||||
$this->entityManager->persist($trace);
|
||||
$this->entityManager->flush();
|
||||
|
||||
try {
|
||||
switch ($command) {
|
||||
case CommandTypes::CREATE_IMAGE:
|
||||
return $this->executeCreateImage($trace, $input);
|
||||
|
||||
case CommandTypes::DEPLOY_IMAGE:
|
||||
return $this->executeDeployImage($trace, $input);
|
||||
|
||||
case CommandTypes::PARTITION_AND_FORMAT:
|
||||
return $this->executePartitionAssistant($trace, $input);
|
||||
|
||||
case CommandTypes::RUN_SCRIPT:
|
||||
return $this->executeRunScript($trace, $input);
|
||||
|
||||
default:
|
||||
throw new \Exception("Unsupported command type: $command");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$trace->setStatus(TraceStatus::FAILED);
|
||||
$trace->setOutput($e->getMessage());
|
||||
$trace->setFinishedAt(new \DateTime());
|
||||
$this->entityManager->persist($trace);
|
||||
$this->entityManager->flush();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function executeCreateImage(Trace $trace, array $input): bool
|
||||
{
|
||||
$client = $trace->getClient();
|
||||
|
||||
if (!isset($input['image'])) {
|
||||
throw new \Exception("Image UUID not found in trace input");
|
||||
}
|
||||
|
||||
$image = $this->entityManager->getRepository(Image::class)
|
||||
->findOneBy(['uuid' => $input['image']]);
|
||||
|
||||
if (!$image) {
|
||||
throw new \Exception("Image not found with UUID: {$input['image']}");
|
||||
}
|
||||
|
||||
$partition = null;
|
||||
if (isset($input['diskNumber']) && isset($input['partitionNumber'])) {
|
||||
$partition = $this->entityManager->getRepository(Partition::class)
|
||||
->findOneBy([
|
||||
'client' => $client,
|
||||
'diskNumber' => $input['diskNumber'],
|
||||
'partitionNumber' => $input['partitionNumber']
|
||||
]);
|
||||
|
||||
if (!$partition) {
|
||||
throw new \Exception("Partition not found for client {$client->getUuid()} with disk {$input['diskNumber']} and partition {$input['partitionNumber']}");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->createImageAction->__invoke(
|
||||
queue: false,
|
||||
image: $image,
|
||||
partition: $partition,
|
||||
client: $client,
|
||||
gitRepositoryName: $input['gitRepositoryName'] ?? null,
|
||||
existingTrace: $trace
|
||||
);
|
||||
|
||||
if ($response->getStatusCode() === 200) {
|
||||
$trace->setStatus(TraceStatus::SUCCESS);
|
||||
$trace->setFinishedAt(new \DateTime());
|
||||
$this->entityManager->persist($trace);
|
||||
$this->entityManager->flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (\Exception $e) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function executeDeployImage(Trace $trace, array $input): bool
|
||||
{
|
||||
$client = $trace->getClient();
|
||||
|
||||
if (!isset($input['imageImageRepository'])) {
|
||||
throw new \Exception("ImageImageRepository UUID not found in trace input");
|
||||
}
|
||||
|
||||
$imageImageRepository = $this->entityManager->getRepository(ImageImageRepository::class)
|
||||
->findOneBy(['uuid' => $input['imageImageRepository']]);
|
||||
|
||||
if (!$imageImageRepository) {
|
||||
throw new \Exception("ImageImageRepository not found with UUID: {$input['imageImageRepository']}");
|
||||
}
|
||||
|
||||
$deployInput = new DeployImageInput();
|
||||
$deployInput->method = $input['method'] ?? 'unicast';
|
||||
$deployInput->type = $input['type'] ?? 'monolithic';
|
||||
$deployInput->diskNumber = $input['diskNumber'] ?? 1;
|
||||
$deployInput->partitionNumber = $input['partitionNumber'] ?? 1;
|
||||
$deployInput->mcastMode = $input['mcastMode'] ?? 'duplex';
|
||||
$deployInput->mcastSpeed = $input['mcastSpeed'] ?? 100;
|
||||
$deployInput->mcastPort = $input['mcastPort'] ?? 8000;
|
||||
$deployInput->mcastIp = $input['mcastIp'] ?? '224.0.0.1';
|
||||
$deployInput->maxClients = $input['maxClients'] ?? 10;
|
||||
$deployInput->maxTime = $input['maxTime'] ?? 3600;
|
||||
$deployInput->p2pMode = $input['p2pMode'] ?? 'seed';
|
||||
$deployInput->p2pTime = $input['p2pTime'] ?? 300;
|
||||
|
||||
$jobId = $this->deployImageAction->__invoke(
|
||||
imageImageRepository: $imageImageRepository,
|
||||
input: $deployInput,
|
||||
client: $client
|
||||
);
|
||||
|
||||
$trace->setJobId($jobId);
|
||||
$this->entityManager->persist($trace);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function executePartitionAssistant(Trace $trace, array $input): bool
|
||||
{
|
||||
$client = $trace->getClient();
|
||||
|
||||
$partitionInput = new PartitionPostInput();
|
||||
$partitionInput->clients = [new ClientOutput($client)];
|
||||
|
||||
$partitions = [];
|
||||
$diskNumber = 1;
|
||||
|
||||
foreach ($input as $item) {
|
||||
if (isset($item['dis'])) {
|
||||
$diskNumber = (int)$item['dis'];
|
||||
} elseif (isset($item['par'])) {
|
||||
$partitionInputObj = new PartitionInput();
|
||||
$partitionInputObj->diskNumber = $diskNumber;
|
||||
$partitionInputObj->partitionNumber = (int)$item['par'];
|
||||
$partitionInputObj->partitionCode = $item['cpt'] ?? 'LINUX';
|
||||
$partitionInputObj->size = (float)($item['tam'] / 1024);
|
||||
$partitionInputObj->filesystem = $item['sfi'] ?? 'EXT4';
|
||||
$partitionInputObj->format = ($item['ope'] ?? '0') === '1';
|
||||
$partitions[] = $partitionInputObj;
|
||||
}
|
||||
}
|
||||
|
||||
$partitionInput->partitions = $partitions;
|
||||
$partitionInput->queue = false;
|
||||
|
||||
try {
|
||||
$response = $this->partitionAssistantAction->__invoke($partitionInput, $trace);
|
||||
|
||||
if ($response->getStatusCode() === 200) {
|
||||
$trace->setStatus(TraceStatus::SUCCESS);
|
||||
$trace->setFinishedAt(new \DateTime());
|
||||
$this->entityManager->persist($trace);
|
||||
$this->entityManager->flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (\Exception $e) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function executeRunScript(Trace $trace, array $input): bool
|
||||
{
|
||||
$client = $trace->getClient();
|
||||
|
||||
if (!isset($input['script'])) {
|
||||
throw new \Exception("Script not found in trace input");
|
||||
}
|
||||
|
||||
$commandExecuteInput = new CommandExecuteInput();
|
||||
$commandExecuteInput->clients = [new ClientOutput($client)];
|
||||
$commandExecuteInput->script = $input['script'];
|
||||
$commandExecuteInput->queue = false;
|
||||
|
||||
try {
|
||||
$response = $this->runScriptAction->__invoke($commandExecuteInput, $trace);
|
||||
|
||||
if ($response->getStatusCode() === 200) {
|
||||
$trace->setStatus(TraceStatus::SUCCESS);
|
||||
$trace->setFinishedAt(new \DateTime());
|
||||
$this->entityManager->persist($trace);
|
||||
$this->entityManager->flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (\Exception $e) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,13 +4,23 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Controller\OgAgent\CreateImageAction;
|
||||
use App\Controller\OgAgent\DeployImageAction;
|
||||
use App\Controller\OgAgent\PartitionAssistantAction;
|
||||
use App\Controller\OgAgent\RunScriptAction;
|
||||
use App\Dto\Input\CommandExecuteInput;
|
||||
use App\Dto\Input\DeployImageInput;
|
||||
use App\Dto\Input\PartitionInput;
|
||||
use App\Dto\Input\PartitionPostInput;
|
||||
use App\Dto\Output\ClientOutput;
|
||||
use App\Entity\CommandTask;
|
||||
use App\Entity\Image;
|
||||
use App\Entity\ImageImageRepository;
|
||||
use App\Entity\Partition;
|
||||
use App\Entity\Trace;
|
||||
use App\Model\ClientStatus;
|
||||
use App\Model\CommandTypes;
|
||||
use App\Model\TraceStatus;
|
||||
use App\Repository\CommandTaskRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
|
@ -27,6 +37,8 @@ class RunScheduledCommandTasksCommand extends Command
|
|||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly RunScriptAction $runScriptAction,
|
||||
private readonly DeployImageAction $deployImageAction,
|
||||
private readonly CreateImageAction $createImageAction,
|
||||
private readonly PartitionAssistantAction $partitionAssistantAction,
|
||||
)
|
||||
{
|
||||
parent::__construct();
|
||||
|
@ -38,47 +50,39 @@ class RunScheduledCommandTasksCommand extends Command
|
|||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$now = new \DateTimeImmutable('now +2 hours');
|
||||
$now->setTimezone(new \DateTimeZone('Europe/Madrid'));
|
||||
$nowMinute = $now->format('Y-m-d H:i');
|
||||
$now = new \DateTimeImmutable('now');
|
||||
|
||||
$tasks = $this->commandTaskRepository->findAll();
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
/** @var CommandTask $task */
|
||||
$nextExecution = $task->getNextExecution();
|
||||
|
||||
if (!$nextExecution) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$taskMinute = $nextExecution->format('Y-m-d H:i');
|
||||
$difference = $now->getTimestamp() - $nextExecution->getTimestamp();
|
||||
|
||||
if ($taskMinute === $nowMinute) {
|
||||
$output->writeln("Now: " . $now->format('Y-m-d H:i:s T'));
|
||||
$output->writeln("NextExecution: " . $nextExecution->format('Y-m-d H:i:s T'));
|
||||
$output->writeln("Diferencia: $difference segundos para la tarea {$task->getName()}");
|
||||
|
||||
if (abs($difference) < 30) {
|
||||
$output->writeln("Ejecutando tarea: " . $task->getName());
|
||||
|
||||
$scripts = $task->getCommandTaskScripts()->toArray();
|
||||
usort($scripts, fn($a, $b) => $a->getExecutionOrder() <=> $b->getExecutionOrder());
|
||||
|
||||
foreach ($scripts as $script) {
|
||||
$output->writeln(" - Ejecutando script de tipo {$script->getType()} con orden {$script->getExecutionOrder()}");
|
||||
$output->writeln(" - Creando traza para script de tipo {$script->getType()} con orden {$script->getExecutionOrder()}");
|
||||
|
||||
if ($script->getType() === 'run-script') {
|
||||
$input = new CommandExecuteInput();
|
||||
|
||||
foreach ($task->getOrganizationalUnit()?->getClients() as $client) {
|
||||
if ($client->getStatus() !== ClientStatus::OG_LIVE) {
|
||||
continue;
|
||||
}
|
||||
$input->clients[] = new ClientOutput($client);
|
||||
}
|
||||
$input->script = $script->getContent();
|
||||
|
||||
$this->runScriptAction->__invoke($input);
|
||||
}
|
||||
$this->createTraceForScript($script, $task, $output);
|
||||
}
|
||||
|
||||
$task->setLastExecution(new \DateTime());
|
||||
$task->setLastExecution(new \DateTimeImmutable('now'));
|
||||
$task->setNextExecution($task->calculateNextExecutionDate());
|
||||
|
||||
$this->entityManager->persist($task);
|
||||
}
|
||||
}
|
||||
|
@ -86,4 +90,134 @@ class RunScheduledCommandTasksCommand extends Command
|
|||
$this->entityManager->flush();
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function createTraceForScript($script, CommandTask $task, OutputInterface $output): void
|
||||
{
|
||||
$scriptParameters = $script->getParameters();
|
||||
|
||||
if ($task->getScope() !== 'clients') {
|
||||
$clients = $task->getOrganizationalUnit()?->getClients();
|
||||
} else {
|
||||
$clients = $task->getClients();
|
||||
}
|
||||
|
||||
foreach ($clients as $client) {
|
||||
$trace = new Trace();
|
||||
$trace->setClient($client);
|
||||
$trace->setStatus(TraceStatus::PENDING);
|
||||
$trace->setExecutedAt(new \DateTime());
|
||||
|
||||
switch ($script->getType()) {
|
||||
case 'run-script':
|
||||
$trace->setCommand(CommandTypes::RUN_SCRIPT);
|
||||
$trace->setInput([
|
||||
'script' => $script->getContent()
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'deploy-image':
|
||||
$trace->setCommand(CommandTypes::DEPLOY_IMAGE);
|
||||
$trace->setInput([
|
||||
'imageImageRepository' => $scriptParameters['imageImageRepositoryUuid'],
|
||||
'method' => $scriptParameters['method'] ?? 'unicast',
|
||||
'type' => $scriptParameters['type'] ?? 'monolithic',
|
||||
'diskNumber' => $scriptParameters['diskNumber'] ?? 1,
|
||||
'partitionNumber' => $scriptParameters['partitionNumber'] ?? 1,
|
||||
'mcastMode' => $scriptParameters['mcastMode'] ?? 'duplex',
|
||||
'mcastSpeed' => $scriptParameters['mcastSpeed'] ?? 100,
|
||||
'mcastPort' => $scriptParameters['mcastPort'] ?? 8000,
|
||||
'mcastIp' => $scriptParameters['mcastIp'] ?? '224.0.0.1',
|
||||
'maxClients' => $scriptParameters['maxClients'] ?? 10,
|
||||
'maxTime' => $scriptParameters['maxTime'] ?? 3600,
|
||||
'p2pMode' => $scriptParameters['p2pMode'] ?? 'seed',
|
||||
'p2pTime' => $scriptParameters['p2pTime'] ?? 300
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'create-image':
|
||||
$trace->setCommand(CommandTypes::CREATE_IMAGE);
|
||||
$trace->setInput([
|
||||
'image' => $scriptParameters['imageUuid'],
|
||||
'diskNumber' => $scriptParameters['diskNumber'] ?? null,
|
||||
'partitionNumber' => $scriptParameters['partitionNumber'] ?? null,
|
||||
'gitRepositoryName' => $scriptParameters['gitRepositoryName'] ?? null
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'partition-assistant':
|
||||
$trace->setCommand(CommandTypes::PARTITION_AND_FORMAT);
|
||||
$trace->setInput($scriptParameters);
|
||||
break;
|
||||
|
||||
default:
|
||||
$output->writeln(" - Tipo de script no soportado: {$script->getType()}");
|
||||
continue 2; // Salta al siguiente cliente
|
||||
}
|
||||
|
||||
$this->entityManager->persist($trace);
|
||||
$output->writeln(" - Traza creada para cliente {$client->getUuid()} con comando {$trace->getCommand()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function executePartitionAssistant($script, CommandTask $task, OutputInterface $output): void
|
||||
{
|
||||
$scriptParameters = $script->getParameters();
|
||||
|
||||
$output->writeln(" - Debug: Parameters = " . ($scriptParameters ? json_encode($scriptParameters) : 'null'));
|
||||
|
||||
if (!$scriptParameters) {
|
||||
$output->writeln(" - Error: Parámetros del script vacíos o nulos");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_array($scriptParameters)) {
|
||||
$output->writeln(" - Error: Los parámetros deben ser un array");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($task->getOrganizationalUnit()?->getClients() as $client) {
|
||||
$partitionInput = new PartitionPostInput();
|
||||
$partitionInput->clients = [new ClientOutput($client)];
|
||||
|
||||
$partitions = [];
|
||||
|
||||
foreach ($scriptParameters as $partitionData) {
|
||||
if (isset($partitionData['removed']) && $partitionData['removed']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($partitionData['size']) || $partitionData['size'] <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$partitionInputObj = new PartitionInput();
|
||||
$partitionInputObj->diskNumber = $partitionData['diskNumber'] ?? 1;
|
||||
$partitionInputObj->partitionNumber = $partitionData['partitionNumber'] ?? 1;
|
||||
$partitionInputObj->partitionCode = $partitionData['partitionCode'] ?? 'LINUX';
|
||||
$partitionInputObj->size = (float)($partitionData['size'] / 1024);
|
||||
$partitionInputObj->filesystem = $partitionData['filesystem'] ?? 'EXT4';
|
||||
$partitionInputObj->format = $partitionData['format'] ?? false;
|
||||
$partitionInputObj->memoryUsage = $partitionData['memoryUsage'] ?? 0;
|
||||
|
||||
$partitions[] = $partitionInputObj;
|
||||
}
|
||||
|
||||
if (empty($partitions)) {
|
||||
$output->writeln(" - Warning: No hay particiones válidas para procesar");
|
||||
continue;
|
||||
}
|
||||
|
||||
$partitionInput->partitions = $partitions;
|
||||
$partitionInput->queue = false;
|
||||
|
||||
try {
|
||||
$response = $this->partitionAssistantAction->__invoke($partitionInput, null);
|
||||
|
||||
$output->writeln(" - Partition assistant iniciado para cliente {$client->getUuid()}");
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln(" - Error en partition assistant para cliente {$client->getUuid()}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class CreateImageAction extends AbstractOgAgentController
|
|||
* @throws RedirectionExceptionInterface
|
||||
* @throws ClientExceptionInterface
|
||||
*/
|
||||
public function __invoke(bool $queue, Image $image, ?Partition $partition = null, ?Client $client = null, ?string $gitRepositoryName = null): JsonResponse
|
||||
public function __invoke(bool $queue, Image $image, ?Partition $partition = null, ?Client $client = null, ?string $gitRepositoryName = null, ?Trace $existingTrace = null): JsonResponse
|
||||
{
|
||||
$client = $client ?? $image->getClient();
|
||||
|
||||
|
@ -77,11 +77,11 @@ class CreateImageAction extends AbstractOgAgentController
|
|||
|
||||
$this->entityManager->persist($imageImageRepository);
|
||||
|
||||
return $this->createMonolithicImage($imageImageRepository, $partitionInfo, $image, $repository, $client, $queue);
|
||||
return $this->createMonolithicImage($imageImageRepository, $partitionInfo, $image, $repository, $client, $queue, $existingTrace);
|
||||
} else {
|
||||
$repository = $image->getClient()->getRepository();
|
||||
|
||||
return $this->createGitImage($image, $partitionInfo, $repository, $queue, $gitRepositoryName);
|
||||
return $this->createGitImage($image, $partitionInfo, $repository, $queue, $gitRepositoryName, $existingTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,8 @@ class CreateImageAction extends AbstractOgAgentController
|
|||
Image $image,
|
||||
ImageRepository $repository,
|
||||
?Client $client = null,
|
||||
bool $queue = false
|
||||
bool $queue = false,
|
||||
?Trace $existingTrace = null
|
||||
): JsonResponse
|
||||
{
|
||||
if (!isset($partitionInfo['numDisk'], $partitionInfo['numPartition'], $partitionInfo['partitionCode'], $partitionInfo['filesystem'])) {
|
||||
|
@ -157,6 +158,8 @@ class CreateImageAction extends AbstractOgAgentController
|
|||
'image' => $image->getUuid(),
|
||||
'partitionCode' => $partitionInfo['partitionCode'],
|
||||
'partitionType' => $partitionInfo['filesystem'],
|
||||
'partitionNumber' => $partitionInfo['numPartition'],
|
||||
'diskNumber' => $partitionInfo['numDisk'],
|
||||
'repository' => $repository->getIp(),
|
||||
'name' => $image->getName().'_v'.$imageImageRepository->getVersion(),
|
||||
];
|
||||
|
@ -188,17 +191,27 @@ class CreateImageAction extends AbstractOgAgentController
|
|||
'image' => $image->getUuid(),
|
||||
'partitionCode' => $partitionInfo['partitionCode'],
|
||||
'partitionType' => $partitionInfo['filesystem'],
|
||||
'partitionNumber' => $partitionInfo['numPartition'],
|
||||
'diskNumber' => $partitionInfo['numDisk'],
|
||||
'repository' => $repository->getIp(),
|
||||
'name' => $image->getName().'_v'.$imageImageRepository->getVersion(),
|
||||
];
|
||||
|
||||
$this->createService->__invoke(
|
||||
$image->getClient(),
|
||||
CommandTypes::CREATE_IMAGE,
|
||||
TraceStatus::IN_PROGRESS,
|
||||
$jobId,
|
||||
$inputData
|
||||
);
|
||||
if ($existingTrace) {
|
||||
$existingTrace->setStatus(TraceStatus::IN_PROGRESS);
|
||||
$existingTrace->setJobId($jobId);
|
||||
$existingTrace->setInput($inputData);
|
||||
$this->entityManager->persist($existingTrace);
|
||||
$this->entityManager->flush();
|
||||
} else {
|
||||
$this->createService->__invoke(
|
||||
$image->getClient(),
|
||||
CommandTypes::CREATE_IMAGE,
|
||||
TraceStatus::IN_PROGRESS,
|
||||
$jobId,
|
||||
$inputData
|
||||
);
|
||||
}
|
||||
|
||||
return new JsonResponse(data: $image, status: Response::HTTP_OK);
|
||||
} catch (Exception $e) {
|
||||
|
@ -226,7 +239,8 @@ class CreateImageAction extends AbstractOgAgentController
|
|||
array $partitionInfo,
|
||||
ImageRepository $repository,
|
||||
bool $queue = false,
|
||||
?string $gitRepositoryName = null
|
||||
?string $gitRepositoryName = null,
|
||||
?Trace $existingTrace = null
|
||||
): JsonResponse
|
||||
{
|
||||
if (!isset($partitionInfo['numDisk'], $partitionInfo['numPartition'], $partitionInfo['partitionCode'], $partitionInfo['filesystem'])) {
|
||||
|
@ -283,6 +297,8 @@ class CreateImageAction extends AbstractOgAgentController
|
|||
'image' => $image->getUuid(),
|
||||
'partitionCode' => $partitionInfo['partitionCode'],
|
||||
'partitionType' => $partitionInfo['filesystem'],
|
||||
'partitionNumber' => $partitionInfo['numPartition'],
|
||||
'diskNumber' => $partitionInfo['numDisk'],
|
||||
'repository' => $repository->getIp(),
|
||||
'name' => $image->getName(),
|
||||
];
|
||||
|
@ -313,17 +329,27 @@ class CreateImageAction extends AbstractOgAgentController
|
|||
'image' => $image->getUuid(),
|
||||
'partitionCode' => $partitionInfo['partitionCode'],
|
||||
'partitionType' => $partitionInfo['filesystem'],
|
||||
'partitionNumber' => $partitionInfo['numPartition'],
|
||||
'diskNumber' => $partitionInfo['numDisk'],
|
||||
'repository' => $repository->getIp(),
|
||||
'name' => $image->getName(),
|
||||
];
|
||||
|
||||
$this->createService->__invoke(
|
||||
$image->getClient(),
|
||||
CommandTypes::CREATE_IMAGE_GIT,
|
||||
TraceStatus::IN_PROGRESS,
|
||||
$jobId,
|
||||
$inputData
|
||||
);
|
||||
if ($existingTrace) {
|
||||
$existingTrace->setStatus(TraceStatus::IN_PROGRESS);
|
||||
$existingTrace->setJobId($jobId);
|
||||
$existingTrace->setInput($inputData);
|
||||
$this->entityManager->persist($existingTrace);
|
||||
$this->entityManager->flush();
|
||||
} else {
|
||||
$this->createService->__invoke(
|
||||
$image->getClient(),
|
||||
CommandTypes::CREATE_IMAGE_GIT,
|
||||
TraceStatus::IN_PROGRESS,
|
||||
$jobId,
|
||||
$inputData
|
||||
);
|
||||
}
|
||||
|
||||
return new JsonResponse(data: $image, status: Response::HTTP_OK);
|
||||
} catch (Exception $e) {
|
||||
|
|
|
@ -37,7 +37,7 @@ class PartitionAssistantAction extends AbstractOgAgentController
|
|||
* @throws RedirectionExceptionInterface
|
||||
* @throws ClientExceptionInterface
|
||||
*/
|
||||
public function __invoke(PartitionPostInput $input): JsonResponse
|
||||
public function __invoke(PartitionPostInput $input, ?Trace $existingTrace = null): JsonResponse
|
||||
{
|
||||
$partitions = $input->partitions;
|
||||
|
||||
|
@ -125,7 +125,15 @@ class PartitionAssistantAction extends AbstractOgAgentController
|
|||
$this->entityManager->persist($client);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->createService->__invoke($client, CommandTypes::PARTITION_AND_FORMAT, TraceStatus::IN_PROGRESS, $jobId, $data);
|
||||
if ($existingTrace) {
|
||||
$existingTrace->setStatus(TraceStatus::IN_PROGRESS);
|
||||
$existingTrace->setJobId($jobId);
|
||||
$existingTrace->setInput($data);
|
||||
$this->entityManager->persist($existingTrace);
|
||||
$this->entityManager->flush();
|
||||
} else {
|
||||
$this->createService->__invoke($client, CommandTypes::PARTITION_AND_FORMAT, TraceStatus::IN_PROGRESS, $jobId, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Controller\OgAgent;
|
|||
use App\Dto\Input\CommandExecuteInput;
|
||||
use App\Dto\Input\MultipleClientsInput;
|
||||
use App\Entity\Client;
|
||||
use App\Entity\Trace;
|
||||
use App\Model\ClientStatus;
|
||||
use App\Model\CommandTypes;
|
||||
use App\Model\TraceStatus;
|
||||
|
@ -30,8 +31,10 @@ class RunScriptAction extends AbstractOgAgentController
|
|||
* @throws RedirectionExceptionInterface
|
||||
* @throws ClientExceptionInterface
|
||||
*/
|
||||
public function __invoke(CommandExecuteInput $input): JsonResponse
|
||||
public function __invoke(CommandExecuteInput $input, ?Trace $existingTrace = null): JsonResponse
|
||||
{
|
||||
$clientJobs = [];
|
||||
|
||||
/** @var Client $clientEntity */
|
||||
foreach ($input->clients as $clientEntity) {
|
||||
/** @var Client $client */
|
||||
|
@ -41,49 +44,111 @@ class RunScriptAction extends AbstractOgAgentController
|
|||
throw new BadRequestHttpException('IP is required');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'nfn' => 'EjecutarScript',
|
||||
'scp' => base64_encode($input->script),
|
||||
'ids' => '0'
|
||||
];
|
||||
$data = $this->buildRequestData($client, $input->script);
|
||||
$response = $this->executeScript($client, $data);
|
||||
|
||||
$response = $this->createRequest(
|
||||
method: 'POST',
|
||||
url: 'https://'.$client->getIp().':8000/opengnsys/EjecutarScript',
|
||||
params: [
|
||||
'json' => $data,
|
||||
],
|
||||
token: $client->getToken(),
|
||||
);
|
||||
|
||||
if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
|
||||
$this->logger->error('Error running script', ['client' => $client->getId(), 'error' => $response['error']]);
|
||||
|
||||
if ($input->queue) {
|
||||
$inputData = [
|
||||
'script' => $input->script,
|
||||
];
|
||||
$this->createService->__invoke($client, CommandTypes::RUN_SCRIPT, TraceStatus::PENDING, null, $inputData);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->isErrorResponse($response)) {
|
||||
$this->handleError($client, $input, $response);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->logger->info('Powering off client', ['client' => $client->getId()]);
|
||||
|
||||
$jobId = $response['job_id'];
|
||||
$clientJobs[(string) $client->get] = $jobId;
|
||||
|
||||
$this->entityManager->persist($client);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$inputData = [
|
||||
'script' => $input->script,
|
||||
];
|
||||
|
||||
$this->createService->__invoke($client, CommandTypes::RUN_SCRIPT, TraceStatus::IN_PROGRESS, $jobId, $inputData);
|
||||
$this->handleSuccess($client, $input, $response, $existingTrace);
|
||||
}
|
||||
|
||||
return new JsonResponse(data: [], status: Response::HTTP_OK);
|
||||
return new JsonResponse(data: $clientJobs, status: Response::HTTP_OK);
|
||||
}
|
||||
|
||||
private function buildRequestData(Client $client, string $script): array
|
||||
{
|
||||
$scriptBase64 = base64_encode($script);
|
||||
|
||||
if ($this->isLinuxOrWindows($client)) {
|
||||
return [
|
||||
'script' => $scriptBase64,
|
||||
'client' => 'false'
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->isLinuxOrWindowsSession($client)) {
|
||||
return [
|
||||
'script' => $scriptBase64,
|
||||
'client' => 'true'
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'nfn' => 'EjecutarScript',
|
||||
'scp' => $scriptBase64,
|
||||
'ids' => '0'
|
||||
];
|
||||
}
|
||||
|
||||
private function isLinuxOrWindows(Client $client): bool
|
||||
{
|
||||
return $client->getStatus() === ClientStatus::LINUX || $client->getStatus() === ClientStatus::WINDOWS;
|
||||
}
|
||||
|
||||
private function isLinuxOrWindowsSession(Client $client): bool
|
||||
{
|
||||
return $client->getStatus() === ClientStatus::LINUX_SESSION || $client->getStatus() === ClientStatus::WINDOWS_SESSION;
|
||||
}
|
||||
|
||||
private function executeScript(Client $client, array $data): array
|
||||
{
|
||||
return $this->createRequest(
|
||||
method: 'POST',
|
||||
url: 'https://'.$client->getIp().':8000/opengnsys/EjecutarScript',
|
||||
params: [
|
||||
'json' => $data,
|
||||
],
|
||||
token: $client->getToken(),
|
||||
);
|
||||
}
|
||||
|
||||
private function isErrorResponse(array $response): bool
|
||||
{
|
||||
return isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
private function handleError(Client $client, CommandExecuteInput $input, array $response): void
|
||||
{
|
||||
$this->logger->error('Error running script', [
|
||||
'client' => $client->getId(),
|
||||
'error' => $response['error']
|
||||
]);
|
||||
|
||||
if ($input->queue) {
|
||||
$inputData = ['script' => $input->script];
|
||||
$this->createService->__invoke($client, CommandTypes::RUN_SCRIPT, TraceStatus::PENDING, null, $inputData);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleSuccess(Client $client, CommandExecuteInput $input, array $response, ?Trace $existingTrace): void
|
||||
{
|
||||
$this->logger->info('Running script on client', ['client' => $client->getId()]);
|
||||
|
||||
$jobId = $response['job_id'];
|
||||
$inputData = ['script' => $input->script];
|
||||
|
||||
$this->entityManager->persist($client);
|
||||
$this->entityManager->flush();
|
||||
|
||||
if ($existingTrace) {
|
||||
$this->updateExistingTrace($existingTrace, $jobId, $inputData);
|
||||
} else {
|
||||
$this->createService->__invoke($client, CommandTypes::RUN_SCRIPT, TraceStatus::IN_PROGRESS, $jobId, $inputData);
|
||||
}
|
||||
}
|
||||
|
||||
private function updateExistingTrace(Trace $trace, string $jobId, array $inputData): void
|
||||
{
|
||||
$trace->setStatus(TraceStatus::IN_PROGRESS);
|
||||
$trace->setJobId($jobId);
|
||||
$trace->setInput($inputData);
|
||||
$this->entityManager->persist($trace);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
}
|
|
@ -57,6 +57,8 @@ class AgentSessionController extends AbstractController
|
|||
return new JsonResponse(['message' => 'Invalid status'], Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$client->setToken($data['secret'] ?? null);
|
||||
|
||||
$this->entityManager->persist($client);
|
||||
$this->entityManager->flush();
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ class StatusController extends AbstractController
|
|||
{
|
||||
const string CREATE_IMAGE = 'RESPUESTA_CrearImagen';
|
||||
const string CREATE_IMAGE_GIT = 'RESPUESTA_CrearImagenGit';
|
||||
const string UPDATE_IMAGE_GIT = 'RESPUESTA_ModificarImagenGit';
|
||||
const string UPDATE_IMAGE_GIT = 'RESPUESTA_ActualizarImagenGit';
|
||||
const string RESTORE_IMAGE = 'RESPUESTA_RestaurarImagen';
|
||||
const string RESTORE_IMAGE_GIT = 'RESPUESTA_RestaurarImagenGit';
|
||||
const string CONFIGURE_IMAGE = 'RESPUESTA_Configurar';
|
||||
|
|
|
@ -69,8 +69,9 @@ final class CommandTaskInput
|
|||
$this->clients[] = new ClientOutput($client);
|
||||
}
|
||||
}
|
||||
|
||||
$this->organizationalUnit = new OrganizationalUnitOutput($commandTask->getOrganizationalUnit());
|
||||
if ($commandTask->getOrganizationalUnit()) {
|
||||
$this->organizationalUnit = new OrganizationalUnitOutput($commandTask->getOrganizationalUnit());
|
||||
}
|
||||
$this->notes = $commandTask->getNotes();
|
||||
$this->scope = $commandTask->getScope();
|
||||
$this->content = $commandTask->getParameters();
|
||||
|
@ -87,12 +88,15 @@ final class CommandTaskInput
|
|||
|
||||
$commandTask->setName($this->name);
|
||||
|
||||
$clientsToAdd = [];
|
||||
foreach ($this->clients as $client) {
|
||||
$clientsToAdd[] = $client->getEntity();
|
||||
}
|
||||
}
|
||||
|
||||
$commandTask->setOrganizationalUnit($this->organizationalUnit->getEntity());
|
||||
$commandTask->setClients( $clientsToAdd ?? [] );
|
||||
if ($this->organizationalUnit) {
|
||||
$commandTask->setOrganizationalUnit($this->organizationalUnit->getEntity());
|
||||
}
|
||||
$commandTask->setClients( $clientsToAdd );
|
||||
$commandTask->setNotes($this->notes);
|
||||
$commandTask->setParameters($this->content);
|
||||
$commandTask->setScope($this->scope);
|
||||
|
|
|
@ -16,53 +16,53 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
|
||||
final class PartitionInput
|
||||
{
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
public ?UuidInterface $uuid = null;
|
||||
|
||||
#[Groups(['partition:write'])]
|
||||
public ?bool $removed = null;
|
||||
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
public ?bool $format = null;
|
||||
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
#[ApiProperty(description: 'The disk number of the partition', example: 1)]
|
||||
public ?int $diskNumber = null;
|
||||
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
#[ApiProperty(description: 'The number of the partition', example: 1)]
|
||||
public ?int $partitionNumber = null;
|
||||
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
#[ApiProperty(description: 'The code of the partition', example: "code")]
|
||||
public ?string $partitionCode = null;
|
||||
|
||||
#[Assert\NotNull()]
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
#[ApiProperty(description: 'The size of the partition', example: 100)]
|
||||
public ?float $size = null;
|
||||
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
#[ApiProperty(description: 'The cache content of the partition', example: "cache content")]
|
||||
public ?string $cacheContent = null;
|
||||
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
#[ApiProperty(description: 'The type of the partition', example: "LINUX")]
|
||||
public ?string $type = null;
|
||||
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
#[ApiProperty(description: 'The filesystem of the partition', example: "EXT4")]
|
||||
public ?string $filesystem = null;
|
||||
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
#[ApiProperty(description: 'The operative system name of the partition', example: "Ubuntu")]
|
||||
public ?OperativeSystemOutput $operativeSystem = null;
|
||||
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
#[ApiProperty(description: 'The memory usage of the partition', example: 100)]
|
||||
public ?int $memoryUsage = null;
|
||||
|
||||
#[Groups(['partition:write'])]
|
||||
#[Groups(['partition:write', 'client:write'])]
|
||||
#[ApiProperty(description: 'The image of the partition')]
|
||||
public ?ImageOutput $image = null;
|
||||
|
||||
|
|
|
@ -50,7 +50,9 @@ final class CommandTaskOutput extends AbstractOutput
|
|||
fn(Client $client) => new ClientOutput($client)
|
||||
)->toArray();
|
||||
|
||||
$this->organizationalUnit = new OrganizationalUnitOutput($commandTask->getOrganizationalUnit());
|
||||
if ($commandTask->getOrganizationalUnit()) {
|
||||
$this->organizationalUnit = new OrganizationalUnitOutput($commandTask->getOrganizationalUnit());
|
||||
}
|
||||
$this->notes = $commandTask->getNotes();
|
||||
$this->scope = $commandTask->getScope();
|
||||
$this->lastExecution = $commandTask->getLastExecution();
|
||||
|
|
|
@ -73,6 +73,7 @@ class Client extends AbstractEntity
|
|||
#[ORM\ManyToOne(inversedBy: 'clients')]
|
||||
#[ORM\JoinColumn( onDelete: 'SET NULL')]
|
||||
private ?Subnet $subnet = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'clients')]
|
||||
#[ORM\JoinColumn( onDelete: 'SET NULL')]
|
||||
private ?OgLive $ogLive = null;
|
||||
|
|
Loading…
Reference in New Issue