refs #2252. Queue actions. Empty queue and logging actions
testing/ogcore-api/pipeline/head This commit looks good Details

pull/37/head
Manuel Aranda Rosales 2025-06-26 17:10:44 +02:00
parent 7b74c9ab70
commit 21cd73bee2
17 changed files with 170 additions and 25 deletions

View File

@ -44,19 +44,16 @@ when@prod:
level: error
formatter: App\Formatter\CustomLineFormatter
channels: ["!event"]
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine"]
syslog:
type: syslog
ident: "ogcore"
level: info
level: error
formatter: App\Formatter\CustomLineFormatter
channels: ["!event"]
deprecation:
type: stream
channels: [deprecation]
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: error
formatter: monolog.formatter.json

View File

@ -36,7 +36,7 @@ class CheckClientAvailability extends Command
$threshold = (new \DateTime())->modify(' - '.self::THRESHOLD_MINUTES . ' minutes');
$startQueryTime = microtime(true);
$validStatuses = [ClientStatus::OG_LIVE, ClientStatus::WINDOWS, ClientStatus::LINUX, ClientStatus::MACOS];
$validStatuses = [ClientStatus::OG_LIVE, ClientStatus::WINDOWS, ClientStatus::LINUX, ClientStatus::MACOS, ClientStatus::INITIALIZING, ClientStatus::LINUX_SESSION, ClientStatus::WINDOWS_SESSION];
$query = $this->entityManager->createQuery(
'UPDATE App\Entity\Client c

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Entity\Trace;
use App\Model\TraceStatus;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(name: 'opengnsys:execute-pending-traces', description: 'Execute pending traces')]
class ExecutePendingTracesCommand extends Command
{
public function __construct(
private readonly EntityManagerInterface $entityManager
) {
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$startTime = microtime(true);
$traces = $this->entityManager->getRepository(Trace::class)
->findBy(['status' => TraceStatus::PENDING]);
$count = count($traces);
$io->info("Found $count pending traces");
foreach ($traces as $trace) {
$trace->setStatus(TraceStatus::IN_PROGRESS);
$this->entityManager->persist($trace);
}
$this->entityManager->flush();
$executionTime = microtime(true) - $startTime;
$io->success("Updated $count traces to IN_PROGRESS status");
$io->note("Execution time: " . round($executionTime, 3) . "s");
return Command::SUCCESS;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace App\Controller;
use App\Dto\Input\CancelMultipleTracesInput;
use App\Entity\Trace;
use App\Model\TraceStatus;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
class CancelMultipleTracesAction extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
)
{
}
public function __invoke(CancelMultipleTracesInput $input): JsonResponse
{
foreach ($input->traces as $trace) {
/** @var Trace $trace */
$trace = $trace->getEntity();
$trace->setStatus(TraceStatus::CANCELLED);
$this->entityManager->persist($trace);
}
$this->entityManager->flush();
return new JsonResponse(data: 'Traces cancelled successfully', status: Response::HTTP_OK);
}
}

View File

@ -53,7 +53,8 @@ class LoginAction extends AbstractOgAgentController
}
if ($client->getStatus() !== ClientStatus::OG_LIVE) {
throw new BadRequestHttpException('Client is not in OG_LIVE status');
$this->createService->__invoke($client, CommandTypes::LOGIN, TraceStatus::PENDING, null, []);
continue;
}
$data = [
@ -73,7 +74,8 @@ class LoginAction extends AbstractOgAgentController
);
if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
throw new BadRequestHttpException('Error logging in: '.$response['error']);
$this->createService->__invoke($client, CommandTypes::LOGIN, TraceStatus::PENDING, null, []);
continue;
}
$this->logger->info('Login client', ['client' => $client->getId()]);

View File

@ -111,7 +111,12 @@ class PartitionAssistantAction extends AbstractOgAgentController
$this->logger->info('Partition assistant', ['client' => $client->getId()]);
if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
throw new BadRequestHttpException('Error occurred while partitioning');
if ($input->queue) {
$this->createService->__invoke($client, CommandTypes::PARTITION_AND_FORMAT, TraceStatus::PENDING, null, $data);
continue;
}
continue;
}
$jobId = $response['job_id'];

View File

@ -47,12 +47,10 @@ class PowerOffAction extends AbstractOgAgentController
throw new BadRequestHttpException('IP is required');
}
if ($client->getStatus() === ClientStatus::OFF) {
if ($client->getStatus() === ClientStatus::OFF || $client->getStatus() === ClientStatus::TURNING_OFF || $client->getStatus() === ClientStatus::DISCONNECTED) {
continue;
}
$endpoint = $client->getStatus() === ClientStatus::OG_LIVE ? 'opengnsys/Apagar' : 'opengnsys/poweroff';
$data = [
'nfn' => 'Apagar',
'ids' => '0'
@ -60,7 +58,7 @@ class PowerOffAction extends AbstractOgAgentController
$response = $this->createRequest(
method: 'POST',
url: 'https://'.$client->getIp().':8000/'.$endpoint,
url: 'https://'.$client->getIp().':8000/opengnsys/Apagar',
params: [
'json' => $data,
],
@ -68,7 +66,14 @@ class PowerOffAction extends AbstractOgAgentController
);
if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
throw new BadRequestHttpException('Error deploying image');
$this->logger->error('Error powering off client', ['client' => $client->getId(), 'error' => $response['error']]);
if ($input->queue) {
$this->createService->__invoke($client, CommandTypes::SHUTDOWN, TraceStatus::PENDING, null, []);
continue;
}
continue;
}
$this->logger->info('Powering off client', ['client' => $client->getId()]);

View File

@ -47,8 +47,6 @@ class RebootAction extends AbstractOgAgentController
throw new BadRequestHttpException('IP is required');
}
$endpoint = $client->getStatus() === ClientStatus::OG_LIVE ? 'opengnsys/Reiniciar' : '/opengnsys/reboot';
$data = [
'nfn' => 'Reiniciar',
'ids' => '0'
@ -56,7 +54,7 @@ class RebootAction extends AbstractOgAgentController
$response = $this->createRequest(
method: 'POST',
url: 'https://'.$client->getIp().':8000/'.$endpoint,
url: 'https://'.$client->getIp().':8000/opengnsys/Reiniciar',
params: [
'json' => $data,
],
@ -64,7 +62,12 @@ class RebootAction extends AbstractOgAgentController
);
if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
throw new BadRequestHttpException('Error deploying image');
if ($input->queue) {
$this->createService->__invoke($client, CommandTypes::REBOOT, TraceStatus::PENDING, null, []);
continue;
}
continue;
}
$this->logger->info('Rebooting client', ['client' => $client->getId()]);

