refs #1851. Added script/funcionality to check PC availability
testing/ogcore-api/pipeline/head There was a failure building this commit Details

pull/27/head
Manuel Aranda Rosales 2025-04-08 15:34:25 +02:00
parent b6c62996f5
commit 097c6a710e
8 changed files with 189 additions and 52 deletions

View File

@ -0,0 +1,33 @@
<?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 Version20250407154425 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('CREATE INDEX IDX_STATUS ON client (status)');
$this->addSql('CREATE INDEX IDX_UPDATED_AT ON client (updated_at)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP INDEX IDX_STATUS ON client');
$this->addSql('DROP INDEX IDX_UPDATED_AT ON client');
}
}

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 Version20250407154620 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('CREATE INDEX IDX_STATUS_UPDATED_AT ON client (status, updated_at)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP INDEX IDX_STATUS_UPDATED_AT ON client');
}
}

View File

@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Entity\Client;
use App\Entity\Trace;
use App\Model\ClientStatus;
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;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
#[AsCommand(name: 'opengnsys:check-client-availability', description: 'Check client availability')]
class CheckClientAvailability extends Command
{
const int THRESHOLD_MINUTES = 3;
public function __construct(
private readonly HubInterface $hub,
private readonly EntityManagerInterface $entityManager
)
{
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$threshold = (new \DateTime())->modify(' - '.self::THRESHOLD_MINUTES . ' minutes');
$startQueryTime = microtime(true);
$query = $this->entityManager->createQuery(
'UPDATE App\Entity\Client c
SET c.status = :status
WHERE c.status = :currentStatus AND c.updatedAt < :threshold'
);
$query->setParameter('status', ClientStatus::DISCONNECTED);
$query->setParameter('currentStatus', ClientStatus::OG_LIVE);
$query->setParameter('threshold', $threshold);
$updatedCount = $query->execute();
$queryTime = microtime(true) - $startQueryTime;
$startMercureTime = microtime(true);
$clients = $this->entityManager->createQueryBuilder()
->select('c')
->from(Client::class, 'c')
->where('c.status = :status')
->andWhere('c.updatedAt < :threshold')
->setParameter('status', ClientStatus::DISCONNECTED)
->setParameter('threshold', $threshold)
->getQuery()
->getResult();
$this->dispatchMercureEvent($clients);
$mercureTime = microtime(true) - $startMercureTime;
$io->success("Updated $updatedCount clients to DISCONNECTED status.");
$io->note("Query time: " . round($queryTime, 3) . "s");
$io->note("Mercure dispatch time: " . round($mercureTime, 3) . "s");
return Command::SUCCESS;
}
private function dispatchMercureEvent(array $clients, int $chunkSize = 10000): void
{
$chunks = array_chunk($clients, $chunkSize);
foreach ($chunks as $chunk) {
$data = [];
foreach ($chunk as $client) {
$data[] = [
'@id' => '/clients/' . $client->getUuid(),
'status' => $client->getStatus(),
];
}
$update = new Update(
'clients',
json_encode($data)
);
$this->hub->publish($update);
}
}
}

View File

@ -1,44 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Entity\Client;
use App\Entity\Trace;
use App\Model\ClientStatus;
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;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
#[AsCommand(name: 'opengnsys:test', description: 'Hello PhpStorm')]
class TestCommand extends Command
{
public function __construct(
private readonly HubInterface $hub,
private readonly EntityManagerInterface $entityManager
)
{
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$trace = $this->entityManager->getRepository(Trace::class)->find(7236);
$trace->setStatus(TraceStatus::SUCCESS);
$trace->setProgress(1000);
$this->entityManager->persist($trace);
$this->entityManager->flush();
return Command::SUCCESS;
}
}

View File

@ -3,6 +3,7 @@
namespace App\Controller\OgAgent\Webhook;
use App\Controller\OgRepository\Image\CreateAuxFilesAction;
use App\Entity\Client;
use App\Entity\Image;
use App\Entity\ImageImageRepository;
use App\Entity\OperativeSystem;
@ -59,16 +60,24 @@ class StatusController extends AbstractController
public function index(Request $request): JsonResponse
{
$data = $request->toArray();
$requiredFields = ['job_id'];
foreach ($requiredFields as $field) {
if (!isset($data[$field])) {
return new JsonResponse(['message' => "Missing parameter: $field"], Response::HTTP_BAD_REQUEST);
}
}
$this->logger->info('Webhook data received', $data);
// Esta parte del codigo nos indica si el cliente se encuentra activo
if (isset($data['iph']) && isset($data['timestamp'])) {
$client = $this->entityManager->getRepository(Client::class)->findOneBy(['ip' => $data['iph']]);
if (!$client) {
$this->logger->error('Client not found', $data);
return new JsonResponse(['message' => 'Client not found'], Response::HTTP_NOT_FOUND);
}
$updateAt = (new \DateTime())->setTimestamp((int)$data['timestamp']);
$client->setUpdatedAt($updateAt);
$this->entityManager->persist($client);
$this->entityManager->flush();
}
if (isset($data['progress'])){
$trace = $this->entityManager->getRepository(Trace::class)->findOneBy(['jobId' => $data['job_id']]);
if ($trace){

View File

@ -14,6 +14,9 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_MAC', fields: ['mac'])]
#[UniqueEntity(fields: ['ip'], message: 'This IP address is already in use.')]
#[UniqueEntity(fields: ['mac'], message: 'This MAC address is already in use.')]
#[ORM\Index(fields: ['status'], name: 'IDX_STATUS')]
#[ORM\Index(fields: ['updatedAt'], name: 'IDX_UPDATED_AT')]
#[ORM\Index(fields: ['status', 'updatedAt'], name: 'IDX_STATUS_UPDATED_AT')]
class Client extends AbstractEntity
{
use NameableTrait;

View File

@ -58,9 +58,14 @@ class MercureSubscriber implements EventSubscriberInterface
/** @var Client $client */
$client = $clientOutput->getEntity();
$data[] = [
'@id' => '/clients/' . $client->getUuid(),
'status' => $client->getStatus(),
];
$update = new Update(
'clients',
json_encode(['@id' => '/clients/'.$client->getUuid(), 'status' => $client->getStatus()])
json_encode($data)
);
$this->hub->publish($update);

View File

@ -6,6 +6,7 @@ final class ClientStatus
{
public const string OFF = 'off';
public const string INITIALIZING = 'initializing';
public const string DISCONNECTED = 'disconnected';
public const string TURNING_OFF = 'turning-off';
public const string OG_LIVE = 'og-live';
public const string BUSY = 'busy';
@ -20,6 +21,7 @@ final class ClientStatus
self::OFF => 'Apagado',
self::TURNING_OFF => 'Apagando',
self::INITIALIZING => 'Inicializando',
self::DISCONNECTED => 'Conexión perdida',
self::OG_LIVE => 'OG Live',
self::BUSY => 'Ocupado',
self::LINUX => 'Linux',