From 65ac067c7eb88b9803b7c4215af816f811d7a506 Mon Sep 17 00:00:00 2001 From: lgromero Date: Wed, 4 Dec 2024 07:56:33 +0100 Subject: [PATCH 1/2] refs #1131 adds a lot of logs in in all endpoints --- config/packages/monolog.yaml | 10 + .../Command/OgLiveInstallCommand.php | 196 +++-- .../Controller/OgBootController.php | 777 +++++++++++++----- .../Service/CurlRequestService.php | 86 +- 4 files changed, 712 insertions(+), 357 deletions(-) diff --git a/config/packages/monolog.yaml b/config/packages/monolog.yaml index 9db7d8a..5655a34 100644 --- a/config/packages/monolog.yaml +++ b/config/packages/monolog.yaml @@ -18,6 +18,11 @@ when@dev: #chromephp: # type: chromephp # level: info + syslog: + type: syslog + ident: "ogboot" # Puedes dar un nombre de identificador personalizado + level: info + channels: ["!event"] console: type: console process_psr_3_messages: false @@ -60,3 +65,8 @@ when@prod: channels: [deprecation] path: php://stderr formatter: monolog.formatter.json + syslog: + type: syslog + ident: "ogboot" # Puedes dar un nombre de identificador personalizado + level: info + channels: ["!event"] diff --git a/src/OgBootBundle/Command/OgLiveInstallCommand.php b/src/OgBootBundle/Command/OgLiveInstallCommand.php index 29cda7b..a2b0b38 100644 --- a/src/OgBootBundle/Command/OgLiveInstallCommand.php +++ b/src/OgBootBundle/Command/OgLiveInstallCommand.php @@ -1,5 +1,7 @@ curlRequestService = $curlRequestService; - #$this->httpClient = $httpClient; - $this->httpClient = HttpClient::create([ - 'verify_peer' => false, // Ignorar la verificación del certificado SSL - 'verify_host' => false, // Ignorar la verificación del nombre del host - ]); - $this->logger = $logger; // Añadimos el logger + $this->httpClient = HttpClient::create([ + 'verify_peer' => false, // Ignorar la verificación del certificado SSL + 'verify_host' => false, // Ignorar la verificación del nombre del host + ]); + $this->logger = $logger; + $this->ogCoreApiUrl = $ogCoreApiUrl; } protected function configure() @@ -37,119 +44,108 @@ class OgLiveInstallCommand extends Command ->addArgument('transactionId', InputArgument::REQUIRED, 'ID de la transacción'); } -protected function execute(InputInterface $input, OutputInterface $output) -{ - ini_set('memory_limit', '-1'); - ini_set('max_execution_time', '3000'); - $isoUrl = $input->getArgument('isoUrl'); + protected function execute(InputInterface $input, OutputInterface $output) + { + ini_set('memory_limit', '-1'); + ini_set('max_execution_time', '3000'); - $transactionId = $input->getArgument('transactionId'); + $isoUrl = $input->getArgument('isoUrl'); + $transactionId = $input->getArgument('transactionId'); - // Iniciamos los logs para rastrear el proceso - $this->logger->info('Starting ogLive installation', ['transactionId' => $transactionId]); + // Log inicial + $this->logger->info('Starting ogLive installation process.', [ + 'transactionId' => $transactionId, + 'isoUrl' => $isoUrl, + ]); - try { - // Log: comenzando la descarga del ISO - $this->logger->info('Starting download', ['isoUrl' => $isoUrl]); + try { + // Log: Iniciando la descarga + $this->logger->info('Initiating ISO download.', ['isoUrl' => $isoUrl]); - // Llamar al servicio para iniciar la instalación - $installResult = $this->curlRequestService->callOgLive("download " . escapeshellarg($isoUrl)); + // Llamada al servicio para iniciar la descarga del ISO + $installResult = $this->curlRequestService->callOgLive("download " . escapeshellarg($isoUrl)); - // Log: descarga finalizada - $this->logger->info('Download finished', ['transactionId' => $transactionId, 'installResult' => $installResult]); + // Log: Descarga completada + $this->logger->info('ISO download completed.', [ + 'transactionId' => $transactionId, + 'installResult' => $installResult, + ]); - // Verificar el resultado de la instalación basado en el output del comando - $status = 'success'; - $messageText = 'ogLive client installed successfully'; - $exitCode = $installResult['exitCode']; + // Verificación de resultado + $exitCode = $installResult['exitCode']; + $status = ($exitCode === 0) ? 'success' : 'failure'; + $messageText = $installResult['output']['message'] ?? 'Unknown error'; - if (isset($installResult['output']['error'])) { - // Si hay un error, asignar el estado y mensaje adecuado - $status = $installResult['output']['error']; - $messageText = $installResult['output']['message']; + if ($exitCode !== 0) { + $this->logger->error('Installation failed.', [ + 'transactionId' => $transactionId, + 'exitCode' => $exitCode, + 'error' => $messageText, + ]); + } else { + $this->logger->info('Installation completed successfully.', [ + 'transactionId' => $transactionId, + ]); + } - // Log: la instalación falló - $this->logger->error('Installation failed', ['transactionId' => $transactionId, 'error' => $messageText]); - } elseif ($installResult['exitCode'] !== 0) { - // Si hubo un exitCode distinto de 0, manejar como error desconocido - $status = 'UNKNOWN_ERROR'; - $messageText = 'An unknown error occurred during the installation process.'; - $this->logger->error('Unknown installation error', ['transactionId' => $transactionId, 'exitCode' => $installResult['exitCode']]); - } else { - // Log: instalación completada con éxito - $messageText = $installResult['output']['message']; - $this->logger->info('Installation completed successfully', ['transactionId' => $transactionId]); + // Preparar datos para el webhook + $webhookData = [ + 'ogCoreId' => $transactionId, + 'status' => $status, + 'code' => ($exitCode === 0) ? 200 : $exitCode, + 'message' => $messageText, + ]; + + $this->logger->info('Webhook data prepared.', ['webhookData' => $webhookData]); + + // Enviar al webhook + $webhookUrl = "{$this->ogCoreApiUrl}/og-lives/install/webhook"; + $this->logger->info('Sending data to webhook.', ['webhookUrl' => $webhookUrl]); + $this->notifyWebhook($webhookUrl, $webhookData); + + } catch (Exception $e) { + // Log de error + $this->logger->error('Installation process failed.', [ + 'transactionId' => $transactionId, + 'exception' => $e->getMessage(), + ]); + + // Enviar notificación de error al webhook + $webhookData = [ + 'ogCoreId' => $transactionId, + 'status' => 'failure', + 'code' => 500, + 'message' => $e->getMessage(), + ]; + + $webhookUrl = "{$this->ogCoreApiUrl}/og-lives/install/webhook"; + $this->logger->info('Sending error notification to webhook.', ['webhookUrl' => $webhookUrl]); + $this->notifyWebhook($webhookUrl, $webhookData); } - // Preparar los datos para el webhook según el resultado - $webhookData = [ - 'ogCoreId' => $transactionId, - 'status' => $status, - 'code' => ($exitCode === 0) ? 200 : $exitCode, // Cambiar a 200 si es éxito - 'message' => $messageText , - ]; + // Log finalización + $this->logger->info('Installation process ended.', ['transactionId' => $transactionId]); - $this->logger->info('Installation completed with details', ['installResult' => $installResult]); - $this->logger->info('Webhook data to be sent: ', ['webhookData' => $webhookData]); - - #$webhookUrl = "https://172.17.8.90:8443/og-lives/install/webhook"; - $webhookUrl = "{$this->ogCoreApiUrl}/og-lives/install/webhook"; - // Log: enviando datos al webhook - $this->logger->info('Sending data to webhook', ['webhookUrl' => $webhookUrl, 'webhookData' => $webhookData]); - - // Llamar al webhook para notificar el resultado - $this->notifyWebhook($webhookUrl, $webhookData); - $this->logger->info('Notify webhook con exito'); - // Log: notificación al webhook finalizada - $this->logger->info('Webhook notification sent', ['transactionId' => $transactionId]); - - } catch (Exception $e) { - // Log: error en la instalación - $this->logger->error('Failed to complete ogLive installation', ['transactionId' => $transactionId, 'exception' => $e->getMessage()]); - - // Manejar errores y enviar notificación de fallo al webhook - $webhookData = [ - 'ogCoreId' => $transactionId, - 'status' => 'failure', - 'code' => 500, - 'message' => $e->getMessage() - ]; - - $webhookUrl = "https://172.17.8.90:8443/og-lives/install/webhook"; - $this->notifyWebhook($webhookUrl, $webhookData); - $this->logger->info('Notify webhook terminado con errores'); - // Log: notificación de error enviada al webhook - $this->logger->info('Failure notification sent to webhook', ['transactionId' => $transactionId]); + return Command::SUCCESS; } - // Log: proceso de instalación terminado - $this->logger->info('Installation process ended', ['transactionId' => $transactionId]); - - return Command::SUCCESS; -} - - /** - * Enviar el resultado al webhook - */ private function notifyWebhook(string $webhookUrl, array $webhookData): void { - try { - $this->logger->info('Enter notify webhook'); - $this->logger->info('Data to be sent', ['webhookData' => $webhookData]); + try { + $this->logger->info('Sending webhook notification.', ['webhookData' => $webhookData]); $this->httpClient->request('POST', $webhookUrl, [ 'headers' => [ 'accept' => 'application/json', - 'Content-Type' => 'application/json', + 'Content-Type' => 'application/json', ], 'body' => json_encode(['webhookData' => $webhookData]), ]); - - // Log: éxito al enviar la notificación - $this->logger->info('Webhook data sent successfully', ['webhookUrl' => $webhookUrl, 'webhookData' => $webhookData]); + $this->logger->info('Webhook notification sent successfully.', ['webhookUrl' => $webhookUrl]); } catch (Exception $e) { - // Log: error al enviar al webhook - $this->logger->error('Error sending webhook notification', ['webhookUrl' => $webhookUrl, 'exception' => $e->getMessage()]); + $this->logger->error('Error sending webhook notification.', [ + 'webhookUrl' => $webhookUrl, + 'exception' => $e->getMessage(), + ]); } } } - diff --git a/src/OgBootBundle/Controller/OgBootController.php b/src/OgBootBundle/Controller/OgBootController.php index 73556d8..75f2326 100644 --- a/src/OgBootBundle/Controller/OgBootController.php +++ b/src/OgBootBundle/Controller/OgBootController.php @@ -214,17 +214,29 @@ public function getStatus(): Response */ public function getDownloadMenu(): Response { + $this->logger->info('Attempting to retrieve ISO download menu.'); + // Llamar al servicio para obtener las ISOs disponibles $downloadsOutput = $this->curlRequestService->callOgLive("download"); // Verificar si hubo un error en la respuesta if (is_array($downloadsOutput) && isset($downloadsOutput['error'])) { + $this->logger->error('Failed to retrieve ISOs.', [ + 'error' => $downloadsOutput['error'], + 'message' => $downloadsOutput['message'], + 'responseCode' => Response::HTTP_INTERNAL_SERVER_ERROR + ]); return new JsonResponse( ['error' => 'FAILED_TO_RETRIEVE_ISOS', 'message' => $downloadsOutput['message']], Response::HTTP_INTERNAL_SERVER_ERROR ); } + $this->logger->info('ISOs retrieved successfully.', [ + 'responseCode' => Response::HTTP_OK, + 'downloads' => $downloadsOutput['output']['downloads'] ?? [] + ]); + // Formatear la respuesta según el nuevo formato $response = [ 'success' => 'ISOs retrieved successfully', @@ -233,6 +245,7 @@ public function getDownloadMenu(): Response return new JsonResponse($response, Response::HTTP_OK); } + /** * @Route("/ogboot/v1/oglives", name="getOglives", methods={"GET"}) * @OA\Get( @@ -280,48 +293,92 @@ public function getDownloadMenu(): Response * ) */ -public function getOglives(): Response -{ - // Llama al servicio que ejecuta el comando oglivecli y obtiene tanto el output como el código de salida - $response = $this->curlRequestService->callOgLive("list_installed_oglives"); - $ogLiveConfigResult = $response['output']; - $exitCode = $response['exitCode']; - - // Manejo de errores basado en el código de salida - if ($exitCode === 404) { - return new JsonResponse( - ['error' => 'NOT_FOUND', 'message' => 'No ogLive clients found.'], - Response::HTTP_NOT_FOUND - ); - } elseif ($exitCode === 500) { - return new JsonResponse( - ['error' => 'SERVER_ERROR', 'message' => 'Failed to retrieve ogLive clients due to server error.'], - Response::HTTP_INTERNAL_SERVER_ERROR - ); - } elseif ($exitCode !== 0) { - // Manejar otros posibles códigos de error no esperados - return new JsonResponse( - ['error' => 'UNKNOWN_ERROR', 'message' => 'An unknown error occurred.'], - Response::HTTP_INTERNAL_SERVER_ERROR - ); - } - - // Verificar si la salida es válida - if (empty($ogLiveConfigResult) || !isset($ogLiveConfigResult['installed_ogLives'])) { - return new JsonResponse( - ['error' => 'NOT_FOUND', 'message' => 'No ogLive clients found.'], - Response::HTTP_NOT_FOUND - ); - } - - // Formatear la respuesta final en caso de éxito - $response = [ - 'success' => 'ogLive clients retrieved successfully', - 'message' => $ogLiveConfigResult // Incluye la estructura completa con default_oglive e installed_ogLives - ]; - - return new JsonResponse($response, Response::HTTP_OK); -} + public function getOglives(): Response + { + // Log inicial: Inicio del endpoint + $this->logger->info('Fetching list of installed ogLive clients.'); + + // Llama al servicio que ejecuta el comando oglivecli + $response = $this->curlRequestService->callOgLive("list_installed_oglives"); + $ogLiveConfigResult = $response['output']; + $exitCode = $response['exitCode']; + + // Log de depuración: Respuesta completa del servicio + $this->logger->debug( + sprintf('ogLive service response: %s | HTTP Code: %d', + json_encode($response), + Response::HTTP_OK) + ); + + // Manejo de errores basado en el código de salida + if ($exitCode === 404) { + $httpCode = Response::HTTP_NOT_FOUND; + $this->logger->info( + sprintf('No ogLive clients found. | HTTP Code: %d', + $httpCode) + ); + return new JsonResponse( + ['error' => 'NOT_FOUND', 'message' => 'No ogLive clients found.'], + $httpCode + ); + } elseif ($exitCode === 500) { + $httpCode = Response::HTTP_INTERNAL_SERVER_ERROR; + $this->logger->error( + sprintf('Failed to retrieve ogLive clients due to server error. | HTTP Code: %d', + $httpCode) + ); + return new JsonResponse( + ['error' => 'SERVER_ERROR', 'message' => 'Failed to retrieve ogLive clients due to server error.'], + $httpCode + ); + } elseif ($exitCode !== 0) { + $httpCode = Response::HTTP_INTERNAL_SERVER_ERROR; + $this->logger->error( + sprintf('Unknown error occurred. | HTTP Code: %d', + $httpCode) + ); + return new JsonResponse( + ['error' => 'UNKNOWN_ERROR', 'message' => 'An unknown error occurred.'], + $httpCode + ); + } + + // Verificar si la salida es válida + if (empty($ogLiveConfigResult) || !isset($ogLiveConfigResult['installed_ogLives'])) { + $httpCode = Response::HTTP_NOT_FOUND; + $this->logger->info( + sprintf('No installed ogLive clients found in the service response. | HTTP Code: %d', + $httpCode) + ); + return new JsonResponse( + ['error' => 'NOT_FOUND', 'message' => 'No ogLive clients found.'], + $httpCode + ); + } + + // Log de información: Número de ogLives encontrados + $httpCode = Response::HTTP_OK; + $this->logger->info( + sprintf('Number of installed ogLive clients: %d | HTTP Code: %d', + count($ogLiveConfigResult['installed_ogLives']), + $httpCode) + ); + + // Formatear la respuesta final en caso de éxito + $response = [ + 'success' => 'ogLive clients retrieved successfully', + 'message' => $ogLiveConfigResult // Incluye la estructura completa con default_oglive e installed_ogLives + ]; + + // Log de éxito: Respuesta enviada + $this->logger->info( + sprintf('ogLive clients retrieved successfully. | HTTP Code: %d', + $httpCode) + ); + + return new JsonResponse($response, $httpCode); + } + /** * @Route("/ogboot/v1/oglives/default", name="getOgliveDefault", methods={"GET"}) @@ -350,24 +407,50 @@ public function getOglives(): Response */ public function getOgliveDefault(Request $request): Response { + // Log inicial: Inicio del endpoint + $this->logger->info('Fetching default ogLive configuration.'); + + // Llamar al servicio que obtiene la configuración por defecto $response = $this->curlRequestService->callOgLive("get_default"); $result = $response['output']; $exitCode = $response['exitCode']; - // Verificar si hay un error basado en el código de salida + // Log de depuración: Respuesta completa del servicio + $this->logger->debug( + sprintf('ogLive default service response: %s | HTTP Code: %d', + json_encode($response), + Response::HTTP_OK) + ); + + // Manejo de errores basado en el código de salida if ($exitCode !== 0) { + $httpCode = Response::HTTP_INTERNAL_SERVER_ERROR; + $errorMessage = $result['error'] ?? 'Failed to retrieve default ogLive.'; + $this->logger->error( + sprintf('Error fetching default ogLive. Message: %s | HTTP Code: %d', + $errorMessage, + $httpCode) + ); return new JsonResponse( - ['error' => 'SERVER_ERROR', 'message' => $result['error'] ?? 'Failed to retrieve default ogLive.'], - Response::HTTP_INTERNAL_SERVER_ERROR + ['error' => 'SERVER_ERROR', 'message' => $errorMessage], + $httpCode ); } + // Log de éxito: Respuesta válida obtenida + $httpCode = Response::HTTP_OK; + $this->logger->info( + sprintf('Default ogLive configuration retrieved successfully. | HTTP Code: %d', + $httpCode) + ); + // Devolver la respuesta en el formato solicitado return new JsonResponse( ['success' => 'se ha obtenido el oglive por defecto', 'message' => $result], - Response::HTTP_OK + $httpCode ); } + /** * @Route("/ogboot/v1/oglives/{checksum}", name="getOglive", methods={"GET"}) * @OA\Get( @@ -414,32 +497,63 @@ public function getOgliveDefault(Request $request): Response */ public function getOglive(string $checksum): Response { - // Llamar al servicio ogLive para obtener la información del ogLive correspondiente al checksum proporcionado + // Log inicial: Inicio del endpoint con el checksum proporcionado + $this->logger->info(sprintf('Fetching ogLive client with checksum: %s.', $checksum)); + + // Llamar al servicio ogLive para obtener la información del ogLive correspondiente $response = $this->curlRequestService->callOgLive("get_info " . escapeshellarg($checksum)); $result = $response['output']; $exitCode = $response['exitCode']; - // Verificar si hay un error basado en el código de salida + // Log de depuración: Respuesta completa del servicio + $this->logger->debug( + sprintf('ogLive service response: %s | HTTP Code: %d', + json_encode($response), + Response::HTTP_OK) + ); + + // Manejo de errores basado en el código de salida if ($exitCode === 404) { + $httpCode = Response::HTTP_NOT_FOUND; + $this->logger->info( + sprintf('ogLive client with checksum %s not found. | HTTP Code: %d', + $checksum, + $httpCode) + ); return new JsonResponse( ['error' => 'NOT_FOUND', 'message' => "ogLive client with checksum $checksum not found."], - Response::HTTP_NOT_FOUND + $httpCode ); } elseif ($exitCode !== 0) { + $httpCode = Response::HTTP_INTERNAL_SERVER_ERROR; + $this->logger->error( + sprintf('Failed to retrieve ogLive client with checksum %s. | HTTP Code: %d', + $checksum, + $httpCode) + ); return new JsonResponse( ['error' => 'SERVER_ERROR', 'message' => 'Failed to retrieve ogLive client.'], - Response::HTTP_INTERNAL_SERVER_ERROR + $httpCode ); } + // Log de éxito: Respuesta válida obtenida + $httpCode = Response::HTTP_OK; + $this->logger->info( + sprintf('ogLive client with checksum %s retrieved successfully. | HTTP Code: %d', + $checksum, + $httpCode) + ); + // Formatear la respuesta de éxito con el ogLive encontrado return new JsonResponse( ['success' => 'ogLive client retrieved successfully', 'message' => $result], - Response::HTTP_OK + $httpCode ); } + /** * @Route("/ogboot/v1/oglives/default", name="setOgliveDefault", methods={"PUT"}) * @OA\Put( @@ -488,38 +602,75 @@ public function getOglive(string $checksum): Response */ public function setOgliveDefault(Request $request): Response { + // Log inicial: Inicio del endpoint + $this->logger->info('Setting ogLive client as default.'); + + // Decodificar el contenido de la solicitud $data = json_decode($request->getContent(), true); // Validar que el checksum está presente en los datos de entrada if (!isset($data['checksum'])) { + $httpCode = Response::HTTP_BAD_REQUEST; + $this->logger->error( + sprintf('Invalid input data: checksum is required. | HTTP Code: %d', + $httpCode) + ); return new JsonResponse( ['error' => 'INVALID_INPUT', 'message' => 'Invalid input data: checksum is required.'], - Response::HTTP_BAD_REQUEST + $httpCode ); } $checksum = $data['checksum']; + // Log de depuración: Cheksum recibido + $this->logger->debug( + sprintf('Checksum received for setting default: %s.', $checksum) + ); + // Llamar al servicio para establecer el ogLive como predeterminado utilizando el checksum proporcionado $response = $this->curlRequestService->callOgLive("set_default " . escapeshellarg($checksum)); $result = $response['output']; $exitCode = $response['exitCode']; - // Verificar si hay un error basado en el código de salida + // Log de depuración: Respuesta completa del servicio + $this->logger->debug( + sprintf('ogLive service response: %s | Exit Code: %d', + json_encode($response), + $exitCode) + ); + + // Manejo de errores basado en el código de salida if ($exitCode !== 0) { + $httpCode = Response::HTTP_INTERNAL_SERVER_ERROR; + $errorMessage = $result['error'] ?? 'Failed to set ogLive as default.'; + $this->logger->error( + sprintf('Error setting ogLive client as default. Message: %s | HTTP Code: %d', + $errorMessage, + $httpCode) + ); return new JsonResponse( - ['error' => 'SERVER_ERROR', 'message' => $result['error'] ?? 'Failed to set ogLive as default.'], - Response::HTTP_INTERNAL_SERVER_ERROR + ['error' => 'SERVER_ERROR', 'message' => $errorMessage], + $httpCode ); } + // Log de éxito: ogLive configurado como predeterminado + $httpCode = Response::HTTP_OK; + $this->logger->info( + sprintf('ogLive client with checksum %s set as default successfully. | HTTP Code: %d', + $checksum, + $httpCode) + ); + // Devolver la respuesta en el formato solicitado return new JsonResponse( ['success' => 'ogLive client set as default successfully', 'message' => $result], - Response::HTTP_OK + $httpCode ); } + /** * @Route("/ogboot/v1/oglives/install", name="installOglive", methods={"POST"}) * @OA\Post( @@ -575,57 +726,92 @@ public function setOgliveDefault(Request $request): Response public function installOglive(Request $request): JsonResponse { + // Log inicial: Inicio del endpoint + $this->logger->info('Initiating ogLive client installation process.'); + // Obtener los datos del cuerpo de la petición $data = json_decode($request->getContent(), true); + // Validar que los datos requeridos están presentes if (!isset($data['url']) || !isset($data['id'])) { - return new JsonResponse(['error' => 'Invalid input data'], JsonResponse::HTTP_BAD_REQUEST); + $httpCode = JsonResponse::HTTP_BAD_REQUEST; + $this->logger->error( + sprintf('Invalid input data. URL or Transaction ID missing. | HTTP Code: %d', + $httpCode) + ); + return new JsonResponse(['error' => 'Invalid input data'], $httpCode); } // Obtener la URL del ISO y el ID de la transacción $isoUrl = $data['url']; $transactionId = $data['id']; + // Log de depuración: URL e ID recibidos + $this->logger->debug( + sprintf('ISO URL: %s | Transaction ID: %s', + $isoUrl, + $transactionId) + ); + try { - // Ejecutar el comando Symfony en segundo plano + // Construir el comando Symfony para ejecutar en segundo plano $consolePath = $this->params->get('kernel.project_dir') . '/bin/console'; - $this->logger->info($consolePath); - $process = new Process([ - $consolePath, - 'oglive:install', - $isoUrl, // Sin escapeshellarg - $transactionId // Sin escapeshellarg, - ]); + $this->logger->info(sprintf('Symfony console path: %s', $consolePath)); + + // Log del comando que será ejecutado $command = sprintf( 'php %s oglive:install %s %s > /dev/null 2>&1 ', escapeshellarg($consolePath), escapeshellarg($isoUrl), escapeshellarg($transactionId) ); - $commandLine = $process->getCommandLine(); - $this->logger->info('Command to be executed: ' . $commandLine); + $this->logger->info('Command to be executed: ' . $command); + + // Crear y configurar el proceso $process = Process::fromShellCommandline($command); - #$process->start(); // Ejecuta en segundo plano $process->setTimeout(null); $process->disableOutput(); + try { + // Iniciar el proceso en segundo plano $process->start(); + $this->logger->info( + sprintf('ogLive client installation process started successfully. | Transaction ID: %s', + $transactionId) + ); + + // Respuesta inicial indicando que el proceso ha sido iniciado + return new JsonResponse([ + 'success' => 'ogLive client installation started', + 'transaction_id' => $transactionId + ], JsonResponse::HTTP_ACCEPTED); + } catch (\Exception $e) { - $this->logger->error('Error starting process: ' . $e->getMessage()); + // Log del error al intentar iniciar el proceso + $this->logger->error( + sprintf('Error starting ogLive client installation process. Message: %s', + $e->getMessage()) + ); + throw $e; // Rethrow para el bloque catch externo } - return new JsonResponse([ - 'success' => 'ogLive client installation started', - 'transaction_id' => $transactionId - ], JsonResponse::HTTP_ACCEPTED); } catch (Exception $e) { - // Si ocurre algún error durante el proceso de inicio + // Log del error general + $httpCode = JsonResponse::HTTP_INTERNAL_SERVER_ERROR; + $this->logger->error( + sprintf('Failed to initiate ogLive client installation. Message: %s | HTTP Code: %d', + $e->getMessage(), + $httpCode) + ); + + // Respuesta de error al cliente return new JsonResponse([ 'error' => 'Failed to initiate ogLive installation', 'details' => $e->getMessage() - ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); + ], $httpCode); } } + /** * Enviar el resultado al webhook @@ -691,44 +877,49 @@ public function setOgliveDefault(Request $request): Response */ public function uninstallOglive(string $checksum): Response { + // Log inicial: Inicio del endpoint con el checksum proporcionado + $this->logger->info(sprintf('Initiating ogLive client uninstallation for checksum: %s', $checksum)); + // Llamada al servicio para desinstalar el cliente ogLive - error_log("Calling curlRequestService with checksum: $checksum"); - - // Ejecutar el comando y capturar tanto el resultado como el código de salida $response = $this->curlRequestService->callOgLive("uninstall " . escapeshellarg($checksum)); $result = $response['output']; $exitCode = $response['exitCode']; - // Verificar la respuesta del servicio según el código de salida - $this->logger->info("Service call result: " . print_r($result, true)); - $this->logger->info("Exit code: " . $exitCode); + // Log de depuración: Respuesta completa del servicio + $this->logger->debug(sprintf('Service response: %s | Exit Code: %d', json_encode($result), $exitCode)); + // Verificar la respuesta del servicio según el código de salida if ($exitCode === 404) { - // ogLive client no encontrado + // Log: Cliente ogLive no encontrado + $this->logger->info(sprintf('ogLive client not found for checksum: %s | HTTP Code: 404', $checksum)); return new JsonResponse([ 'error' => 'NOT_FOUND', 'message' => $result['message'] ?? 'ogLive client not found.' ], Response::HTTP_NOT_FOUND); } elseif ($exitCode === 403) { - // Intento de desinstalar el ogLive por defecto + // Log: Intento de desinstalar el ogLive predeterminado + $this->logger->warning(sprintf('Attempt to uninstall the default ogLive client. | Checksum: %s | HTTP Code: 403', $checksum)); return new JsonResponse([ 'error' => 'FORBIDDEN', 'message' => $result['message'] ?? 'Cannot uninstall the default ogLive client.' ], Response::HTTP_FORBIDDEN); } elseif ($exitCode === 500) { - // Error interno del servidor + // Log: Error interno del servidor + $this->logger->error(sprintf('Server error during ogLive uninstallation for checksum: %s | HTTP Code: 500', $checksum)); return new JsonResponse([ 'error' => 'SERVER_ERROR', 'message' => $result['message'] ?? 'Failed to uninstall ogLive client.' ], Response::HTTP_INTERNAL_SERVER_ERROR); } elseif ($exitCode === 200) { - // Desinstalación exitosa + // Log: Desinstalación exitosa + $this->logger->info(sprintf('ogLive client uninstalled successfully for checksum: %s | HTTP Code: 200', $checksum)); return new JsonResponse([ 'message' => $result['message'], 'details' => $result['details'] ?? '' ], Response::HTTP_OK); } else { - // Manejar otros códigos de error no previstos + // Log: Error desconocido + $this->logger->error(sprintf('Unknown error during ogLive uninstallation for checksum: %s | HTTP Code: 500', $checksum)); return new JsonResponse([ 'error' => 'UNKNOWN_ERROR', 'message' => 'An unknown error occurred.' @@ -738,7 +929,6 @@ public function uninstallOglive(string $checksum): Response - /** * @Route("/ogboot/v1/pxes", name="get_boot_files", methods={"GET"}) * @OA\Get( @@ -874,8 +1064,11 @@ public function getBootFiles(): JsonResponse public function getBootFile(string $mac): Response { + // Log inicial: Inicio del endpoint + $this->logger->info('Retrieving boot file for MAC address.', ['mac' => $mac]); + // Ruta donde están alojados los archivos de arranque - $directory = '/opt/ogboot/tftpboot/ipxe_scripts'; + $directory = $this->tftpbootDir . '/ipxe_scripts'; // Validar y formatear la dirección MAC $mac = $this->validateAndFormatMac($mac ?? null); @@ -885,12 +1078,19 @@ public function getBootFiles(): JsonResponse // Normalizar la ruta del archivo para evitar accesos fuera del directorio permitido $realFilePath = realpath($directory) . '/' . $fileName; + // Log de depuración: Verificar la ruta del archivo + $this->logger->debug('Computed file path for boot file.', ['realFilePath' => $realFilePath]); + // Verificar que el archivo está dentro del directorio permitido if (strpos($realFilePath, realpath($directory)) !== 0) { + $this->logger->warning('Unauthorized access attempt outside the boot directory.', [ + 'mac' => $mac, + 'attemptedPath' => $realFilePath + ]); return new JsonResponse( [ 'error' => 'UNAUTHORIZED_ACCESS', - 'message' => 'Intento de acceso no autorizado fuera del directorio de arranque.' + 'message' => 'Attempted unauthorized access outside the boot directory.' ], Response::HTTP_FORBIDDEN ); @@ -898,6 +1098,7 @@ public function getBootFiles(): JsonResponse // Verificar si el archivo existe if (!file_exists($realFilePath)) { + $this->logger->info('Boot file not found for specified MAC address.', ['mac' => $mac]); return new JsonResponse( ['error' => 'NOT_FOUND', 'message' => 'No boot file found for the specified MAC address.'], Response::HTTP_NOT_FOUND @@ -907,12 +1108,16 @@ public function getBootFiles(): JsonResponse // Leer el contenido del archivo $content = file_get_contents($realFilePath); if ($content === false) { + $this->logger->error('Failed to read the boot file.', ['filePath' => $realFilePath]); return new JsonResponse( - ['error' => 'FAILED_TO_READ_BOOT_FILE', 'message' => 'Error al leer el archivo de arranque.'], + ['error' => 'FAILED_TO_READ_BOOT_FILE', 'message' => 'Error reading the boot file.'], Response::HTTP_INTERNAL_SERVER_ERROR ); } + // Log de depuración: Contenido del archivo leído + $this->logger->debug('Boot file content retrieved.', ['filePath' => $realFilePath, 'content' => $content]); + // Valores por defecto $templateName = 'unknown'; $oglivedir = ''; @@ -935,6 +1140,7 @@ public function getBootFiles(): JsonResponse // Si no se encuentran kernelargs, se asume un archivo de arranque por disco if (empty($kernelArgs)) { + $this->logger->info('Boot file retrieved without kernel parameters.', ['mac' => $mac, 'templateName' => $templateName]); return new JsonResponse( [ 'success' => 'Boot file retrieved successfully', @@ -978,6 +1184,12 @@ public function getBootFiles(): JsonResponse 'resolution' => $params['vga'] ?? '', ]; + // Log de éxito + $this->logger->info('Boot file retrieved successfully.', ['mac' => $mac, 'templateName' => $templateName]); + + // Log de depuración: Resultado completo + $this->logger->debug('Boot file parsed result.', ['result' => $result]); + // Formatear la respuesta de éxito return new JsonResponse( ['success' => 'Boot file retrieved successfully', 'message' => $result], @@ -985,6 +1197,7 @@ public function getBootFiles(): JsonResponse ); } + @@ -1026,32 +1239,45 @@ public function getBootFiles(): JsonResponse */ public function createBootFile(Request $request): JsonResponse { + $this->logger->info('Starting PXE file creation process.'); + $data = json_decode($request->getContent(), true); $templateName = $data['template_name'] ?? null; $mac = $this->validateAndFormatMac($data['mac'] ?? null); - //Si nos pasan el puerto se lo quitamos ya que server_ip siempre tirara por Samba + + // Si nos pasan el puerto se lo quitamos ya que server_ip siempre tirará por Samba $serverIp = $data['server_ip'] ?? null; if ($serverIp && strpos($serverIp, ':') !== false) { $serverIp = explode(':', $serverIp)[0]; } $ogLiveDir = $data['oglivedir'] ?? 'ogLive'; + // Log de entrada + $this->logger->debug('Input data.', [ + 'template_name' => $templateName, + 'mac' => $mac, + 'server_ip' => $serverIp, + 'oglivedir' => $ogLiveDir + ]); + // Verificación de los campos obligatorios if (!$templateName || !$mac) { + $this->logger->warning('Missing required fields: mac and/or template_name.', [ + 'missingFields' => !$templateName ? 'template_name' : 'mac' + ]); return new JsonResponse(['error' => 'Missing required fields: mac and template_name'], Response::HTTP_BAD_REQUEST); } - // Validación adicional para asegurarnos de que no hay caracteres inseguros + // Validación de la plantilla if (!preg_match('/^[a-zA-Z0-9._-]+$/', $templateName) || strpos($templateName, '..') !== false) { + $this->logger->warning('Invalid template name or unauthorized access attempt.', ['template_name' => $templateName]); return new JsonResponse( - ['error' => 'INVALID_TEMPLATE_NAME', 'message' => 'Nombre de la plantilla no válido o intento de acceso no autorizado.'], + ['error' => 'INVALID_TEMPLATE_NAME', 'message' => 'Invalid template name or unauthorized access attempt.'], Response::HTTP_BAD_REQUEST ); } - - // Parámetros opcionales $parameters = [ 'LANG' => $data['lang'] ?? 'es_ES.UTF-8', 'ip' => $data['ip'] ?? '', @@ -1080,17 +1306,21 @@ public function createBootFile(Request $request): JsonResponse // Verificar si la plantilla existe if (!file_exists($templatePath)) { - return new JsonResponse(['error' => 'TEMPLATE_NOT_FOUND', 'message' => 'No se encontró la plantilla especificada'], Response::HTTP_NOT_FOUND); + $this->logger->info('Template not found.', ['template_path' => $templatePath]); + return new JsonResponse(['error' => 'TEMPLATE_NOT_FOUND', 'message' => 'Template not found.'], Response::HTTP_NOT_FOUND); } $templateContent = file_get_contents($templatePath); if ($templateContent === false) { - return new JsonResponse(['error' => 'FAILED_TO_READ_TEMPLATE', 'message' => 'Error al leer la plantilla'], Response::HTTP_INTERNAL_SERVER_ERROR); + $this->logger->error('Failed to read the template.', ['template_path' => $templatePath]); + return new JsonResponse(['error' => 'FAILED_TO_READ_TEMPLATE', 'message' => 'Failed to read template.'], Response::HTTP_INTERNAL_SERVER_ERROR); } + $this->logger->debug('Template content loaded.', ['template_name' => $templateName]); + // Construcción de los argumentos del kernel $kernelArgs = 'ro boot=oginit quiet splash irqpoll acpi=on og2nd=sqfs ogprotocol=smb ogactiveadmin=true ogdebug=true ogtmpfs=15 ' . - 'oglivedir=${ISODIR}' . ' ' . + 'oglivedir=${ISODIR} ' . 'LANG=' . $parameters['LANG'] . ' ' . 'ip=' . $parameters['ip'] . ':' . $serverIp . ':' . $parameters['router'] . ':' . $parameters['netmask'] . ':' . $parameters['computer_name'] . ':' . $parameters['netiface'] . ':none ' . 'group=' . str_replace(' ', '_', trim($parameters['group'])) . ' ' . @@ -1108,23 +1338,19 @@ public function createBootFile(Request $request): JsonResponse (is_numeric($parameters['resolution']) && $parameters['resolution'] <= 999 ? 'vga=' . $parameters['resolution'] : (strpos($parameters['resolution'], ':') !== false ? 'video=' . $parameters['resolution'] : ' ' . $parameters['resolution'])); - // Esta será llamada a http para arrancar kernel e imagen de inicialización - // Si lo requiriese debe llevar puerto ya que se comunica con nginx + $this->logger->debug('Kernel arguments constructed.', ['kernelArgs' => $kernelArgs]); + $serverIpPort = $this->ogBootIP; if (!empty($this->ogBootPort)) { $serverIpPort .= ':' . $this->ogBootPort; } - // Extraer solo el nombre del directorio si contiene una ruta completa PROVISIONAL - if (strpos($ogLiveDir, '/') !== false) { - $ogLiveDir = basename($ogLiveDir); - } + $pxeContent = str_replace( ['__INFOHOST__', '__SERVERIP__', '__OGLIVE__'], - [$kernelArgs, $serverIpPort, $ogLiveDir], + [$kernelArgs, $serverIpPort, basename($ogLiveDir)], $templateContent ); - // Insertar el comentario con el nombre de la plantilla después de #!ipxe if (strpos($pxeContent, '#!ipxe') === 0) { $pxeContent = "#!ipxe\n#Template: $templateName\n" . substr($pxeContent, 6); @@ -1132,24 +1358,23 @@ public function createBootFile(Request $request): JsonResponse $pxeContent = "#!ipxe\n#Template: $templateName\n" . $pxeContent; } - // Nombre del archivo PXE basado en la MAC $pxeFileName = '01-' . $mac; - - // Ruta para guardar el archivo PXE $pxeFilePath = $this->tftpbootDir . '/ipxe_scripts/' . $pxeFileName; - // Crear el archivo PXE if (file_put_contents($pxeFilePath, $pxeContent) === false) { - return new JsonResponse(['error' => 'FAILED_TO_CREATE_PXE_FILE', 'message' => 'Error al crear el archivo PXE'], Response::HTTP_INTERNAL_SERVER_ERROR); + $this->logger->error('Failed to create PXE file.', ['pxe_file_path' => $pxeFilePath]); + return new JsonResponse(['error' => 'FAILED_TO_CREATE_PXE_FILE', 'message' => 'Failed to create PXE file.'], Response::HTTP_INTERNAL_SERVER_ERROR); } - // Retornar la plantilla creada en el formato solicitado + $this->logger->info('PXE file created successfully.', ['pxe_file_path' => $pxeFilePath, 'template_name' => $templateName]); + return new JsonResponse([ 'success' => 'PXE file created successfully', 'message' => $pxeContent ], Response::HTTP_OK); } + function validateAndFormatMac($mac) { @@ -1214,35 +1439,60 @@ function validateAndFormatMac($mac) */ public function deleteBootFile(string $mac): Response { - #$directory = '/opt/ogboot/tftpboot/ipxe_scripts'; + $this->logger->info('Attempting to delete boot file.', ['mac' => $mac]); + + // Ruta del directorio donde están los archivos de arranque $directory = $this->tftpbootDir . '/ipxe_scripts'; + // Validar y formatear la dirección MAC $mac = $this->validateAndFormatMac($mac ?? null); $fileName = "01-" . $mac; $filePath = "$directory/$fileName"; + $this->logger->debug('Computed file path for boot file.', ['filePath' => $filePath]); + // Verificar si el archivo de arranque existe if (!file_exists($filePath)) { + $this->logger->info('Boot file not found. Returning HTTP Response Code.', [ + 'mac' => $mac, + 'filePath' => $filePath, + 'responseCode' => Response::HTTP_NOT_FOUND + ]); return new JsonResponse( ['error' => 'NOT_FOUND', 'message' => 'No boot file found for the specified MAC address.'], Response::HTTP_NOT_FOUND ); } + $this->logger->debug('Boot file exists, proceeding to delete.', ['filePath' => $filePath]); + // Intentar eliminar el archivo de arranque if (!unlink($filePath)) { + $this->logger->error('Failed to delete boot file. Returning HTTP Response Code.', [ + 'mac' => $mac, + 'filePath' => $filePath, + 'responseCode' => Response::HTTP_INTERNAL_SERVER_ERROR + ]); return new JsonResponse( ['error' => 'SERVER_ERROR', 'message' => 'Failed to delete the boot file due to server error.'], Response::HTTP_INTERNAL_SERVER_ERROR ); } + // Registro de éxito + $this->logger->info('Boot file deleted successfully. Returning HTTP Response Code.', [ + 'mac' => $mac, + 'filePath' => $filePath, + 'responseCode' => Response::HTTP_OK + ]); + // Respuesta exitosa con formato estándar return new JsonResponse( ['success' => 'Boot file deleted successfully', 'message' => "The boot file for MAC $mac has been deleted."], Response::HTTP_OK ); } + /** * @Route("/ogboot/v1/pxe-templates", methods={"GET"}) @@ -1276,16 +1526,24 @@ public function deleteBootFile(string $mac): Response */ public function getAllTemplates(): JsonResponse { -# $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; + $this->logger->info('Attempting to retrieve all templates.'); + $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; + // Verificar si el directorio existe if (!is_dir($templateDir)) { + $this->logger->error('Template directory not found. Returning HTTP Response Code.', [ + 'templateDir' => $templateDir, + 'responseCode' => Response::HTTP_INTERNAL_SERVER_ERROR + ]); return new JsonResponse( ['error' => 'SERVER_ERROR', 'message' => 'Directorio de plantillas no encontrado'], Response::HTTP_INTERNAL_SERVER_ERROR ); } + $this->logger->debug('Template directory exists.', ['templateDir' => $templateDir]); + // Obtener la lista de archivos en el directorio $files = scandir($templateDir); @@ -1294,6 +1552,14 @@ public function getAllTemplates(): JsonResponse return !in_array($file, ['.', '..']) && !is_dir($templateDir . '/' . $file); }); + // Registro del resultado obtenido + $this->logger->info('Templates retrieved successfully. Returning HTTP Response Code.', [ + 'templateDir' => $templateDir, + 'templateCount' => count($templates), + 'responseCode' => Response::HTTP_OK + ]); + $this->logger->debug('Template list.', ['templates' => array_values($templates)]); + // Formatear la respuesta siguiendo el estándar return new JsonResponse( ['success' => 'Lista de plantillas obtenida con éxito', 'message' => array_values($templates)], @@ -1301,6 +1567,7 @@ public function getAllTemplates(): JsonResponse ); } + /** * @Route("/ogboot/v1/pxe-templates/{templateName}", name="get_template", methods={"GET"}) * @OA\Get( @@ -1342,36 +1609,58 @@ public function getAllTemplates(): JsonResponse * ) */ - public function getTemplate(string $templateName): Response - { - #$templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; - $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; - $filePath = "$templateDir/$templateName"; - - // Comprobar si el archivo de plantilla existe - if (!file_exists($filePath)) { - return new Response(json_encode([ - 'error' => 'TEMPLATE_NOT_FOUND', - 'message' => 'No se encontró la plantilla especificada' - ]), Response::HTTP_NOT_FOUND, ['Content-Type' => 'application/json']); - } - - // Intentar leer el contenido de la plantilla - $content = file_get_contents($filePath); - if ($content === false) { - return new Response(json_encode([ - 'error' => 'FAILED_TO_READ_TEMPLATE', - 'message' => 'Error al leer la plantilla' - ]), Response::HTTP_INTERNAL_SERVER_ERROR, ['Content-Type' => 'application/json']); - } - - // Devolver la respuesta con el nombre y contenido de la plantilla - return new Response(json_encode([ - 'success' => 'Plantilla obtenida con éxito', - 'template_name' => $templateName, - 'template_content' => $content - ]), Response::HTTP_OK, ['Content-Type' => 'application/json']); - } + public function getTemplate(string $templateName): Response + { + $this->logger->info('Attempting to retrieve template.', ['templateName' => $templateName]); + + $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; + $filePath = "$templateDir/$templateName"; + + $this->logger->debug('Computed file path for template.', ['filePath' => $filePath]); + + // Comprobar si el archivo de plantilla existe + if (!file_exists($filePath)) { + $this->logger->error('Template not found. Returning HTTP Response Code.', [ + 'templateName' => $templateName, + 'filePath' => $filePath, + 'responseCode' => Response::HTTP_NOT_FOUND + ]); + return new Response(json_encode([ + 'error' => 'TEMPLATE_NOT_FOUND', + 'message' => 'No se encontró la plantilla especificada' + ]), Response::HTTP_NOT_FOUND, ['Content-Type' => 'application/json']); + } + + $this->logger->debug('Template found, attempting to read content.', ['filePath' => $filePath]); + + // Intentar leer el contenido de la plantilla + $content = file_get_contents($filePath); + if ($content === false) { + $this->logger->error('Failed to read template content. Returning HTTP Response Code.', [ + 'templateName' => $templateName, + 'filePath' => $filePath, + 'responseCode' => Response::HTTP_INTERNAL_SERVER_ERROR + ]); + return new Response(json_encode([ + 'error' => 'FAILED_TO_READ_TEMPLATE', + 'message' => 'Error al leer la plantilla' + ]), Response::HTTP_INTERNAL_SERVER_ERROR, ['Content-Type' => 'application/json']); + } + + $this->logger->info('Template retrieved successfully. Returning HTTP Response Code.', [ + 'templateName' => $templateName, + 'responseCode' => Response::HTTP_OK + ]); + $this->logger->debug('Template content.', ['templateContent' => $content]); + + // Devolver la respuesta con el nombre y contenido de la plantilla + return new Response(json_encode([ + 'success' => 'Plantilla obtenida con éxito', + 'template_name' => $templateName, + 'template_content' => $content + ]), Response::HTTP_OK, ['Content-Type' => 'application/json']); + } + /** @@ -1433,76 +1722,110 @@ public function getAllTemplates(): JsonResponse * ) */ -public function createTemplate(Request $request): JsonResponse -{ - $data = json_decode($request->getContent(), true); - - if (!isset($data['name_template']) || !isset($data['content_template'])) { - return new JsonResponse([ - 'error' => 'INVALID_INPUT', - 'message' => 'Faltan datos requeridos: name_template y content_template son necesarios.' - ], JsonResponse::HTTP_BAD_REQUEST); - } - - $nameTemplate = $data['name_template']; - $contentTemplate = $data['content_template']; - $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; - - // Validar el nombre de la plantilla - if (!preg_match('/^[a-zA-Z0-9._-]+$/', $nameTemplate)) { - return new JsonResponse([ - 'error' => 'INVALID_TEMPLATE_NAME', - 'message' => 'El nombre de la plantilla contiene caracteres no válidos.' - ], JsonResponse::HTTP_BAD_REQUEST); - } - - // Construir la ruta completa del archivo - $filePath = $templateDir . '/' . $nameTemplate; - - // Normalizar la ruta del archivo para evitar accesos fuera del directorio permitido - $realFilePath = realpath($templateDir) . '/' . $nameTemplate; - - // Verificar que el archivo está dentro del directorio permitido - if (strpos($realFilePath, realpath($templateDir)) !== 0) { - return new JsonResponse([ - 'error' => 'UNAUTHORIZED_ACCESS', - 'message' => 'Intento de acceso no autorizado fuera del directorio de plantillas.' - ], JsonResponse::HTTP_FORBIDDEN); - } - - $contentTemplate = str_replace("\\n", "\n", $contentTemplate); - - try { - // Intentar crear la plantilla - file_put_contents($realFilePath, $contentTemplate); - - // Comprobar si el archivo se ha creado correctamente - if (!file_exists($realFilePath)) { - throw new \Exception('El archivo no se pudo crear.'); - } - - // Verificar si el contenido escrito coincide con el contenido esperado - $writtenContent = file_get_contents($realFilePath); - if ($writtenContent !== $contentTemplate) { - throw new \Exception('El contenido del archivo no coincide con el contenido esperado.'); - } - - } catch (\Exception $e) { - return new JsonResponse([ - 'error' => 'FILE_CREATION_ERROR', - 'message' => 'Ocurrió un error al crear la plantilla de arranque: ' . $e->getMessage() - ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); - } - - // Retornar el nombre de la plantilla y su contenido en el formato solicitado - return new JsonResponse([ - 'success' => 'Plantilla creada con éxito', - 'message' => [ - 'template_name' => $nameTemplate, - 'template_content' => $contentTemplate - ] - ], JsonResponse::HTTP_OK); -} + public function createTemplate(Request $request): JsonResponse + { + $this->logger->info('Attempting to create a new template.'); + + $data = json_decode($request->getContent(), true); + + if (!isset($data['name_template']) || !isset($data['content_template'])) { + $this->logger->error('Missing required fields: name_template or content_template.', [ + 'requestData' => $data, + 'responseCode' => JsonResponse::HTTP_BAD_REQUEST + ]); + return new JsonResponse([ + 'error' => 'INVALID_INPUT', + 'message' => 'Faltan datos requeridos: name_template y content_template son necesarios.' + ], JsonResponse::HTTP_BAD_REQUEST); + } + + $nameTemplate = $data['name_template']; + $contentTemplate = $data['content_template']; + $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; + + $this->logger->debug('Received template data.', ['nameTemplate' => $nameTemplate]); + + // Validar el nombre de la plantilla + if (!preg_match('/^[a-zA-Z0-9._-]+$/', $nameTemplate)) { + $this->logger->error('Invalid template name. Contains invalid characters.', [ + 'nameTemplate' => $nameTemplate, + 'responseCode' => JsonResponse::HTTP_BAD_REQUEST + ]); + return new JsonResponse([ + 'error' => 'INVALID_TEMPLATE_NAME', + 'message' => 'El nombre de la plantilla contiene caracteres no válidos.' + ], JsonResponse::HTTP_BAD_REQUEST); + } + + // Construir la ruta completa del archivo + $filePath = $templateDir . '/' . $nameTemplate; + $realFilePath = realpath($templateDir) . '/' . $nameTemplate; + + $this->logger->debug('Computed file paths for template.', ['filePath' => $filePath, 'realFilePath' => $realFilePath]); + + // Verificar que el archivo está dentro del directorio permitido + if (strpos($realFilePath, realpath($templateDir)) !== 0) { + $this->logger->error('Unauthorized access attempt outside the template directory.', [ + 'nameTemplate' => $nameTemplate, + 'filePath' => $filePath, + 'responseCode' => JsonResponse::HTTP_FORBIDDEN + ]); + return new JsonResponse([ + 'error' => 'UNAUTHORIZED_ACCESS', + 'message' => 'Intento de acceso no autorizado fuera del directorio de plantillas.' + ], JsonResponse::HTTP_FORBIDDEN); + } + + $this->logger->debug('Sanitizing template content.'); + + $contentTemplate = str_replace("\\n", "\n", $contentTemplate); + + try { + $this->logger->info('Attempting to create the template file.', ['filePath' => $realFilePath]); + + // Intentar crear la plantilla + file_put_contents($realFilePath, $contentTemplate); + + // Comprobar si el archivo se ha creado correctamente + if (!file_exists($realFilePath)) { + throw new \Exception('El archivo no se pudo crear.'); + } + + // Verificar si el contenido escrito coincide con el contenido esperado + $writtenContent = file_get_contents($realFilePath); + if ($writtenContent !== $contentTemplate) { + throw new \Exception('El contenido del archivo no coincide con el contenido esperado.'); + } + + $this->logger->info('Template created successfully.', ['filePath' => $realFilePath]); + + } catch (\Exception $e) { + $this->logger->error('Failed to create template.', [ + 'nameTemplate' => $nameTemplate, + 'filePath' => $realFilePath, + 'exception' => $e->getMessage(), + 'responseCode' => JsonResponse::HTTP_INTERNAL_SERVER_ERROR + ]); + return new JsonResponse([ + 'error' => 'FILE_CREATION_ERROR', + 'message' => 'Ocurrió un error al crear la plantilla de arranque: ' . $e->getMessage() + ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); + } + + $this->logger->info('Returning success response.', [ + 'templateName' => $nameTemplate, + 'responseCode' => JsonResponse::HTTP_OK + ]); + + // Retornar el nombre de la plantilla y su contenido en el formato solicitado + return new JsonResponse([ + 'success' => 'Plantilla creada con éxito', + 'message' => [ + 'template_name' => $nameTemplate, + 'template_content' => $contentTemplate + ] + ], JsonResponse::HTTP_OK); + } @@ -1547,30 +1870,52 @@ public function createTemplate(Request $request): JsonResponse * ) * ) */ -public function deleteTemplate($name): Response +public function deleteTemplate(string $name): Response { - #$templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; + $this->logger->info('Attempting to delete template.', ['templateName' => $name]); + $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; $filePath = $templateDir . '/' . $name; + $this->logger->debug('Computed file path for template.', ['filePath' => $filePath]); + // Comprobar si la plantilla existe if (!file_exists($filePath)) { + $this->logger->error('Template not found. Returning HTTP Response Code.', [ + 'templateName' => $name, + 'filePath' => $filePath, + 'responseCode' => Response::HTTP_NOT_FOUND + ]); return new Response(json_encode([ 'error' => 'TEMPLATE_NOT_FOUND', 'message' => 'No se encontró la plantilla especificada' ]), Response::HTTP_NOT_FOUND, ['Content-Type' => 'application/json']); } + $this->logger->debug('Template exists. Proceeding to delete.', ['filePath' => $filePath]); + // Intentar eliminar la plantilla try { unlink($filePath); + $this->logger->info('Template deleted successfully.', ['templateName' => $name, 'filePath' => $filePath]); } catch (\Exception $e) { + $this->logger->error('Failed to delete template. Returning HTTP Response Code.', [ + 'templateName' => $name, + 'filePath' => $filePath, + 'exception' => $e->getMessage(), + 'responseCode' => Response::HTTP_INTERNAL_SERVER_ERROR + ]); return new Response(json_encode([ 'error' => 'FAILED_TO_DELETE_TEMPLATE', 'message' => 'Ocurrió un error al intentar eliminar la plantilla' ]), Response::HTTP_INTERNAL_SERVER_ERROR, ['Content-Type' => 'application/json']); } + $this->logger->info('Returning success response.', [ + 'templateName' => $name, + 'responseCode' => Response::HTTP_OK + ]); + // Devolver respuesta exitosa return new Response(json_encode([ 'success' => 'Plantilla eliminada', diff --git a/src/OgBootBundle/Service/CurlRequestService.php b/src/OgBootBundle/Service/CurlRequestService.php index bba89d9..9e68192 100644 --- a/src/OgBootBundle/Service/CurlRequestService.php +++ b/src/OgBootBundle/Service/CurlRequestService.php @@ -2,68 +2,72 @@ // src/OgBootBundle/Service/CurlRequestService.php namespace App\OgBootBundle\Service; + use Exception; use Psr\Log\LoggerInterface; class CurlRequestService { + private LoggerInterface $logger; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } public function convertMaskToCIDR($mask) { $bits = 0; $mask = explode(".", $mask); - foreach ($mask as $octect) - $bits += strlen(str_replace("0", "", decbin($octect))); + foreach ($mask as $octet) { + $bits += strlen(str_replace("0", "", decbin($octet))); + } return $bits; } + public function callOgLive($parameter) + { + // Ruta completa al script oglivecli + $ogLiveCliPath = sprintf("%s/bin/oglivecli", dirname(dirname(dirname(__DIR__)))); -// src/Service/CurlRequestService.php + // Dividir el parámetro en acción y argumentos + $args = array_map('trim', explode(' ', $parameter)); + $action = array_shift($args); -public function callOgLive($parameter) -{ - // Ruta completa al script oglivecli - $ogLiveCliPath = sprintf("%s/bin/oglivecli", dirname(dirname(dirname(__DIR__)))); + // Registrar la acción y los argumentos + $this->logger->debug('Action: ' . $action); + $this->logger->debug('Arguments: ' . json_encode($args)); - // Dividir el parámetro en acción y argumentos - $args = array_map('trim', explode(' ', $parameter)); - $action = array_shift($args); + // Limpiar los argumentos de comillas innecesarias + $cleanedArgs = array_map(function ($arg) { + return trim($arg, '\'\"'); + }, $args); - // Registrar la acción y los argumentos - syslog(LOG_INFO, 'action ' . $action); - syslog(LOG_INFO, 'args ' . json_encode($args)); + // Construir el comando final sin añadir comillas alrededor de cada elemento + $commandToRun = $ogLiveCliPath . ' ' . $action . ' ' . implode(' ', $cleanedArgs); - // Limpiar los argumentos de comillas innecesarias - $cleanedArgs = array_map(function($arg) { - return trim($arg, '\'\"'); - }, $args); - // Construir el comando final sin añadir comillas alrededor de cada elemento - $commandToRun = $ogLiveCliPath . ' ' . $action . ' ' . implode(' ', $cleanedArgs); + // Registrar el comando para depuración + $this->logger->debug('Command: ' . $commandToRun); - // Registrar el comando en syslog para depuración - // Ejecutar el comando, capturando la salida y el código de salida - $output = []; - $exitCode = null; - exec($commandToRun, $output, $exitCode); // Ejecuta el comando, captura la salida y el código de salida + // Ejecutar el comando, capturando la salida y el código de salida + $output = []; + $exitCode = null; + exec($commandToRun, $output, $exitCode); - // Unir la salida en una sola cadena y registrar en syslog - $outputString = implode("\n", $output); - syslog(LOG_INFO, 'output ' . $outputString); - syslog(LOG_INFO, 'exitCode ' . $exitCode); + // Unir la salida en una sola cadena y registrar en el logger + $outputString = implode("\n", $output); + $this->logger->debug('Output: ' . $outputString); + $this->logger->debug('Exit Code: ' . $exitCode); - // Decodificar la salida JSON si es posible - $decodedOutput = json_decode($outputString, true); - error_log('Decoded Output: ' . print_r($decodedOutput, true)); - error_log('exitCode ' . $exitCode); - return [ - 'output' => $decodedOutput, // Retorna la salida decodificada (JSON) - 'exitCode' => $exitCode // Retorna el código de salida del comando - ]; + // Decodificar la salida JSON si es posible + $decodedOutput = json_decode($outputString, true); + $this->logger->debug('Decoded Output: ' . print_r($decodedOutput, true)); + + return [ + 'output' => $decodedOutput, // Retorna la salida decodificada (JSON) + 'exitCode' => $exitCode // Retorna el código de salida del comando + ]; + } } - - - -} - -- 2.40.1 From c33f87ccc671ac7f0211af8825eca1ee2c590b43 Mon Sep 17 00:00:00 2001 From: lgromero Date: Fri, 10 Jan 2025 07:48:23 +0100 Subject: [PATCH 2/2] refs #1131 #1256 adds symfony logs to journalctl with format --- .../Controller/OgBootController.php | 1406 +++++++++++------ 1 file changed, 923 insertions(+), 483 deletions(-) diff --git a/src/OgBootBundle/Controller/OgBootController.php b/src/OgBootBundle/Controller/OgBootController.php index 75f2326..4053c2a 100644 --- a/src/OgBootBundle/Controller/OgBootController.php +++ b/src/OgBootBundle/Controller/OgBootController.php @@ -214,38 +214,55 @@ public function getStatus(): Response */ public function getDownloadMenu(): Response { - $this->logger->info('Attempting to retrieve ISO download menu.'); + $operation = 'ogboot.getDownloadMenu'; + $component = 'ogboot'; - // Llamar al servicio para obtener las ISOs disponibles + // Log de inicio + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'desc' => 'Attempting to retrieve ISO download menu.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + // Obtener datos $downloadsOutput = $this->curlRequestService->callOgLive("download"); - // Verificar si hubo un error en la respuesta + // Error en la llamada if (is_array($downloadsOutput) && isset($downloadsOutput['error'])) { - $this->logger->error('Failed to retrieve ISOs.', [ - 'error' => $downloadsOutput['error'], - 'message' => $downloadsOutput['message'], - 'responseCode' => Response::HTTP_INTERNAL_SERVER_ERROR - ]); + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'params' => ['error' => $downloadsOutput['error']], + 'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR, + 'desc' => 'Failed to retrieve ISOs.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( - ['error' => 'FAILED_TO_RETRIEVE_ISOS', 'message' => $downloadsOutput['message']], + ['error' => 'FAILED_TO_RETRIEVE_ISOS', 'message' => $downloadsOutput['message']], Response::HTTP_INTERNAL_SERVER_ERROR ); } - $this->logger->info('ISOs retrieved successfully.', [ - 'responseCode' => Response::HTTP_OK, - 'downloads' => $downloadsOutput['output']['downloads'] ?? [] - ]); + // Log de salida exitosa + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'params' => [], + 'http_code' => Response::HTTP_OK, + 'desc' => 'ISOs retrieved successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Formatear la respuesta según el nuevo formato - $response = [ + // Respuesta + return new JsonResponse([ 'success' => 'ISOs retrieved successfully', - 'message' => $downloadsOutput['output']['downloads'] // Aquí la estructura ya está en el formato correcto de la salida del shell script - ]; - - return new JsonResponse($response, Response::HTTP_OK); + 'message' => $downloadsOutput['output']['downloads'] ?? [] + ], Response::HTTP_OK); } + /** * @Route("/ogboot/v1/oglives", name="getOglives", methods={"GET"}) * @OA\Get( @@ -295,90 +312,109 @@ public function getDownloadMenu(): Response public function getOglives(): Response { - // Log inicial: Inicio del endpoint - $this->logger->info('Fetching list of installed ogLive clients.'); + $operation = 'ogboot.getOglives'; // Nombre de la operación + $component = 'ogboot'; // Componente responsable - // Llama al servicio que ejecuta el comando oglivecli - $response = $this->curlRequestService->callOgLive("list_installed_oglives"); + // Log inicial: Inicio del endpoint + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'desc' => 'Fetching list of installed ogLive clients.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + $response = $this->curlRequestService->callOgLive('list_installed_oglives'); $ogLiveConfigResult = $response['output']; $exitCode = $response['exitCode']; // Log de depuración: Respuesta completa del servicio - $this->logger->debug( - sprintf('ogLive service response: %s | HTTP Code: %d', - json_encode($response), - Response::HTTP_OK) - ); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['response' => $response], + 'desc' => 'Service response for list_installed_oglives.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Manejo de errores basado en el código de salida if ($exitCode === 404) { - $httpCode = Response::HTTP_NOT_FOUND; - $this->logger->info( - sprintf('No ogLive clients found. | HTTP Code: %d', - $httpCode) - ); + $httpCode = (string) Response::HTTP_NOT_FOUND; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'No ogLive clients found.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( - ['error' => 'NOT_FOUND', 'message' => 'No ogLive clients found.'], - $httpCode + ['error' => 'NOT_FOUND', 'message' => 'No ogLive clients found.'], + Response::HTTP_NOT_FOUND ); } elseif ($exitCode === 500) { - $httpCode = Response::HTTP_INTERNAL_SERVER_ERROR; - $this->logger->error( - sprintf('Failed to retrieve ogLive clients due to server error. | HTTP Code: %d', - $httpCode) - ); + $httpCode = (string) Response::HTTP_INTERNAL_SERVER_ERROR; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'Failed to retrieve ogLive clients due to server error.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( - ['error' => 'SERVER_ERROR', 'message' => 'Failed to retrieve ogLive clients due to server error.'], - $httpCode + ['error' => 'SERVER_ERROR', 'message' => 'Failed to retrieve ogLive clients due to server error.'], + Response::HTTP_INTERNAL_SERVER_ERROR ); } elseif ($exitCode !== 0) { - $httpCode = Response::HTTP_INTERNAL_SERVER_ERROR; - $this->logger->error( - sprintf('Unknown error occurred. | HTTP Code: %d', - $httpCode) - ); + $httpCode = (string) Response::HTTP_INTERNAL_SERVER_ERROR; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'Unknown error occurred while retrieving ogLive clients.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( - ['error' => 'UNKNOWN_ERROR', 'message' => 'An unknown error occurred.'], - $httpCode + ['error' => 'UNKNOWN_ERROR', 'message' => 'An unknown error occurred.'], + Response::HTTP_INTERNAL_SERVER_ERROR ); } - // Verificar si la salida es válida if (empty($ogLiveConfigResult) || !isset($ogLiveConfigResult['installed_ogLives'])) { - $httpCode = Response::HTTP_NOT_FOUND; - $this->logger->info( - sprintf('No installed ogLive clients found in the service response. | HTTP Code: %d', - $httpCode) - ); + $httpCode = (string) Response::HTTP_NOT_FOUND; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'No installed ogLive clients found in the service response.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( - ['error' => 'NOT_FOUND', 'message' => 'No ogLive clients found.'], - $httpCode + ['error' => 'NOT_FOUND', 'message' => 'No ogLive clients found.'], + Response::HTTP_NOT_FOUND ); } - // Log de información: Número de ogLives encontrados - $httpCode = Response::HTTP_OK; - $this->logger->info( - sprintf('Number of installed ogLive clients: %d | HTTP Code: %d', - count($ogLiveConfigResult['installed_ogLives']), - $httpCode) - ); + $httpCode = (string) Response::HTTP_OK; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'ogLive clients retrieved successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Formatear la respuesta final en caso de éxito $response = [ 'success' => 'ogLive clients retrieved successfully', - 'message' => $ogLiveConfigResult // Incluye la estructura completa con default_oglive e installed_ogLives + 'message' => $ogLiveConfigResult // Incluye la estructura completa con default_oglive e installed_ogLives ]; - // Log de éxito: Respuesta enviada - $this->logger->info( - sprintf('ogLive clients retrieved successfully. | HTTP Code: %d', - $httpCode) - ); - - return new JsonResponse($response, $httpCode); + return new JsonResponse($response, Response::HTTP_OK); } + + /** * @Route("/ogboot/v1/oglives/default", name="getOgliveDefault", methods={"GET"}) @@ -407,50 +443,67 @@ public function getDownloadMenu(): Response */ public function getOgliveDefault(Request $request): Response { + $operation = 'ogboot.getOgliveDefault'; // Nombre de la operación + $component = 'ogboot'; // Componente responsable + // Log inicial: Inicio del endpoint - $this->logger->info('Fetching default ogLive configuration.'); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'desc' => 'Fetching default ogLive configuration.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); // Llamar al servicio que obtiene la configuración por defecto - $response = $this->curlRequestService->callOgLive("get_default"); + $response = $this->curlRequestService->callOgLive('get_default'); $result = $response['output']; $exitCode = $response['exitCode']; // Log de depuración: Respuesta completa del servicio - $this->logger->debug( - sprintf('ogLive default service response: %s | HTTP Code: %d', - json_encode($response), - Response::HTTP_OK) - ); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['response' => $response], + 'desc' => 'Service response for get_default.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); // Manejo de errores basado en el código de salida if ($exitCode !== 0) { - $httpCode = Response::HTTP_INTERNAL_SERVER_ERROR; + $httpCode = (string) Response::HTTP_INTERNAL_SERVER_ERROR; $errorMessage = $result['error'] ?? 'Failed to retrieve default ogLive.'; - $this->logger->error( - sprintf('Error fetching default ogLive. Message: %s | HTTP Code: %d', - $errorMessage, - $httpCode) - ); + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => $errorMessage + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'SERVER_ERROR', 'message' => $errorMessage], - $httpCode + Response::HTTP_INTERNAL_SERVER_ERROR ); } // Log de éxito: Respuesta válida obtenida - $httpCode = Response::HTTP_OK; - $this->logger->info( - sprintf('Default ogLive configuration retrieved successfully. | HTTP Code: %d', - $httpCode) - ); + $httpCode = (string) Response::HTTP_OK; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'Default ogLive configuration retrieved successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); // Devolver la respuesta en el formato solicitado return new JsonResponse( ['success' => 'se ha obtenido el oglive por defecto', 'message' => $result], - $httpCode + Response::HTTP_OK ); } + /** * @Route("/ogboot/v1/oglives/{checksum}", name="getOglive", methods={"GET"}) * @OA\Get( @@ -497,8 +550,17 @@ public function getOgliveDefault(Request $request): Response */ public function getOglive(string $checksum): Response { + $operation = 'ogboot.getOglive'; // Nombre de la operación + $component = 'ogboot'; // Componente responsable + // Log inicial: Inicio del endpoint con el checksum proporcionado - $this->logger->info(sprintf('Fetching ogLive client with checksum: %s.', $checksum)); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['checksum' => $checksum], + 'desc' => 'Fetching ogLive client.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); // Llamar al servicio ogLive para obtener la información del ogLive correspondiente $response = $this->curlRequestService->callOgLive("get_info " . escapeshellarg($checksum)); @@ -506,49 +568,62 @@ public function getOglive(string $checksum): Response $exitCode = $response['exitCode']; // Log de depuración: Respuesta completa del servicio - $this->logger->debug( - sprintf('ogLive service response: %s | HTTP Code: %d', - json_encode($response), - Response::HTTP_OK) - ); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['response' => $response], + 'desc' => 'Service response for get_info.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); // Manejo de errores basado en el código de salida if ($exitCode === 404) { - $httpCode = Response::HTTP_NOT_FOUND; - $this->logger->info( - sprintf('ogLive client with checksum %s not found. | HTTP Code: %d', - $checksum, - $httpCode) - ); + $httpCode = (string) Response::HTTP_NOT_FOUND; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['checksum' => $checksum], + 'desc' => 'ogLive client not found.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'NOT_FOUND', 'message' => "ogLive client with checksum $checksum not found."], - $httpCode + Response::HTTP_NOT_FOUND ); } elseif ($exitCode !== 0) { - $httpCode = Response::HTTP_INTERNAL_SERVER_ERROR; - $this->logger->error( - sprintf('Failed to retrieve ogLive client with checksum %s. | HTTP Code: %d', - $checksum, - $httpCode) - ); + $httpCode = (string) Response::HTTP_INTERNAL_SERVER_ERROR; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['checksum' => $checksum], + 'desc' => 'Failed to retrieve ogLive client.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'SERVER_ERROR', 'message' => 'Failed to retrieve ogLive client.'], - $httpCode + Response::HTTP_INTERNAL_SERVER_ERROR ); } // Log de éxito: Respuesta válida obtenida - $httpCode = Response::HTTP_OK; - $this->logger->info( - sprintf('ogLive client with checksum %s retrieved successfully. | HTTP Code: %d', - $checksum, - $httpCode) - ); + $httpCode = (string) Response::HTTP_OK; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['checksum' => $checksum], + 'desc' => 'ogLive client retrieved successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); // Formatear la respuesta de éxito con el ogLive encontrado return new JsonResponse( ['success' => 'ogLive client retrieved successfully', 'message' => $result], - $httpCode + Response::HTTP_OK ); } @@ -602,75 +677,92 @@ public function getOglive(string $checksum): Response */ public function setOgliveDefault(Request $request): Response { - // Log inicial: Inicio del endpoint - $this->logger->info('Setting ogLive client as default.'); + $operation = 'ogboot.setOgliveDefault'; + $component = 'ogboot'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'desc' => 'Setting ogLive client as default.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Decodificar el contenido de la solicitud $data = json_decode($request->getContent(), true); - // Validar que el checksum está presente en los datos de entrada if (!isset($data['checksum'])) { - $httpCode = Response::HTTP_BAD_REQUEST; - $this->logger->error( - sprintf('Invalid input data: checksum is required. | HTTP Code: %d', - $httpCode) - ); + $httpCode = (string) Response::HTTP_BAD_REQUEST; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'Invalid input data: checksum is required.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'INVALID_INPUT', 'message' => 'Invalid input data: checksum is required.'], - $httpCode + Response::HTTP_BAD_REQUEST ); } $checksum = $data['checksum']; - // Log de depuración: Cheksum recibido - $this->logger->debug( - sprintf('Checksum received for setting default: %s.', $checksum) - ); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['checksum' => $checksum], + 'desc' => 'Checksum received for setting default.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Llamar al servicio para establecer el ogLive como predeterminado utilizando el checksum proporcionado $response = $this->curlRequestService->callOgLive("set_default " . escapeshellarg($checksum)); $result = $response['output']; $exitCode = $response['exitCode']; - // Log de depuración: Respuesta completa del servicio - $this->logger->debug( - sprintf('ogLive service response: %s | Exit Code: %d', - json_encode($response), - $exitCode) - ); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['response' => $response], + 'desc' => 'Service response for set_default.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Manejo de errores basado en el código de salida if ($exitCode !== 0) { - $httpCode = Response::HTTP_INTERNAL_SERVER_ERROR; + $httpCode = (string) Response::HTTP_INTERNAL_SERVER_ERROR; $errorMessage = $result['error'] ?? 'Failed to set ogLive as default.'; - $this->logger->error( - sprintf('Error setting ogLive client as default. Message: %s | HTTP Code: %d', - $errorMessage, - $httpCode) - ); + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['checksum' => $checksum], + 'desc' => $errorMessage + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'SERVER_ERROR', 'message' => $errorMessage], - $httpCode + Response::HTTP_INTERNAL_SERVER_ERROR ); } - // Log de éxito: ogLive configurado como predeterminado - $httpCode = Response::HTTP_OK; - $this->logger->info( - sprintf('ogLive client with checksum %s set as default successfully. | HTTP Code: %d', - $checksum, - $httpCode) - ); + $httpCode = (string) Response::HTTP_OK; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['checksum' => $checksum], + 'desc' => 'ogLive client set as default successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Devolver la respuesta en el formato solicitado return new JsonResponse( ['success' => 'ogLive client set as default successfully', 'message' => $result], - $httpCode + Response::HTTP_OK ); } + /** * @Route("/ogboot/v1/oglives/install", name="installOglive", methods={"POST"}) * @OA\Post( @@ -726,92 +818,113 @@ public function setOgliveDefault(Request $request): Response public function installOglive(Request $request): JsonResponse { - // Log inicial: Inicio del endpoint - $this->logger->info('Initiating ogLive client installation process.'); + $operation = 'ogboot.installOglive'; + $component = 'ogboot'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'desc' => 'Initiating ogLive client installation process.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Obtener los datos del cuerpo de la petición $data = json_decode($request->getContent(), true); - // Validar que los datos requeridos están presentes if (!isset($data['url']) || !isset($data['id'])) { - $httpCode = JsonResponse::HTTP_BAD_REQUEST; - $this->logger->error( - sprintf('Invalid input data. URL or Transaction ID missing. | HTTP Code: %d', - $httpCode) - ); - return new JsonResponse(['error' => 'Invalid input data'], $httpCode); + $httpCode = (string) JsonResponse::HTTP_BAD_REQUEST; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'Invalid input data. URL or Transaction ID missing.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + return new JsonResponse(['error' => 'Invalid input data'], JsonResponse::HTTP_BAD_REQUEST); } - // Obtener la URL del ISO y el ID de la transacción $isoUrl = $data['url']; $transactionId = $data['id']; - // Log de depuración: URL e ID recibidos - $this->logger->debug( - sprintf('ISO URL: %s | Transaction ID: %s', - $isoUrl, - $transactionId) - ); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['url' => $isoUrl, 'transaction_id' => $transactionId], + 'desc' => 'Received ISO URL and Transaction ID.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); try { - // Construir el comando Symfony para ejecutar en segundo plano $consolePath = $this->params->get('kernel.project_dir') . '/bin/console'; - $this->logger->info(sprintf('Symfony console path: %s', $consolePath)); - - // Log del comando que será ejecutado $command = sprintf( - 'php %s oglive:install %s %s > /dev/null 2>&1 ', + 'php %s oglive:install %s %s > /dev/null 2>&1', escapeshellarg($consolePath), escapeshellarg($isoUrl), escapeshellarg($transactionId) ); - $this->logger->info('Command to be executed: ' . $command); - // Crear y configurar el proceso + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['command' => $command], + 'desc' => 'Command constructed for execution.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + $process = Process::fromShellCommandline($command); $process->setTimeout(null); $process->disableOutput(); try { - // Iniciar el proceso en segundo plano $process->start(); - $this->logger->info( - sprintf('ogLive client installation process started successfully. | Transaction ID: %s', - $transactionId) - ); - // Respuesta inicial indicando que el proceso ha sido iniciado + $httpCode = (string) JsonResponse::HTTP_ACCEPTED; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['transaction_id' => $transactionId], + 'desc' => 'ogLive client installation process started successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse([ 'success' => 'ogLive client installation started', 'transaction_id' => $transactionId ], JsonResponse::HTTP_ACCEPTED); } catch (\Exception $e) { - // Log del error al intentar iniciar el proceso - $this->logger->error( - sprintf('Error starting ogLive client installation process. Message: %s', - $e->getMessage()) - ); - throw $e; // Rethrow para el bloque catch externo + $httpCode = (string) JsonResponse::HTTP_INTERNAL_SERVER_ERROR; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'Error starting ogLive client installation process.', + 'params' => ['message' => $e->getMessage()] + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + throw $e; } + } catch (\Exception $e) { + $httpCode = (string) JsonResponse::HTTP_INTERNAL_SERVER_ERROR; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'Failed to initiate ogLive installation.', + 'params' => ['message' => $e->getMessage()] + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - } catch (Exception $e) { - // Log del error general - $httpCode = JsonResponse::HTTP_INTERNAL_SERVER_ERROR; - $this->logger->error( - sprintf('Failed to initiate ogLive client installation. Message: %s | HTTP Code: %d', - $e->getMessage(), - $httpCode) - ); - - // Respuesta de error al cliente return new JsonResponse([ 'error' => 'Failed to initiate ogLive installation', 'details' => $e->getMessage() - ], $httpCode); + ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); } } + /** * Enviar el resultado al webhook @@ -877,54 +990,111 @@ public function setOgliveDefault(Request $request): Response */ public function uninstallOglive(string $checksum): Response { - // Log inicial: Inicio del endpoint con el checksum proporcionado - $this->logger->info(sprintf('Initiating ogLive client uninstallation for checksum: %s', $checksum)); + $operation = 'ogboot.uninstallOglive'; + $component = 'ogboot'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['checksum' => $checksum], + 'desc' => 'Initiating ogLive client uninstallation.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Llamada al servicio para desinstalar el cliente ogLive $response = $this->curlRequestService->callOgLive("uninstall " . escapeshellarg($checksum)); $result = $response['output']; $exitCode = $response['exitCode']; - // Log de depuración: Respuesta completa del servicio - $this->logger->debug(sprintf('Service response: %s | Exit Code: %d', json_encode($result), $exitCode)); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['response' => $result, 'exitCode' => $exitCode], + 'desc' => 'Service response for ogLive uninstallation.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Verificar la respuesta del servicio según el código de salida if ($exitCode === 404) { - // Log: Cliente ogLive no encontrado - $this->logger->info(sprintf('ogLive client not found for checksum: %s | HTTP Code: 404', $checksum)); + $httpCode = '404'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['checksum' => $checksum], + 'desc' => 'ogLive client not found.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse([ 'error' => 'NOT_FOUND', 'message' => $result['message'] ?? 'ogLive client not found.' ], Response::HTTP_NOT_FOUND); - } elseif ($exitCode === 403) { - // Log: Intento de desinstalar el ogLive predeterminado - $this->logger->warning(sprintf('Attempt to uninstall the default ogLive client. | Checksum: %s | HTTP Code: 403', $checksum)); + } + + if ($exitCode === 403) { + $httpCode = '403'; + $this->logger->warning(json_encode([ + 'severity' => 'WARNING', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['checksum' => $checksum], + 'desc' => 'Attempt to uninstall the default ogLive client.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse([ 'error' => 'FORBIDDEN', 'message' => $result['message'] ?? 'Cannot uninstall the default ogLive client.' ], Response::HTTP_FORBIDDEN); - } elseif ($exitCode === 500) { - // Log: Error interno del servidor - $this->logger->error(sprintf('Server error during ogLive uninstallation for checksum: %s | HTTP Code: 500', $checksum)); + } + + if ($exitCode === 500) { + $httpCode = '500'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['checksum' => $checksum], + 'desc' => 'Server error during ogLive uninstallation.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse([ 'error' => 'SERVER_ERROR', 'message' => $result['message'] ?? 'Failed to uninstall ogLive client.' ], Response::HTTP_INTERNAL_SERVER_ERROR); - } elseif ($exitCode === 200) { - // Log: Desinstalación exitosa - $this->logger->info(sprintf('ogLive client uninstalled successfully for checksum: %s | HTTP Code: 200', $checksum)); + } + + if ($exitCode === 200) { + $httpCode = '200'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['checksum' => $checksum], + 'desc' => 'ogLive client uninstalled successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse([ 'message' => $result['message'], 'details' => $result['details'] ?? '' ], Response::HTTP_OK); - } else { - // Log: Error desconocido - $this->logger->error(sprintf('Unknown error during ogLive uninstallation for checksum: %s | HTTP Code: 500', $checksum)); - return new JsonResponse([ - 'error' => 'UNKNOWN_ERROR', - 'message' => 'An unknown error occurred.' - ], Response::HTTP_INTERNAL_SERVER_ERROR); } + + $httpCode = '500'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['checksum' => $checksum], + 'desc' => 'Unknown error during ogLive uninstallation.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + return new JsonResponse([ + 'error' => 'UNKNOWN_ERROR', + 'message' => 'An unknown error occurred.' + ], Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -962,33 +1132,68 @@ public function uninstallOglive(string $checksum): Response */ public function getBootFiles(): JsonResponse { - #$directory = '/opt/ogboot/tftpboot/ipxe_scripts'; + $operation = 'ogboot.getBootFiles'; + $component = 'ogboot'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'desc' => 'Attempting to retrieve boot files.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + $directory = $this->tftpbootDir . '/ipxe_scripts'; - // Verificar si el directorio existe + if (!is_dir($directory)) { + $httpCode = '500'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['directory' => $directory], + 'desc' => 'Failed to retrieve boot files: directory not found.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'SERVER_ERROR', 'message' => 'Failed to retrieve boot files: directory not found.'], Response::HTTP_INTERNAL_SERVER_ERROR ); } - // Obtener la lista de archivos $files = scandir($directory); - // Filtrar los archivos que comiencen con '01-' y excluir directorios $bootFiles = array_filter($files, function ($file) use ($directory) { return !is_dir($directory . '/' . $file) && strpos($file, '01-') === 0; }); - // Si no se encuentran archivos if (empty($bootFiles)) { + $httpCode = '404'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['directory' => $directory], + 'desc' => 'No boot files found.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'NOT_FOUND', 'message' => 'No boot files found.'], Response::HTTP_NOT_FOUND ); } - // Formatear la respuesta de éxito + $httpCode = '200'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['boot_files' => array_values($bootFiles)], + 'desc' => 'Boot files retrieved successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + $response = [ 'success' => 'Boot files retrieved successfully', 'message' => array_values($bootFiles) @@ -998,6 +1203,7 @@ public function getBootFiles(): JsonResponse } + /** * @Route("/ogboot/v1/pxes/{mac}", name="ogboot_get_boot_file_with_params", methods={"GET"}) * @OA\Get( @@ -1064,83 +1270,102 @@ public function getBootFiles(): JsonResponse public function getBootFile(string $mac): Response { - // Log inicial: Inicio del endpoint - $this->logger->info('Retrieving boot file for MAC address.', ['mac' => $mac]); + $operation = 'ogboot.getBootFile'; + $component = 'ogboot'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['mac' => $mac], + 'desc' => 'Retrieving boot file for MAC address.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Ruta donde están alojados los archivos de arranque $directory = $this->tftpbootDir . '/ipxe_scripts'; - - // Validar y formatear la dirección MAC $mac = $this->validateAndFormatMac($mac ?? null); $fileName = "01-" . $mac; $filePath = "$directory/$fileName"; - - // Normalizar la ruta del archivo para evitar accesos fuera del directorio permitido $realFilePath = realpath($directory) . '/' . $fileName; - // Log de depuración: Verificar la ruta del archivo - $this->logger->debug('Computed file path for boot file.', ['realFilePath' => $realFilePath]); - - // Verificar que el archivo está dentro del directorio permitido if (strpos($realFilePath, realpath($directory)) !== 0) { - $this->logger->warning('Unauthorized access attempt outside the boot directory.', [ - 'mac' => $mac, - 'attemptedPath' => $realFilePath - ]); + $httpCode = '403'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['mac' => $mac, 'attemptedPath' => $realFilePath], + 'desc' => 'Unauthorized access attempt outside the boot directory.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( - [ - 'error' => 'UNAUTHORIZED_ACCESS', - 'message' => 'Attempted unauthorized access outside the boot directory.' - ], + ['error' => 'UNAUTHORIZED_ACCESS', 'message' => 'Attempted unauthorized access outside the boot directory.'], Response::HTTP_FORBIDDEN ); } - // Verificar si el archivo existe if (!file_exists($realFilePath)) { - $this->logger->info('Boot file not found for specified MAC address.', ['mac' => $mac]); + $httpCode = '404'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['mac' => $mac], + 'desc' => 'Boot file not found for specified MAC address.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'NOT_FOUND', 'message' => 'No boot file found for the specified MAC address.'], Response::HTTP_NOT_FOUND ); } - // Leer el contenido del archivo $content = file_get_contents($realFilePath); if ($content === false) { - $this->logger->error('Failed to read the boot file.', ['filePath' => $realFilePath]); + $httpCode = '500'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['filePath' => $realFilePath], + 'desc' => 'Failed to read the boot file.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'FAILED_TO_READ_BOOT_FILE', 'message' => 'Error reading the boot file.'], Response::HTTP_INTERNAL_SERVER_ERROR ); } - // Log de depuración: Contenido del archivo leído - $this->logger->debug('Boot file content retrieved.', ['filePath' => $realFilePath, 'content' => $content]); - - // Valores por defecto $templateName = 'unknown'; $oglivedir = ''; $kernelArgs = ''; - // Obtener el nombre de la plantilla if (preg_match('/#Template:\s*(.*)/', $content, $matches)) { $templateName = trim($matches[1]); } - // Obtener el valor de ISODIR if (preg_match('/set ISODIR\s+(.*)/', $content, $matches)) { $oglivedir = trim($matches[1]); } - // Obtener los kernelargs if (preg_match('/set kernelargs\s+(.*)/', $content, $matches)) { $kernelArgs = trim($matches[1]); } - // Si no se encuentran kernelargs, se asume un archivo de arranque por disco if (empty($kernelArgs)) { - $this->logger->info('Boot file retrieved without kernel parameters.', ['mac' => $mac, 'templateName' => $templateName]); + $httpCode = '200'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['mac' => $mac, 'templateName' => $templateName], + 'desc' => 'Boot file retrieved without kernel parameters.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( [ 'success' => 'Boot file retrieved successfully', @@ -1154,10 +1379,8 @@ public function getBootFiles(): JsonResponse ); } - // Parsear los argumentos del kernel parse_str(str_replace(' ', '&', $kernelArgs), $params); - // Crear la estructura del resultado $result = [ 'template_name' => $templateName, 'mac' => $mac, @@ -1184,13 +1407,16 @@ public function getBootFiles(): JsonResponse 'resolution' => $params['vga'] ?? '', ]; - // Log de éxito - $this->logger->info('Boot file retrieved successfully.', ['mac' => $mac, 'templateName' => $templateName]); + $httpCode = '200'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['mac' => $mac, 'templateName' => $templateName], + 'desc' => 'Boot file retrieved successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Log de depuración: Resultado completo - $this->logger->debug('Boot file parsed result.', ['result' => $result]); - - // Formatear la respuesta de éxito return new JsonResponse( ['success' => 'Boot file retrieved successfully', 'message' => $result], Response::HTTP_OK @@ -1198,6 +1424,7 @@ public function getBootFiles(): JsonResponse } + @@ -1239,106 +1466,130 @@ public function getBootFiles(): JsonResponse */ public function createBootFile(Request $request): JsonResponse { - $this->logger->info('Starting PXE file creation process.'); + $operation = 'ogboot.createBootFile'; + $component = 'ogboot'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'desc' => 'Starting PXE file creation process.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); $data = json_decode($request->getContent(), true); - $templateName = $data['template_name'] ?? null; $mac = $this->validateAndFormatMac($data['mac'] ?? null); - - // Si nos pasan el puerto se lo quitamos ya que server_ip siempre tirará por Samba $serverIp = $data['server_ip'] ?? null; + if ($serverIp && strpos($serverIp, ':') !== false) { $serverIp = explode(':', $serverIp)[0]; } + $ogLiveDir = $data['oglivedir'] ?? 'ogLive'; - // Log de entrada - $this->logger->debug('Input data.', [ - 'template_name' => $templateName, - 'mac' => $mac, - 'server_ip' => $serverIp, - 'oglivedir' => $ogLiveDir - ]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => [ + 'template_name' => $templateName, + 'mac' => $mac, + 'server_ip' => $serverIp, + 'oglivedir' => $ogLiveDir + ], + 'desc' => 'Input data for PXE file creation.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Verificación de los campos obligatorios if (!$templateName || !$mac) { - $this->logger->warning('Missing required fields: mac and/or template_name.', [ - 'missingFields' => !$templateName ? 'template_name' : 'mac' - ]); - return new JsonResponse(['error' => 'Missing required fields: mac and template_name'], Response::HTTP_BAD_REQUEST); + $httpCode = '400'; + $this->logger->warning(json_encode([ + 'severity' => 'WARNING', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'Missing required fields: mac and/or template_name.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + return new JsonResponse( + ['error' => 'Missing required fields: mac and template_name'], + Response::HTTP_BAD_REQUEST + ); } - // Validación de la plantilla if (!preg_match('/^[a-zA-Z0-9._-]+$/', $templateName) || strpos($templateName, '..') !== false) { - $this->logger->warning('Invalid template name or unauthorized access attempt.', ['template_name' => $templateName]); + $httpCode = '400'; + $this->logger->warning(json_encode([ + 'severity' => 'WARNING', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['template_name' => $templateName], + 'desc' => 'Invalid template name or unauthorized access attempt.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'INVALID_TEMPLATE_NAME', 'message' => 'Invalid template name or unauthorized access attempt.'], Response::HTTP_BAD_REQUEST ); } - $parameters = [ - 'LANG' => $data['lang'] ?? 'es_ES.UTF-8', - 'ip' => $data['ip'] ?? '', - 'router' => $data['router'] ?? '', - 'netmask' => $data['netmask'] ?? '', - 'computer_name' => $data['computer_name'] ?? '', - 'netiface' => $data['netiface'] ?? '', - 'group' => $data['group'] ?? '', - 'ogrepo' => isset($data['ogrepo']) ? explode(':', $data['ogrepo'])[0] : '', - 'ogcore' => isset($data['ogcore']) ? explode(':', $data['ogcore'])[0] : '', - 'oglive' => isset($data['oglive']) ? explode(':', $data['oglive'])[0] : $serverIp, - 'oglog' => isset($data['oglog']) ? explode(':', $data['oglog'])[0] : $serverIp, - 'ogshare' => isset($data['ogshare']) ? explode(':', $data['ogshare'])[0] : $serverIp, - 'oglivedir' => $data['oglivedir'] ?? '', - 'ogprof' => $data['ogprof'] ?? 'false', - 'hardprofile' => $data['hardprofile'] ?? '', - 'ogntp' => $data['ogntp'] ?? '', - 'ogdns' => $data['ogdns'] ?? '', - 'ogproxy' => $data['ogproxy'] ?? '', - 'ogunit' => $data['ogunit'] ?? '', - 'resolution' => $data['resolution'] ?? '788' - ]; - $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; $templatePath = $templateDir . '/' . $templateName; - // Verificar si la plantilla existe if (!file_exists($templatePath)) { - $this->logger->info('Template not found.', ['template_path' => $templatePath]); - return new JsonResponse(['error' => 'TEMPLATE_NOT_FOUND', 'message' => 'Template not found.'], Response::HTTP_NOT_FOUND); + $httpCode = '404'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['template_path' => $templatePath], + 'desc' => 'Template not found.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + return new JsonResponse( + ['error' => 'TEMPLATE_NOT_FOUND', 'message' => 'Template not found.'], + Response::HTTP_NOT_FOUND + ); } $templateContent = file_get_contents($templatePath); if ($templateContent === false) { - $this->logger->error('Failed to read the template.', ['template_path' => $templatePath]); - return new JsonResponse(['error' => 'FAILED_TO_READ_TEMPLATE', 'message' => 'Failed to read template.'], Response::HTTP_INTERNAL_SERVER_ERROR); + $httpCode = '500'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['template_path' => $templatePath], + 'desc' => 'Failed to read the template.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + return new JsonResponse( + ['error' => 'FAILED_TO_READ_TEMPLATE', 'message' => 'Failed to read template.'], + Response::HTTP_INTERNAL_SERVER_ERROR + ); } - $this->logger->debug('Template content loaded.', ['template_name' => $templateName]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'desc' => 'Template content loaded.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Construcción de los argumentos del kernel $kernelArgs = 'ro boot=oginit quiet splash irqpoll acpi=on og2nd=sqfs ogprotocol=smb ogactiveadmin=true ogdebug=true ogtmpfs=15 ' . 'oglivedir=${ISODIR} ' . - 'LANG=' . $parameters['LANG'] . ' ' . - 'ip=' . $parameters['ip'] . ':' . $serverIp . ':' . $parameters['router'] . ':' . $parameters['netmask'] . ':' . $parameters['computer_name'] . ':' . $parameters['netiface'] . ':none ' . - 'group=' . str_replace(' ', '_', trim($parameters['group'])) . ' ' . - 'ogrepo=' . $parameters['ogrepo'] . ' ' . - 'ogcore=' . $parameters['ogcore'] . ' ' . - 'oglive=' . $parameters['oglive'] . ' ' . - 'oglog=' . $parameters['oglog'] . ' ' . - 'ogshare=' . $parameters['ogshare'] . ' ' . - 'ogprof=' . ($parameters['ogprof'] === 'true' ? 'true' : 'false') . ' ' . - (!empty($parameters['hardprofile']) ? 'hardprofile=' . str_replace(' ', '_', trim($parameters['hardprofile'])) . ' ' : '') . - (!empty($parameters['ogntp']) ? 'ogntp=' . $parameters['ogntp'] . ' ' : '') . - (!empty($parameters['ogdns']) ? 'ogdns=' . $parameters['ogdns'] . ' ' : '') . - (!empty($parameters['ogproxy']) ? 'ogproxy=' . $parameters['ogproxy'] . ' ' : '') . - (!empty($parameters['ogunit']) ? 'ogunit=' . $parameters['ogunit'] . ' ' : '') . - (is_numeric($parameters['resolution']) && $parameters['resolution'] <= 999 ? 'vga=' . $parameters['resolution'] : - (strpos($parameters['resolution'], ':') !== false ? 'video=' . $parameters['resolution'] : ' ' . $parameters['resolution'])); + 'LANG=' . ($data['lang'] ?? 'es_ES.UTF-8') . ' ' . + 'ip=' . ($data['ip'] ?? '') . ':' . $serverIp . ':' . ($data['router'] ?? '') . ':' . ($data['netmask'] ?? '') . ':' . ($data['computer_name'] ?? '') . ':' . ($data['netiface'] ?? '') . ':none '; - $this->logger->debug('Kernel arguments constructed.', ['kernelArgs' => $kernelArgs]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['kernelArgs' => $kernelArgs], + 'desc' => 'Kernel arguments constructed.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); $serverIpPort = $this->ogBootIP; if (!empty($this->ogBootPort)) { @@ -1351,22 +1602,30 @@ public function createBootFile(Request $request): JsonResponse $templateContent ); - // Insertar el comentario con el nombre de la plantilla después de #!ipxe - if (strpos($pxeContent, '#!ipxe') === 0) { - $pxeContent = "#!ipxe\n#Template: $templateName\n" . substr($pxeContent, 6); - } else { - $pxeContent = "#!ipxe\n#Template: $templateName\n" . $pxeContent; + if (file_put_contents($this->tftpbootDir . '/ipxe_scripts/01-' . $mac, $pxeContent) === false) { + $httpCode = '500'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'Failed to create PXE file.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + return new JsonResponse( + ['error' => 'FAILED_TO_CREATE_PXE_FILE', 'message' => 'Failed to create PXE file.'], + Response::HTTP_INTERNAL_SERVER_ERROR + ); } - $pxeFileName = '01-' . $mac; - $pxeFilePath = $this->tftpbootDir . '/ipxe_scripts/' . $pxeFileName; - - if (file_put_contents($pxeFilePath, $pxeContent) === false) { - $this->logger->error('Failed to create PXE file.', ['pxe_file_path' => $pxeFilePath]); - return new JsonResponse(['error' => 'FAILED_TO_CREATE_PXE_FILE', 'message' => 'Failed to create PXE file.'], Response::HTTP_INTERNAL_SERVER_ERROR); - } - - $this->logger->info('PXE file created successfully.', ['pxe_file_path' => $pxeFilePath, 'template_name' => $templateName]); + $httpCode = '200'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'desc' => 'PXE file created successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse([ 'success' => 'PXE file created successfully', @@ -1375,6 +1634,7 @@ public function createBootFile(Request $request): JsonResponse } + function validateAndFormatMac($mac) { @@ -1439,60 +1699,98 @@ function validateAndFormatMac($mac) */ public function deleteBootFile(string $mac): Response { - $this->logger->info('Attempting to delete boot file.', ['mac' => $mac]); + $operation = 'ogboot.deleteBootFile'; + $component = 'ogboot'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['mac' => $mac], + 'desc' => 'Attempting to delete boot file.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Ruta del directorio donde están los archivos de arranque $directory = $this->tftpbootDir . '/ipxe_scripts'; - - // Validar y formatear la dirección MAC $mac = $this->validateAndFormatMac($mac ?? null); $fileName = "01-" . $mac; $filePath = "$directory/$fileName"; - $this->logger->debug('Computed file path for boot file.', ['filePath' => $filePath]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['filePath' => $filePath], + 'desc' => 'Computed file path for boot file.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Verificar si el archivo de arranque existe if (!file_exists($filePath)) { - $this->logger->info('Boot file not found. Returning HTTP Response Code.', [ - 'mac' => $mac, - 'filePath' => $filePath, - 'responseCode' => Response::HTTP_NOT_FOUND - ]); + $httpCode = '404'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => [ + 'mac' => $mac, + 'filePath' => $filePath + ], + 'desc' => 'Boot file not found.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'NOT_FOUND', 'message' => 'No boot file found for the specified MAC address.'], Response::HTTP_NOT_FOUND ); } - $this->logger->debug('Boot file exists, proceeding to delete.', ['filePath' => $filePath]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['filePath' => $filePath], + 'desc' => 'Boot file exists, proceeding to delete.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Intentar eliminar el archivo de arranque if (!unlink($filePath)) { - $this->logger->error('Failed to delete boot file. Returning HTTP Response Code.', [ - 'mac' => $mac, - 'filePath' => $filePath, - 'responseCode' => Response::HTTP_INTERNAL_SERVER_ERROR - ]); + $httpCode = '500'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => [ + 'mac' => $mac, + 'filePath' => $filePath + ], + 'desc' => 'Failed to delete boot file.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( ['error' => 'SERVER_ERROR', 'message' => 'Failed to delete the boot file due to server error.'], Response::HTTP_INTERNAL_SERVER_ERROR ); } - // Registro de éxito - $this->logger->info('Boot file deleted successfully. Returning HTTP Response Code.', [ - 'mac' => $mac, - 'filePath' => $filePath, - 'responseCode' => Response::HTTP_OK - ]); + $httpCode = '200'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => [ + 'mac' => $mac, + 'filePath' => $filePath + ], + 'desc' => 'Boot file deleted successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Respuesta exitosa con formato estándar return new JsonResponse( ['success' => 'Boot file deleted successfully', 'message' => "The boot file for MAC $mac has been deleted."], Response::HTTP_OK ); } + /** * @Route("/ogboot/v1/pxe-templates", methods={"GET"}) @@ -1526,48 +1824,78 @@ public function deleteBootFile(string $mac): Response */ public function getAllTemplates(): JsonResponse { - $this->logger->info('Attempting to retrieve all templates.'); + $operation = 'ogboot.getAllTemplates'; + $component = 'ogboot'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'desc' => 'Attempting to retrieve all templates.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; - // Verificar si el directorio existe if (!is_dir($templateDir)) { - $this->logger->error('Template directory not found. Returning HTTP Response Code.', [ - 'templateDir' => $templateDir, - 'responseCode' => Response::HTTP_INTERNAL_SERVER_ERROR - ]); + $httpCode = '500'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['templateDir' => $templateDir], + 'desc' => 'Template directory not found.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse( - ['error' => 'SERVER_ERROR', 'message' => 'Directorio de plantillas no encontrado'], + ['error' => 'SERVER_ERROR', 'message' => 'Directorio de plantillas no encontrado'], Response::HTTP_INTERNAL_SERVER_ERROR ); } - $this->logger->debug('Template directory exists.', ['templateDir' => $templateDir]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['templateDir' => $templateDir], + 'desc' => 'Template directory exists.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Obtener la lista de archivos en el directorio $files = scandir($templateDir); - // Filtrar los archivos válidos (excluir directorios y archivos ocultos) $templates = array_filter($files, function ($file) use ($templateDir) { return !in_array($file, ['.', '..']) && !is_dir($templateDir . '/' . $file); }); - // Registro del resultado obtenido - $this->logger->info('Templates retrieved successfully. Returning HTTP Response Code.', [ - 'templateDir' => $templateDir, - 'templateCount' => count($templates), - 'responseCode' => Response::HTTP_OK - ]); - $this->logger->debug('Template list.', ['templates' => array_values($templates)]); + $httpCode = '200'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => [ + 'templateDir' => $templateDir, + 'templateCount' => count($templates) + ], + 'desc' => 'Templates retrieved successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['templates' => array_values($templates)], + 'desc' => 'Template list retrieved.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Formatear la respuesta siguiendo el estándar return new JsonResponse( - ['success' => 'Lista de plantillas obtenida con éxito', 'message' => array_values($templates)], + ['success' => 'Lista de plantillas obtenida con éxito', 'message' => array_values($templates)], Response::HTTP_OK ); } + /** * @Route("/ogboot/v1/pxe-templates/{templateName}", name="get_template", methods={"GET"}) * @OA\Get( @@ -1611,49 +1939,89 @@ public function getAllTemplates(): JsonResponse public function getTemplate(string $templateName): Response { - $this->logger->info('Attempting to retrieve template.', ['templateName' => $templateName]); + $operation = 'ogboot.getTemplate'; + $component = 'ogboot'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['templateName' => $templateName], + 'desc' => 'Attempting to retrieve template.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; $filePath = "$templateDir/$templateName"; - $this->logger->debug('Computed file path for template.', ['filePath' => $filePath]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['filePath' => $filePath], + 'desc' => 'Computed file path for template.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Comprobar si el archivo de plantilla existe if (!file_exists($filePath)) { - $this->logger->error('Template not found. Returning HTTP Response Code.', [ - 'templateName' => $templateName, - 'filePath' => $filePath, - 'responseCode' => Response::HTTP_NOT_FOUND - ]); + $httpCode = '404'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['templateName' => $templateName, 'filePath' => $filePath], + 'desc' => 'Template not found.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new Response(json_encode([ 'error' => 'TEMPLATE_NOT_FOUND', 'message' => 'No se encontró la plantilla especificada' ]), Response::HTTP_NOT_FOUND, ['Content-Type' => 'application/json']); } - $this->logger->debug('Template found, attempting to read content.', ['filePath' => $filePath]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['filePath' => $filePath], + 'desc' => 'Template found, attempting to read content.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Intentar leer el contenido de la plantilla $content = file_get_contents($filePath); if ($content === false) { - $this->logger->error('Failed to read template content. Returning HTTP Response Code.', [ - 'templateName' => $templateName, - 'filePath' => $filePath, - 'responseCode' => Response::HTTP_INTERNAL_SERVER_ERROR - ]); + $httpCode = '500'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['templateName' => $templateName, 'filePath' => $filePath], + 'desc' => 'Failed to read template content.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new Response(json_encode([ 'error' => 'FAILED_TO_READ_TEMPLATE', 'message' => 'Error al leer la plantilla' ]), Response::HTTP_INTERNAL_SERVER_ERROR, ['Content-Type' => 'application/json']); } - $this->logger->info('Template retrieved successfully. Returning HTTP Response Code.', [ - 'templateName' => $templateName, - 'responseCode' => Response::HTTP_OK - ]); - $this->logger->debug('Template content.', ['templateContent' => $content]); + $httpCode = '200'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['templateName' => $templateName], + 'desc' => 'Template retrieved successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['templateContent' => $content], + 'desc' => 'Template content retrieved.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Devolver la respuesta con el nombre y contenido de la plantilla return new Response(json_encode([ 'success' => 'Plantilla obtenida con éxito', 'template_name' => $templateName, @@ -1724,15 +2092,29 @@ public function getAllTemplates(): JsonResponse public function createTemplate(Request $request): JsonResponse { - $this->logger->info('Attempting to create a new template.'); + $operation = 'ogboot.createTemplate'; + $component = 'ogboot'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'desc' => 'Attempting to create a new template.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); $data = json_decode($request->getContent(), true); if (!isset($data['name_template']) || !isset($data['content_template'])) { - $this->logger->error('Missing required fields: name_template or content_template.', [ - 'requestData' => $data, - 'responseCode' => JsonResponse::HTTP_BAD_REQUEST - ]); + $httpCode = '400'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['requestData' => $data], + 'desc' => 'Missing required fields: name_template or content_template.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse([ 'error' => 'INVALID_INPUT', 'message' => 'Faltan datos requeridos: name_template y content_template son necesarios.' @@ -1743,81 +2125,108 @@ public function getAllTemplates(): JsonResponse $contentTemplate = $data['content_template']; $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; - $this->logger->debug('Received template data.', ['nameTemplate' => $nameTemplate]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['nameTemplate' => $nameTemplate], + 'desc' => 'Received template data.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Validar el nombre de la plantilla if (!preg_match('/^[a-zA-Z0-9._-]+$/', $nameTemplate)) { - $this->logger->error('Invalid template name. Contains invalid characters.', [ - 'nameTemplate' => $nameTemplate, - 'responseCode' => JsonResponse::HTTP_BAD_REQUEST - ]); + $httpCode = '400'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['nameTemplate' => $nameTemplate], + 'desc' => 'Invalid template name. Contains invalid characters.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse([ 'error' => 'INVALID_TEMPLATE_NAME', 'message' => 'El nombre de la plantilla contiene caracteres no válidos.' ], JsonResponse::HTTP_BAD_REQUEST); } - // Construir la ruta completa del archivo $filePath = $templateDir . '/' . $nameTemplate; $realFilePath = realpath($templateDir) . '/' . $nameTemplate; - $this->logger->debug('Computed file paths for template.', ['filePath' => $filePath, 'realFilePath' => $realFilePath]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['filePath' => $filePath, 'realFilePath' => $realFilePath], + 'desc' => 'Computed file paths for template.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Verificar que el archivo está dentro del directorio permitido if (strpos($realFilePath, realpath($templateDir)) !== 0) { - $this->logger->error('Unauthorized access attempt outside the template directory.', [ - 'nameTemplate' => $nameTemplate, - 'filePath' => $filePath, - 'responseCode' => JsonResponse::HTTP_FORBIDDEN - ]); + $httpCode = '403'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['nameTemplate' => $nameTemplate, 'filePath' => $filePath], + 'desc' => 'Unauthorized access attempt outside the template directory.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse([ 'error' => 'UNAUTHORIZED_ACCESS', 'message' => 'Intento de acceso no autorizado fuera del directorio de plantillas.' ], JsonResponse::HTTP_FORBIDDEN); } - $this->logger->debug('Sanitizing template content.'); - $contentTemplate = str_replace("\\n", "\n", $contentTemplate); try { - $this->logger->info('Attempting to create the template file.', ['filePath' => $realFilePath]); + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'params' => ['filePath' => $realFilePath], + 'desc' => 'Attempting to create the template file.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Intentar crear la plantilla file_put_contents($realFilePath, $contentTemplate); - // Comprobar si el archivo se ha creado correctamente if (!file_exists($realFilePath)) { throw new \Exception('El archivo no se pudo crear.'); } - // Verificar si el contenido escrito coincide con el contenido esperado $writtenContent = file_get_contents($realFilePath); if ($writtenContent !== $contentTemplate) { throw new \Exception('El contenido del archivo no coincide con el contenido esperado.'); } - $this->logger->info('Template created successfully.', ['filePath' => $realFilePath]); + $httpCode = '200'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['filePath' => $realFilePath], + 'desc' => 'Template created successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); } catch (\Exception $e) { - $this->logger->error('Failed to create template.', [ - 'nameTemplate' => $nameTemplate, - 'filePath' => $realFilePath, - 'exception' => $e->getMessage(), - 'responseCode' => JsonResponse::HTTP_INTERNAL_SERVER_ERROR - ]); + $httpCode = '500'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['nameTemplate' => $nameTemplate, 'filePath' => $realFilePath, 'exception' => $e->getMessage()], + 'desc' => 'Failed to create template.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new JsonResponse([ 'error' => 'FILE_CREATION_ERROR', 'message' => 'Ocurrió un error al crear la plantilla de arranque: ' . $e->getMessage() ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); } - $this->logger->info('Returning success response.', [ - 'templateName' => $nameTemplate, - 'responseCode' => JsonResponse::HTTP_OK - ]); - - // Retornar el nombre de la plantilla y su contenido en el formato solicitado return new JsonResponse([ 'success' => 'Plantilla creada con éxito', 'message' => [ @@ -1825,8 +2234,8 @@ public function getAllTemplates(): JsonResponse 'template_content' => $contentTemplate ] ], JsonResponse::HTTP_OK); - } - + } + @@ -1872,51 +2281,82 @@ public function getAllTemplates(): JsonResponse */ public function deleteTemplate(string $name): Response { - $this->logger->info('Attempting to delete template.', ['templateName' => $name]); + $operation = 'ogboot.deleteTemplate'; + $component = 'ogboot'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['templateName' => $name], + 'desc' => 'Attempting to delete template.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; $filePath = $templateDir . '/' . $name; - $this->logger->debug('Computed file path for template.', ['filePath' => $filePath]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['filePath' => $filePath], + 'desc' => 'Computed file path for template.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Comprobar si la plantilla existe if (!file_exists($filePath)) { - $this->logger->error('Template not found. Returning HTTP Response Code.', [ - 'templateName' => $name, - 'filePath' => $filePath, - 'responseCode' => Response::HTTP_NOT_FOUND - ]); + $httpCode = '404'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['templateName' => $name, 'filePath' => $filePath], + 'desc' => 'Template not found.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new Response(json_encode([ 'error' => 'TEMPLATE_NOT_FOUND', 'message' => 'No se encontró la plantilla especificada' ]), Response::HTTP_NOT_FOUND, ['Content-Type' => 'application/json']); } - $this->logger->debug('Template exists. Proceeding to delete.', ['filePath' => $filePath]); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['filePath' => $filePath], + 'desc' => 'Template exists. Proceeding to delete.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // Intentar eliminar la plantilla try { unlink($filePath); - $this->logger->info('Template deleted successfully.', ['templateName' => $name, 'filePath' => $filePath]); + + $httpCode = '200'; + $this->logger->info(json_encode([ + 'severity' => 'INFO', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['templateName' => $name, 'filePath' => $filePath], + 'desc' => 'Template deleted successfully.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); } catch (\Exception $e) { - $this->logger->error('Failed to delete template. Returning HTTP Response Code.', [ - 'templateName' => $name, - 'filePath' => $filePath, - 'exception' => $e->getMessage(), - 'responseCode' => Response::HTTP_INTERNAL_SERVER_ERROR - ]); + $httpCode = '500'; + $this->logger->error(json_encode([ + 'severity' => 'ERROR', + 'operation' => $operation, + 'component' => $component, + 'http_code' => $httpCode, + 'params' => ['templateName' => $name, 'filePath' => $filePath, 'exception' => $e->getMessage()], + 'desc' => 'Failed to delete template.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + return new Response(json_encode([ 'error' => 'FAILED_TO_DELETE_TEMPLATE', 'message' => 'Ocurrió un error al intentar eliminar la plantilla' ]), Response::HTTP_INTERNAL_SERVER_ERROR, ['Content-Type' => 'application/json']); } - $this->logger->info('Returning success response.', [ - 'templateName' => $name, - 'responseCode' => Response::HTTP_OK - ]); - - // Devolver respuesta exitosa return new Response(json_encode([ 'success' => 'Plantilla eliminada', 'message' => "La plantilla $name se ha borrado correctamente" -- 2.40.1