View File

@ -43,10 +43,11 @@ class RemoveCacheImageAction extends AbstractOgAgentController
}
if ($client->getStatus() !== ClientStatus::OG_LIVE) {
throw new BadRequestHttpException('Client is not in OG_LIVE status');
$this->createService->__invoke($client, CommandTypes::REMOVE_CACHE_IMAGE, TraceStatus::PENDING, null, []);
continue;
}
$script = `rm%20-r%20/opt/opengnsys/cache/opt/opengnsys/images/{$partition->getImage()->getName()}.*@'`;
$script = 'rm -r /opt/opengnsys/cache/opt/opengnsys/images/' . $partition->getImage()->getName() . '.*';
$data = [
'nfn' => 'EjecutarScript',
@ -64,9 +65,13 @@ class RemoveCacheImageAction extends AbstractOgAgentController
);
if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
throw new BadRequestHttpException('Error logging in: '.$response['error']);
if ($input->queue) {
$this->createService->__invoke($client, CommandTypes::REMOVE_CACHE_IMAGE, TraceStatus::PENDING, null, []);
continue;
}
continue;
}
$this->logger->info('Login client', ['client' => $client->getId()]);
$jobId = $response['job_id'];

View File

@ -57,7 +57,17 @@ class RunScriptAction extends AbstractOgAgentController
);
if (isset($response['error']) && $response['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
throw new BadRequestHttpException('Error deploying image');
$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;
}
continue;
}
$this->logger->info('Powering off client', ['client' => $client->getId()]);

View File

@ -35,6 +35,7 @@ class DeployImageAction extends AbstractOgRepositoryController
$params = [
'json' => [
'ID_img' => $data->getImageFullsum(),
//'image_name' => $data->getName(),
'bitrate' => (string) $input->mcastSpeed.'M',
'ip' => $input->mcastIp,
'port' => $input->mcastPort,
@ -54,6 +55,11 @@ class DeployImageAction extends AbstractOgRepositoryController
$content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/'.$type, $params);
// Verificar si la respuesta contiene un error HTTP 500
if (isset($content['code']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
throw new \Exception('Error del servidor OgRepository: ' . ($content['error'] ?? 'Error desconocido') . ' - ' . ($content['details'] ?? ''));
}
return new JsonResponse(data: [], status: Response::HTTP_OK);
}
}

View File

@ -62,6 +62,8 @@ class TransferAction extends AbstractOgRepositoryController
$inputData = [
'imageName' => $image->getName(),
'imageVersion' => $imageImageRepository->getVersion(),
'imageCompleteName' => $imageImageRepository->getName(),
'imageUuid' => $image->getUuid(),
'imageImageRepositoryUuid' => $imageImageRepository->getUuid(),
'repositoryUuid' => $repository->getUuid(),

View File

@ -92,9 +92,9 @@ class ResponseController extends AbstractOgRepositoryController
$latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersionByImageAndRepository($image, $repository);
$newImageRepo = new ImageImageRepository();
$newImageRepo->setName($image->getName().'_v'.($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1));
$newImageRepo->setName($originImageImageRepository->getName());
$newImageRepo->setImage($image);
$newImageRepo->setVersion($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1);
$newImageRepo->setVersion($originImageImageRepository->getVersion());
$newImageRepo->setRepository($repository);
$newImageRepo->setStatus(ImageStatus::SUCCESS);

View File

@ -0,0 +1,17 @@
<?php
namespace App\Dto\Input;
use App\Dto\Output\TraceOutput;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
final class CancelMultipleTracesInput
{
/**
* @var TraceOutput[]
*/
#[Assert\NotNull]
#[Groups(['trace:write'])]
public array $traces = [];
}

View File

@ -19,4 +19,7 @@ final class CommandExecuteInput
#[Assert\NotNull]
#[Groups(['command:write'])]
public string $script = '';
#[Groups(['command:write'])]
public bool $queue = false;
}

View File

@ -12,5 +12,8 @@ final class MultipleClientsInput
*/
#[Groups(['client:write'])]
public array $clients = [];
#[Groups(['client:write'])]
public bool $queue = false;
}

View File

@ -18,5 +18,8 @@ final class PartitionPostInput
*/
#[Groups(['partition:write'])]
public array $clients = [];
#[Groups(['partition:write'])]
public bool $queue = false;
}