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..4053c2a 100644 --- a/src/OgBootBundle/Controller/OgBootController.php +++ b/src/OgBootBundle/Controller/OgBootController.php @@ -214,25 +214,55 @@ public function getStatus(): Response */ public function getDownloadMenu(): Response { - // Llamar al servicio para obtener las ISOs disponibles + $operation = 'ogboot.getDownloadMenu'; + $component = 'ogboot'; + + // 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(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 ); } - // Formatear la respuesta según el nuevo formato - $response = [ - 'success' => 'ISOs retrieved successfully', - 'message' => $downloadsOutput['output']['downloads'] // Aquí la estructura ya está en el formato correcto de la salida del shell script - ]; + // 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)); - return new JsonResponse($response, Response::HTTP_OK); + // Respuesta + return new JsonResponse([ + 'success' => 'ISOs retrieved successfully', + 'message' => $downloadsOutput['output']['downloads'] ?? [] + ], Response::HTTP_OK); } + + /** * @Route("/ogboot/v1/oglives", name="getOglives", methods={"GET"}) * @OA\Get( @@ -280,48 +310,111 @@ 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 + { + $operation = 'ogboot.getOglives'; // Nombre de la operación + $component = 'ogboot'; // Componente responsable + + // 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(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['response' => $response], + 'desc' => 'Service response for list_installed_oglives.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + if ($exitCode === 404) { + $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.'], + Response::HTTP_NOT_FOUND + ); + } elseif ($exitCode === 500) { + $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.'], + Response::HTTP_INTERNAL_SERVER_ERROR + ); + } elseif ($exitCode !== 0) { + $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.'], + Response::HTTP_INTERNAL_SERVER_ERROR + ); + } + + if (empty($ogLiveConfigResult) || !isset($ogLiveConfigResult['installed_ogLives'])) { + $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.'], + Response::HTTP_NOT_FOUND + ); + } + + $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)); + + $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); + } + + + /** * @Route("/ogboot/v1/oglives/default", name="getOgliveDefault", methods={"GET"}) @@ -350,24 +443,67 @@ public function getOglives(): Response */ public function getOgliveDefault(Request $request): Response { - $response = $this->curlRequestService->callOgLive("get_default"); + $operation = 'ogboot.getOgliveDefault'; // Nombre de la operación + $component = 'ogboot'; // Componente responsable + + // Log inicial: Inicio del endpoint + $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'); $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(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 = (string) Response::HTTP_INTERNAL_SERVER_ERROR; + $errorMessage = $result['error'] ?? 'Failed to retrieve default ogLive.'; + $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' => $result['error'] ?? 'Failed to retrieve default ogLive.'], + ['error' => 'SERVER_ERROR', 'message' => $errorMessage], Response::HTTP_INTERNAL_SERVER_ERROR ); } + // Log de éxito: Respuesta válida obtenida + $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], Response::HTTP_OK ); } + + /** * @Route("/ogboot/v1/oglives/{checksum}", name="getOglive", methods={"GET"}) * @OA\Get( @@ -414,24 +550,76 @@ 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 + $operation = 'ogboot.getOglive'; // Nombre de la operación + $component = 'ogboot'; // Componente responsable + + // Log inicial: Inicio del endpoint con el checksum proporcionado + $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)); $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(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 = (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."], Response::HTTP_NOT_FOUND ); } elseif ($exitCode !== 0) { + $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.'], Response::HTTP_INTERNAL_SERVER_ERROR ); } + // Log de éxito: Respuesta válida obtenida + $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], @@ -440,6 +628,7 @@ public function getOglive(string $checksum): Response } + /** * @Route("/ogboot/v1/oglives/default", name="setOgliveDefault", methods={"PUT"}) * @OA\Put( @@ -488,10 +677,28 @@ public function getOglive(string $checksum): Response */ public function setOgliveDefault(Request $request): Response { + $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)); + $data = json_decode($request->getContent(), true); - // Validar que el checksum está presente en los datos de entrada if (!isset($data['checksum'])) { + $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.'], Response::HTTP_BAD_REQUEST @@ -500,26 +707,62 @@ public function setOgliveDefault(Request $request): Response $checksum = $data['checksum']; - // Llamar al servicio para establecer el ogLive como predeterminado utilizando el checksum proporcionado + $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)); + $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 + $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)); + if ($exitCode !== 0) { + $httpCode = (string) Response::HTTP_INTERNAL_SERVER_ERROR; + $errorMessage = $result['error'] ?? 'Failed to set ogLive as default.'; + $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' => $result['error'] ?? 'Failed to set ogLive as default.'], + ['error' => 'SERVER_ERROR', 'message' => $errorMessage], Response::HTTP_INTERNAL_SERVER_ERROR ); } - // Devolver la respuesta en el formato solicitado + $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)); + return new JsonResponse( ['success' => 'ogLive client set as default successfully', 'message' => $result], Response::HTTP_OK ); } + + /** * @Route("/ogboot/v1/oglives/install", name="installOglive", methods={"POST"}) * @OA\Post( @@ -575,57 +818,113 @@ public function setOgliveDefault(Request $request): Response public function installOglive(Request $request): JsonResponse { - // Obtener los datos del cuerpo de la petición + $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)); + $data = json_decode($request->getContent(), true); if (!isset($data['url']) || !isset($data['id'])) { + $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']; + $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 { - // Ejecutar el comando Symfony 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, - ]); $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) ); - $commandLine = $process->getCommandLine(); - $this->logger->info('Command to be executed: ' . $commandLine); + + $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->start(); // Ejecuta en segundo plano $process->setTimeout(null); $process->disableOutput(); + try { $process->start(); - } catch (\Exception $e) { - $this->logger->error('Error starting process: ' . $e->getMessage()); - } - 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 + $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) { + $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)); + return new JsonResponse([ 'error' => 'Failed to initiate ogLive installation', 'details' => $e->getMessage() ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); } } + + /** * Enviar el resultado al webhook @@ -691,51 +990,112 @@ public function setOgliveDefault(Request $request): Response */ public function uninstallOglive(string $checksum): Response { - // 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 + $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)); + $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); + $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)); if ($exitCode === 404) { - // ogLive client no encontrado + $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) { - // Intento de desinstalar el ogLive por defecto + } + + 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) { - // Error interno del servidor + } + + 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) { - // Desinstalación exitosa + } + + 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 { - // Manejar otros códigos de error no previstos - 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); +} @@ -772,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) @@ -808,6 +1203,7 @@ public function getBootFiles(): JsonResponse } + /** * @Route("/ogboot/v1/pxes/{mac}", name="ogboot_get_boot_file_with_params", methods={"GET"}) * @OA\Get( @@ -874,67 +1270,102 @@ public function getBootFiles(): JsonResponse public function getBootFile(string $mac): Response { - // Ruta donde están alojados los archivos de arranque - $directory = '/opt/ogboot/tftpboot/ipxe_scripts'; + $operation = 'ogboot.getBootFile'; + $component = 'ogboot'; - // Validar y formatear la dirección MAC + $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)); + + $directory = $this->tftpbootDir . '/ipxe_scripts'; $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; - // Verificar que el archivo está dentro del directorio permitido if (strpos($realFilePath, realpath($directory)) !== 0) { + $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' => 'Intento de acceso no autorizado fuera del directorio de arranque.' - ], + ['error' => 'UNAUTHORIZED_ACCESS', 'message' => 'Attempted unauthorized access outside the boot directory.'], Response::HTTP_FORBIDDEN ); } - // Verificar si el archivo existe if (!file_exists($realFilePath)) { + $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) { + $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 al leer el archivo de arranque.'], + ['error' => 'FAILED_TO_READ_BOOT_FILE', 'message' => 'Error reading the boot file.'], Response::HTTP_INTERNAL_SERVER_ERROR ); } - // 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)) { + $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', @@ -948,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, @@ -978,13 +1407,24 @@ public function getBootFiles(): JsonResponse 'resolution' => $params['vga'] ?? '', ]; - // Formatear la respuesta de éxito + $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)); + return new JsonResponse( ['success' => 'Boot file retrieved successfully', 'message' => $result], Response::HTTP_OK ); } + + @@ -1026,130 +1466,175 @@ public function getBootFiles(): JsonResponse */ public function createBootFile(Request $request): JsonResponse { - $data = json_decode($request->getContent(), true); + $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 tirara por Samba $serverIp = $data['server_ip'] ?? null; + if ($serverIp && strpos($serverIp, ':') !== false) { $serverIp = explode(':', $serverIp)[0]; } + $ogLiveDir = $data['oglivedir'] ?? 'ogLive'; - // Verificación de los campos obligatorios - if (!$templateName || !$mac) { - return new JsonResponse(['error' => 'Missing required fields: mac and template_name'], Response::HTTP_BAD_REQUEST); - } + $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)); + + if (!$templateName || !$mac) { + $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)); - // Validación adicional para asegurarnos de que no hay caracteres inseguros - if (!preg_match('/^[a-zA-Z0-9._-]+$/', $templateName) || strpos($templateName, '..') !== false) { return new JsonResponse( - ['error' => 'INVALID_TEMPLATE_NAME', 'message' => 'Nombre de la plantilla no válido o intento de acceso no autorizado.'], + ['error' => 'Missing required fields: mac and template_name'], Response::HTTP_BAD_REQUEST ); } + if (!preg_match('/^[a-zA-Z0-9._-]+$/', $templateName) || strpos($templateName, '..') !== false) { + $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)); - // Parámetros opcionales - $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' - ]; + return new JsonResponse( + ['error' => 'INVALID_TEMPLATE_NAME', 'message' => 'Invalid template name or unauthorized access attempt.'], + Response::HTTP_BAD_REQUEST + ); + } $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; $templatePath = $templateDir . '/' . $templateName; - // 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); + $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) { - return new JsonResponse(['error' => 'FAILED_TO_READ_TEMPLATE', 'message' => 'Error al leer la plantilla'], 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 + ); } - // 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'])); + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'desc' => 'Template content loaded.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + $kernelArgs = 'ro boot=oginit quiet splash irqpoll acpi=on og2nd=sqfs ogprotocol=smb ogactiveadmin=true ogdebug=true ogtmpfs=15 ' . + 'oglivedir=${ISODIR} ' . + 'LANG=' . ($data['lang'] ?? 'es_ES.UTF-8') . ' ' . + 'ip=' . ($data['ip'] ?? '') . ':' . $serverIp . ':' . ($data['router'] ?? '') . ':' . ($data['netmask'] ?? '') . ':' . ($data['computer_name'] ?? '') . ':' . ($data['netiface'] ?? '') . ':none '; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['kernelArgs' => $kernelArgs], + 'desc' => 'Kernel arguments constructed.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); - // 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 $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 ); + 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)); - // 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; + return new JsonResponse( + ['error' => 'FAILED_TO_CREATE_PXE_FILE', 'message' => 'Failed to create PXE file.'], + Response::HTTP_INTERNAL_SERVER_ERROR + ); } - // Nombre del archivo PXE basado en la MAC - $pxeFileName = '01-' . $mac; + $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)); - // 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); - } - - // Retornar la plantilla creada en el formato solicitado return new JsonResponse([ 'success' => 'PXE file created successfully', 'message' => $pxeContent ], Response::HTTP_OK); } + + function validateAndFormatMac($mac) { @@ -1214,35 +1699,98 @@ function validateAndFormatMac($mac) */ public function deleteBootFile(string $mac): Response { - #$directory = '/opt/ogboot/tftpboot/ipxe_scripts'; + $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)); + $directory = $this->tftpbootDir . '/ipxe_scripts'; - // Validar y formatear la dirección MAC $mac = $this->validateAndFormatMac($mac ?? null); $fileName = "01-" . $mac; $filePath = "$directory/$fileName"; - // Verificar si el archivo de arranque existe + $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)); + if (!file_exists($filePath)) { + $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 ); } - // Intentar eliminar el archivo de arranque + $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)); + if (!unlink($filePath)) { + $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 ); } - // Respuesta exitosa con formato estándar + $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)); + 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,31 +1824,78 @@ public function deleteBootFile(string $mac): Response */ public function getAllTemplates(): JsonResponse { -# $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/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)) { + $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 ); } - // Obtener la lista de archivos en el directorio + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['templateDir' => $templateDir], + 'desc' => 'Template directory exists.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + $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); }); - // Formatear la respuesta siguiendo el estándar + $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)); + 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( @@ -1342,36 +1937,98 @@ 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 + { + $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(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['filePath' => $filePath], + 'desc' => 'Computed file path for template.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + if (!file_exists($filePath)) { + $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(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['filePath' => $filePath], + 'desc' => 'Template found, attempting to read content.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + $content = file_get_contents($filePath); + if ($content === false) { + $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']); + } + + $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)); + + return new Response(json_encode([ + 'success' => 'Plantilla obtenida con éxito', + 'template_name' => $templateName, + 'template_content' => $content + ]), Response::HTTP_OK, ['Content-Type' => 'application/json']); + } + /** @@ -1433,77 +2090,152 @@ 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 + { + $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'])) { + $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.' + ], JsonResponse::HTTP_BAD_REQUEST); + } + + $nameTemplate = $data['name_template']; + $contentTemplate = $data['content_template']; + $templateDir = $this->tftpbootDir . '/ipxe_scripts/templates'; + + $this->logger->debug(json_encode([ + 'severity' => 'DEBUG', + 'operation' => $operation, + 'component' => $component, + 'params' => ['nameTemplate' => $nameTemplate], + 'desc' => 'Received template data.' + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + if (!preg_match('/^[a-zA-Z0-9._-]+$/', $nameTemplate)) { + $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); + } + + $filePath = $templateDir . '/' . $nameTemplate; + $realFilePath = realpath($templateDir) . '/' . $nameTemplate; + + $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)); + + if (strpos($realFilePath, realpath($templateDir)) !== 0) { + $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); + } + + $contentTemplate = str_replace("\\n", "\n", $contentTemplate); + + try { + $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)); + + file_put_contents($realFilePath, $contentTemplate); + + if (!file_exists($realFilePath)) { + throw new \Exception('El archivo no se pudo crear.'); + } + + $writtenContent = file_get_contents($realFilePath); + if ($writtenContent !== $contentTemplate) { + throw new \Exception('El contenido del archivo no coincide con el contenido esperado.'); + } + + $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) { + $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); + } + + return new JsonResponse([ + 'success' => 'Plantilla creada con éxito', + 'message' => [ + 'template_name' => $nameTemplate, + 'template_content' => $contentTemplate + ] + ], JsonResponse::HTTP_OK); + } + @@ -1547,31 +2279,84 @@ public function createTemplate(Request $request): JsonResponse * ) * ) */ -public function deleteTemplate($name): Response +public function deleteTemplate(string $name): Response { - #$templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; + $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; - // Comprobar si la plantilla existe + $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)); + if (!file_exists($filePath)) { + $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']); } - // Intentar eliminar la plantilla + $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)); + try { unlink($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) { + $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']); } - // Devolver respuesta exitosa return new Response(json_encode([ 'success' => 'Plantilla eliminada', 'message' => "La plantilla $name se ha borrado correctamente" 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 + ]; + } } - - - -} -