curlRequestService = $curlRequestService; $this->logger = $logger; $this->httpClient = $httpClient; $this->params = $params; // Accedemos a las variables de entorno a través de ParameterBagInterface $this->tftpbootDir = $params->get('tftpboot_dir'); } /*Tabla de Contenido Obtener configuración de ogboot - GET /ogboot/config x Obtener datos de rendimiento - GET /ogboot/status x Mostrar información de todos los clientes ogLive instalados - GET /ogboot/oglives x Mostrar información de un cliente ogLive instalado - GET /ogboot/oglives/{Index|Dir} x Mostrar información del cliente ogLive predeterminado - GET /ogboot/oglives/default x Cambiar ogLive predeterminado - POST /ogboot/oglives/default/{Index} x Instalar nuevo cliente ogLive desde imagen descargada - POST /ogboot/oglive/{Index/iso} ---- Desinstalar cliente ogLive y eliminar imagen - DELETE /ogboot/oglives/{Index/iso} ------- Regenerar archivo de información de los ogLive - PUT /ogboot/oglives Mostrar menú de descarga de imagen de ogLive - GET /ogboot/images/download X ///////////////////////////////////////////// Obtener todos los archivos de arranque - GET /ogboot/pxes Obtener archivo de arranque - GET /ogboot/clients/pxes/{mac} Crear archivo de arranque - POST /ogboot/pxes Eliminar archivo de arranque - DELETE /ogboot/clients/pxes Obtener todas las plantillas - GET /ogboot/pxe-templates Obtener contenido de la plantilla - GET /ogboot/pxe-templates/{nombre} Crear plantilla - POST /ogboot/pxe-templates Regenerar plantilla - PUT /ogboot/pxe-templates */ /** * @Route("/ogboot/v1/status", name="getStatus", methods={"GET"}) * @OA\Get( * path="/ogboot/v1/status", * summary="Get ogboot status", * @OA\Response( * response=200, * description="Status retrieved successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="disk_usage", type="object", * @OA\Property(property="total", type="string", example="20G"), * @OA\Property(property="used", type="string", example="15G"), * @OA\Property(property="available", type="string", example="5G"), * @OA\Property(property="percentage", type="string", example="75%") * ), * @OA\Property(property="default_oglive", type="string", example="ogLive-5.11.0-r20210413"), * @OA\Property( * property="installed_oglives", * type="array", * @OA\Items(ref="#/components/schemas/OgLive") * ), * @OA\Property(property="services_status", type="object", * @OA\Property(property="service_name", type="string", example="nginx"), * @OA\Property(property="status", type="string", example="running") * ) * ) * ), * @OA\Response( * response=404, * description="No ogLive clients found or no default ogLive client available", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="NOT_FOUND"), * @OA\Property(property="message", type="string", example="No default or installed ogLives found.") * ) * ), * @OA\Response( * response=500, * description="Server error occurred while retrieving the status", * @OA\JsonContent( * oneOf={ * @OA\Schema( * @OA\Property(property="error", type="string", example="FAILED_TO_RETRIEVE_DISK_USAGE"), * @OA\Property(property="details", type="string", example="Unknown error") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="FAILED_TO_RETRIEVE_OGLIVE_CONFIGURATION"), * @OA\Property(property="details", type="string", example="Unknown error") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="FAILED_TO_RETRIEVE_SERVICES_STATUS"), * @OA\Property(property="details", type="string", example="Unknown error") * ) * } * ) * ) * ) */ public function getStatus(): Response { // Llamar a oglivecli para obtener el uso del disco $diskUsageResponse = $this->curlRequestService->callOgLive("disk_usage"); $diskUsageResult = $diskUsageResponse['output']; $diskUsageExitCode = $diskUsageResponse['exitCode']; if ($diskUsageExitCode !== 0) { return new JsonResponse( ['error' => 'FAILED_TO_RETRIEVE_DISK_USAGE', 'details' => $diskUsageResult['error'] ?? 'Unknown error'], Response::HTTP_INTERNAL_SERVER_ERROR ); } // Llamar a oglivecli para obtener la configuración de los oglives instalados y el oglive por defecto $ogLiveResponse = $this->curlRequestService->callOgLive("list_installed_oglives"); $ogLiveConfigResult = $ogLiveResponse['output']; $ogLiveExitCode = $ogLiveResponse['exitCode']; if ($ogLiveExitCode !== 0) { return new JsonResponse( ['error' => 'FAILED_TO_RETRIEVE_OGLIVE_CONFIGURATION', 'details' => $ogLiveConfigResult['error'] ?? 'Unknown error'], Response::HTTP_INTERNAL_SERVER_ERROR ); } // Llamar a oglivecli para obtener el estado de los servicios $servicesStatusResponse = $this->curlRequestService->callOgLive("check_services_status"); $servicesStatusResult = $servicesStatusResponse['output']; $servicesStatusExitCode = $servicesStatusResponse['exitCode']; if ($servicesStatusExitCode !== 0) { return new JsonResponse( ['error' => 'FAILED_TO_RETRIEVE_SERVICES_STATUS', 'details' => $servicesStatusResult['error'] ?? 'Unknown error'], Response::HTTP_INTERNAL_SERVER_ERROR ); } // Verificar si las claves 'default_oglive' e 'installed_ogLives' existen en la respuesta de oglivecli $defaultOglive = $ogLiveConfigResult['default_oglive'] ?? null; $installedOglives = $ogLiveConfigResult['installed_ogLives'] ?? []; if ($defaultOglive === null || empty($installedOglives)) { return new JsonResponse( ['error' => 'NOT_FOUND', 'message' => 'No default or installed ogLives found.'], Response::HTTP_NOT_FOUND ); } $response = [ 'success' => 'Status retrieved successfully', 'message' => [ 'disk_usage' => $diskUsageResult, 'default_oglive' => $defaultOglive, 'installed_oglives' => $installedOglives, 'services_status' => $servicesStatusResult ] ]; // Formatear la respuesta final con todos los datos return new JsonResponse($response, Response::HTTP_OK); } /** * @Route("/ogboot/v1/oglives/isos", name="getDownloadMenu", methods={"GET"}) * @OA\Get( * path="/ogboot/v1/oglives/isos", * summary="Get ogLive downloads menu", * @OA\Response( * response=200, * description="Isos retrieved successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string"), * @OA\Property(property="message", type="array", * @OA\Items(ref="#/components/schemas/DownloadOgLive") * ) * ) * ), * @OA\Response( * response=500, * description="Failed to retrieve isos", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string"), * @OA\Property(property="message", type="string") * ) * ) * ) */ public function getDownloadMenu(): Response { $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"); // 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']], Response::HTTP_INTERNAL_SERVER_ERROR ); } // 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)); // 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( * path="/ogboot/v1/oglives", * summary="Get list of all installed ogLive clients", * @OA\Response( * response=200, * description="List of installed ogLive clients retrieved successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="ogLive clients retrieved successfully"), * @OA\Property(property="message", type="object", * @OA\Property(property="default_oglive", type="string", description="The default ogLive client"), * @OA\Property(property="installed_ogLives", type="array", * @OA\Items(ref="#/components/schemas/OgLive") * ) * ) * ) * ), * @OA\Response( * response=404, * description="No ogLive clients found", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="NOT_FOUND"), * @OA\Property(property="message", type="string", example="No ogLive clients found.") * ) * ), * @OA\Response( * response=500, * description="Server or unknown error occurred", * @OA\JsonContent( * oneOf={ * @OA\Schema( * @OA\Property(property="error", type="string", example="SERVER_ERROR"), * @OA\Property(property="message", type="string", example="Failed to retrieve ogLive clients due to server error.") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="UNKNOWN_ERROR"), * @OA\Property(property="message", type="string", example="An unknown error occurred.") * ) * } * ) * ) * ) */ 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"}) * @OA\Get( * path="/ogboot/v1/oglives/default", * summary="Get information of the default ogLive client", * @OA\Response( * response=200, * description="Successful operation", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="se ha obtenido el oglive por defecto"), * @OA\Property(property="message", ref="#/components/schemas/OgLive") * ) * ), * @OA\Response( * response=500, * description="Failed to retrieve information of the default ogLive client", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="SERVER_ERROR"), * @OA\Property(property="message", type="string", example="Failed to retrieve default ogLive.") * ) * ) * ) */ 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->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']; // 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' => $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( * path="/ogboot/v1/oglives/{checksum}", * summary="Get information of an installed ogLive client", * @OA\Parameter( * name="checksum", * in="path", * description="Checksum of the installed ogLive client", * required=true, * @OA\Schema( * type="string", * example="9e49a085ba74f97a81bdf9b3d0785094" * ) * ), * @OA\Response( * response=200, * description="ogLive client retrieved successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="ogLive client retrieved successfully"), * @OA\Property(property="message", ref="#/components/schemas/OgLive") * ) * ), * @OA\Response( * response=404, * description="ogLive client not found", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="NOT_FOUND"), * @OA\Property(property="message", type="string", example="ogLive client with checksum 9e49a085ba74f97a81bdf9b3d0785094 not found.") * ) * ), * @OA\Response( * response=500, * description="Failed to retrieve ogLive client", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="SERVER_ERROR"), * @OA\Property(property="message", type="string", example="Failed to retrieve ogLive client.") * ) * ) * ) */ 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->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']; // 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], Response::HTTP_OK ); } /** * @Route("/ogboot/v1/oglives/default", name="setOgliveDefault", methods={"PUT"}) * @OA\Put( * path="/ogboot/v1/oglives/default", * summary="Set default ogLive client", * @OA\RequestBody( * required=true, * @OA\JsonContent( * type="object", * @OA\Property( * property="checksum", * type="string", * example="9e49a085ba74f97a81bdf9b3d0785094", * description="Checksum of the ogLive client to set as default" * ) * ) * ), * @OA\Response( * response=200, * description="ogLive client set as default successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="ogLive client set as default successfully"), * @OA\Property(property="message", ref="#/components/schemas/OgLive") * ) * ), * @OA\Response( * response=400, * description="Invalid input data", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="INVALID_INPUT"), * @OA\Property(property="message", type="string", example="Invalid input data: checksum is required.") * ) * ), * @OA\Response( * response=500, * description="Failed to set default ogLive client", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="SERVER_ERROR"), * @OA\Property(property="message", type="string", example="Failed to set ogLive as default.") * ) * ) * ) */ 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); 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 ); } $checksum = $data['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)); $response = $this->curlRequestService->callOgLive("set_default " . escapeshellarg($checksum)); $result = $response['output']; $exitCode = $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)); 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' => $errorMessage], Response::HTTP_INTERNAL_SERVER_ERROR ); } $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( * path="/ogboot/v1/oglives/install", * summary="Install an ogLive client", * @OA\RequestBody( * required=true, * @OA\JsonContent( * type="object", * @OA\Property( * property="url", * type="string", * example="https://ognproject.evlt.uma.es/trac/downloads/ogLive-focal-5.11.0-22-generic-amd64-r20210413.992ebb9.iso", * description="URL of the ogLive ISO to download and install" * ), * @OA\Property( * property="id", * type="string", * example="12345", * description="Unique transaction ID to track the ogLive installation process" * ) * ) * ), * @OA\Response( * response=200, * description="ogLive client installation initiated", * @OA\JsonContent( * type="object", * @OA\Property(property="message", type="string"), * @OA\Property(property="details", type="object") * ) * ), * @OA\Response( * response=400, * description="Invalid input data", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string"), * @OA\Property(property="details", type="string") * ) * ), * @OA\Response( * response=500, * description="Failed to initiate ogLive installation", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string"), * @OA\Property(property="details", type="string") * ) * ) * ) */ public function installOglive(Request $request): JsonResponse { $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); } $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 { $consolePath = $this->params->get('kernel.project_dir') . '/bin/console'; $command = sprintf( 'php %s oglive:install %s %s > /dev/null 2>&1', escapeshellarg($consolePath), escapeshellarg($isoUrl), escapeshellarg($transactionId) ); $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 { $process->start(); $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 * * @param string $webhookUrl * @param array $data Datos para enviar al webhook */ private function notifyWebhook(string $webhookUrl, array $data): void { try { error_log('Webhook URL: ' . $webhookUrl); $this->logger->info('Webhook URL: ' . $webhookUrl); error_log('Data being sent to webhook: ' . json_encode($data)); $this->logger->info('Data being sent to webhook: ' . json_encode($data)); $this->httpClient->request('POST', $webhookUrl, [ 'headers' => [ 'accept' => 'application/json', 'Content-Type' => 'application/json', ], 'body' => json_encode($data), ]); } catch (Exception $e) { // Manejo de errores en la notificación del webhook error_log('Error sending webhook notification: ' . $e->getMessage()); } } /** * @Route("/ogboot/v1/oglives/{checksum}", name="uninstallOglive", methods={"DELETE"}) * @OA\Delete( * path="/ogboot/v1/oglives/{checksum}", * summary="Uninstall an ogLive client", * @OA\Parameter( * name="checksum", * in="path", * description="Checksum of the ogLive client to uninstall", * required=true, * @OA\Schema(type="string", example="9e49a085ba74f97a81bdf9b3d0785094") * ), * @OA\Response( * response=200, * description="ogLive client uninstalled successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="message", type="string"), * @OA\Property(property="details", type="string") * ) * ), * @OA\Response( * response=404, * description="ogLive client not found" * ), * @OA\Response( * response=500, * description="Failed to uninstall ogLive client", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string") * ) * ) * ) */ public function uninstallOglive(string $checksum): Response { $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']; $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) { $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); } 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); } 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); } 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); } $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); } /** * @Route("/ogboot/v1/pxes", name="get_boot_files", methods={"GET"}) * @OA\Get( * path="/ogboot/v1/pxes", * summary="Get all PXE boot files", * @OA\Response( * response=200, * description="Successful operation", * @OA\JsonContent( * type="object", * @OA\Property( * property="success", * type="string", * example="Boot files retrieved successfully" * ), * @OA\Property( * property="message", * type="array", * @OA\Items( * type="string", * description="Boot file" * ) * ) * ) * ), * @OA\Response( * response=500, * description="Failed to retrieve boot files" * ) * ) */ public function getBootFiles(): JsonResponse { $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'; 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 ); } $files = scandir($directory); $bootFiles = array_filter($files, function ($file) use ($directory) { return !is_dir($directory . '/' . $file) && strpos($file, '01-') === 0; }); 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 ); } $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) ]; return new JsonResponse($response, Response::HTTP_OK); } /** * @Route("/ogboot/v1/pxes/{mac}", name="ogboot_get_boot_file_with_params", methods={"GET"}) * @OA\Get( * path="/ogboot/v1/pxes/{mac}", * summary="Get boot file with parameters", * @OA\Parameter( * name="mac", * in="path", * description="MAC address", * required=true, * @OA\Schema(type="string", example="00:50:56:22:11:12") * ), * @OA\Response( * response=200, * description="Successful operation", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="Boot file retrieved successfully"), * @OA\Property( * property="message", * type="object", * @OA\Property(property="template_name", type="string", description="Template name (required)", example="pxe"), * @OA\Property(property="mac", type="string", description="MAC address (required)", example="00:50:56:22:11:12"), * @OA\Property(property="lang", type="string", description="Language (optional)", example="es_ES.UTF-8"), * @OA\Property(property="ip", type="string", description="IP address (optional)", example="192.168.2.11"), * @OA\Property(property="server_ip", type="string", description="Server IP address (optional)", example="192.168.2.1"), * @OA\Property(property="router", type="string", description="Router (optional)", example="192.168.2.1"), * @OA\Property(property="netmask", type="string", description="Netmask (optional)", example="255.255.255.0"), * @OA\Property(property="computer_name", type="string", description="Computer name (optional)", example="pc11"), * @OA\Property(property="netiface", type="string", description="Network interface (optional)", example="eth0"), * @OA\Property(property="group", type="string", description="Group (optional)", example="Aula_virtual"), * @OA\Property(property="ogrepo", type="string", description="Repository IP (optional)", example="192.168.2.1"), * @OA\Property(property="oglive", type="string", description="Live server IP (optional)", example="192.168.2.1"), * @OA\Property(property="ogcore", type="string", description="Ogcore IP(optional)", example="192.168.2.1"), * @OA\Property(property="oglog", type="string", description="Log server IP (optional)", example="192.168.2.1"), * @OA\Property(property="ogshare", type="string", description="Share server IP (optional)", example="192.168.2.1"), * @OA\Property(property="oglivedir", type="string", description="Live directory (optional)", example="ogLive"), * @OA\Property(property="ogprof", type="string", description="Is professor (optional)", example="false"), * @OA\Property(property="hardprofile", type="string", description="Hardware profile (optional)", example=""), * @OA\Property(property="ogntp", type="string", description="NTP server (optional)", example=""), * @OA\Property(property="ogdns", type="string", description="DNS server (optional)", example=""), * @OA\Property(property="ogproxy", type="string", description="Proxy server (optional)", example=""), * @OA\Property(property="ogunit", type="string", description="Unit directory (optional)", example=""), * @OA\Property(property="resolution", type="string", description="Screen resolution (optional)", example="788") * ) * ) * ), * @OA\Response( * response=404, * description="No boot file found for the specified MAC address", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="NOT_FOUND"), * @OA\Property(property="message", type="string", example="No boot file found for the specified MAC address.") * ) * ), * @OA\Response( * response=500, * description="Internal server error" * ) * ) */ public function getBootFile(string $mac): Response { $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)); $directory = $this->tftpbootDir . '/ipxe_scripts'; $mac = $this->validateAndFormatMac($mac ?? null); $fileName = "01-" . $mac; $filePath = "$directory/$fileName"; $realFilePath = realpath($directory) . '/' . $fileName; 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' => 'Attempted unauthorized access outside the boot directory.'], Response::HTTP_FORBIDDEN ); } 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 ); } $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 reading the boot file.'], Response::HTTP_INTERNAL_SERVER_ERROR ); } $templateName = 'unknown'; $oglivedir = ''; $kernelArgs = ''; if (preg_match('/#Template:\s*(.*)/', $content, $matches)) { $templateName = trim($matches[1]); } if (preg_match('/set ISODIR\s+(.*)/', $content, $matches)) { $oglivedir = trim($matches[1]); } if (preg_match('/set kernelargs\s+(.*)/', $content, $matches)) { $kernelArgs = trim($matches[1]); } 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', 'message' => [ 'template_name' => $templateName, 'mac' => $mac, 'message' => 'Boot file without parameters, possibly a disk boot template.' ] ], Response::HTTP_OK ); } parse_str(str_replace(' ', '&', $kernelArgs), $params); $result = [ 'template_name' => $templateName, 'mac' => $mac, 'lang' => $params['LANG'] ?? '', 'ip' => explode(':', $params['ip'])[0] ?? '', 'server_ip' => explode(':', $params['ip'])[1] ?? '', 'router' => explode(':', $params['ip'])[2] ?? '', 'netmask' => explode(':', $params['ip'])[3] ?? '', 'computer_name' => explode(':', $params['ip'])[4] ?? '', 'netiface' => explode(':', $params['ip'])[5] ?? '', 'group' => $params['group'] ?? '', 'ogrepo' => $params['ogrepo'] ?? '', 'oglive' => $params['oglive'] ?? '', 'ogcore' => $params['ogcore'] ?? '', 'oglog' => $params['oglog'] ?? '', 'ogshare' => $params['ogshare'] ?? '', 'oglivedir' => $oglivedir, 'ogprof' => $params['ogprof'] ?? '', 'hardprofile' => $params['hardprofile'] ?? '', 'ogntp' => $params['ogntp'] ?? '', 'ogdns' => $params['ogdns'] ?? '', 'ogproxy' => $params['ogproxy'] ?? '', 'ogunit' => $params['ogunit'] ?? '', 'resolution' => $params['vga'] ?? '', ]; $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 ); } /** * @Route("/ogboot/v1/pxes", methods={"POST"}) * @OA\Post( * path="/ogboot/v1/pxes", * summary="Crear archivo PXE", * description="Crea un archivo de arranque PXE con la MAC y plantilla proporcionadas.", * @OA\RequestBody( * required=true, * @OA\JsonContent( * type="object", * @OA\Property(property="mac", type="string", example="00:50:56:22:11:12"), * @OA\Property(property="template_name", type="string", example="mi_plantilla.ipxe"), * @OA\Property(property="server_ip", type="string", example="192.168.2.1"), * @OA\Property(property="oglivedir", type="string", example="ogLive") * ) * ), * @OA\Response( * response=200, * description="Plantilla creada exitosamente", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="Plantilla creada con éxito"), * @OA\Property(property="message", type="string") * ) * ), * @OA\Response( * response=400, * description="Entrada inválida o datos requeridos faltantes" * ), * @OA\Response( * response=500, * description="Error interno del servidor al crear el archivo PXE" * ) * ) */ public function createBootFile(Request $request): JsonResponse { $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); $serverIp = $data['server_ip'] ?? null; if ($serverIp && strpos($serverIp, ':') !== false) { $serverIp = explode(':', $serverIp)[0]; } $ogLiveDir = $data['oglivedir'] ?? 'ogLive'; $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)); return new JsonResponse( ['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)); 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; if (!file_exists($templatePath)) { $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) { $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(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)); $serverIpPort = $this->ogBootIP; if (!empty($this->ogBootPort)) { $serverIpPort .= ':' . $this->ogBootPort; } $pxeContent = str_replace( ['__INFOHOST__', '__SERVERIP__', '__OGLIVE__'], [$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)); return new JsonResponse( ['error' => 'FAILED_TO_CREATE_PXE_FILE', 'message' => 'Failed to create PXE file.'], Response::HTTP_INTERNAL_SERVER_ERROR ); } $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', 'message' => $pxeContent ], Response::HTTP_OK); } function validateAndFormatMac($mac) { // Convertir a minúsculas para un formato uniforme $mac = strtolower($mac); // Comprobar si la MAC tiene el formato correcto con dos puntos (ej. fc:aa:14:21:55:b9) if (preg_match('/^([0-9a-f]{2}:){5}[0-9a-f]{2}$/', $mac)) { return $mac; // MAC ya está en el formato correcto } // Comprobar si la MAC tiene el formato sin los dos puntos (ej. fcaa142155b9) if (preg_match('/^[0-9a-f]{12}$/', $mac)) { // Insertar los dos puntos en la MAC y devolverla en el formato correcto return strtolower(implode(':', str_split($mac, 2))); } // Si no cumple con ninguno de los formatos, devolver un error return false; // MAC no válida } /** * @Route("/ogboot/v1/pxes/{mac}", name="ogboot_delete_boot_file", methods={"DELETE"}) * @OA\Delete( * path="/ogboot/v1/pxes/{mac}", * summary="Delete PXE boot file", * description="Delete a specific PXE boot file using its MAC address.", * @OA\Parameter( * name="mac", * in="path", * description="MAC address of the client whose boot file is to be deleted", * required=true, * @OA\Schema(type="string", example="00:11:22:33:44:55") * ), * @OA\Response( * response=200, * description="Boot file deleted successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="Boot file deleted successfully"), * @OA\Property(property="message", type="string", example="The boot file for MAC 00:11:22:33:44:55 has been deleted.") * ) * ), * @OA\Response( * response=404, * description="No boot file found for the specified MAC address", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="No boot file found for the specified MAC address.") * ) * ), * @OA\Response( * response=500, * description="Failed to delete the boot file", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="Failed to delete the boot file due to server error.") * ) * ) * ) */ public function deleteBootFile(string $mac): Response { $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'; $mac = $this->validateAndFormatMac($mac ?? null); $fileName = "01-" . $mac; $filePath = "$directory/$fileName"; $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 ); } $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 ); } $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"}) * @OA\Get( * path="/ogboot/v1/pxe-templates", * summary="Obtener todas las plantillas PXE", * description="Obtiene una lista de todas las plantillas de arranque PXE disponibles.", * @OA\Response( * response=200, * description="Lista de plantillas de arranque PXE", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="Lista de plantillas obtenida con éxito"), * @OA\Property( * property="message", * type="array", * @OA\Items(type="string", example="mi_plantilla.ipxe") * ) * ) * ), * @OA\Response( * response=500, * description="Error interno del servidor", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="SERVER_ERROR"), * @OA\Property(property="message", type="string", example="Directorio de plantillas no encontrado") * ) * ) * ) */ public function getAllTemplates(): JsonResponse { $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'; 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'], Response::HTTP_INTERNAL_SERVER_ERROR ); } $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); $templates = array_filter($files, function ($file) use ($templateDir) { return !in_array($file, ['.', '..']) && !is_dir($templateDir . '/' . $file); }); $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)], Response::HTTP_OK ); } /** * @Route("/ogboot/v1/pxe-templates/{templateName}", name="get_template", methods={"GET"}) * @OA\Get( * path="/ogboot/v1/pxe-templates/{templateName}", * summary="Obtener contenido de una plantilla PXE", * description="Obtiene el contenido de una plantilla de arranque PXE específica por su nombre.", * @OA\Parameter( * name="templateName", * in="path", * required=true, * description="Nombre de la plantilla PXE", * @OA\Schema(type="string", example="mi_plantilla.ipxe") * ), * @OA\Response( * response=200, * description="Contenido de la plantilla de arranque PXE", * @OA\JsonContent( * @OA\Property(property="success", type="string", example="Plantilla obtenida con éxito"), * @OA\Property(property="template_name", type="string", example="mi_plantilla.ipxe"), * @OA\Property(property="template_content", type="string", example="#!ipxe\nset timeout 0\nset timeout-style hidden\n\nset ISODIR ogLive\nset default 0\nset kernelargs INFOHOST\nkernel tftp://SERVERIP/ogLive/ogvmlinuz ${kernelargs}\ninitrd tftp://SERVERIP/ogLive/oginitrd.img\nboot") * ) * ), * @OA\Response( * response=404, * description="Plantilla no encontrada", * @OA\JsonContent( * @OA\Property(property="error", type="string", example="TEMPLATE_NOT_FOUND"), * @OA\Property(property="message", type="string", example="No se encontró la plantilla especificada") * ) * ), * @OA\Response( * response=500, * description="Error interno del servidor", * @OA\JsonContent( * @OA\Property(property="error", type="string", example="FAILED_TO_READ_TEMPLATE"), * @OA\Property(property="message", type="string", example="Error al leer la plantilla") * ) * ) * ) */ 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']); } /** * @Route("/ogboot/v1/pxe-templates", methods={"POST"}) * * @OA\Post( * path="/ogboot/v1/pxe-templates", * summary="Crear Plantilla", * description="Crea una nueva plantilla de arranque utilizando los datos proporcionados.", * @OA\RequestBody( * required=true, * @OA\JsonContent( * type="object", * @OA\Property(property="name_template", type="string", example="mi_plantilla.ipxe"), * @OA\Property(property="content_template", type="string", example="#!ipxe\nset timeout 0\nset timeout-style hidden\nset ISODIR __OGLIVE__\nset default 0\nset kernelargs __INFOHOST__\n:try_iso\nkernel http://__SERVERIP__/tftpboot/${ISODIR}/ogvmlinuz ${kernelargs} || goto fallback\ninitrd http://__SERVERIP__/tftpboot/${ISODIR}/oginitrd.img\nboot\n\n:fallback\nset ISODIR ogLive\nkernel http://__SERVERIP__/tftpboot/${ISODIR}/ogvmlinuz ${kernelargs}\ninitrd http://__SERVERIP__/tftpboot/${ISODIR}/oginitrd.img\nboot\n") * ) * ), * @OA\Response( * response=200, * description="La plantilla de arranque se creó exitosamente.", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="Plantilla creada con éxito"), * @OA\Property( * property="message", * type="object", * @OA\Property(property="template_name", type="string", example="mi_plantilla.ipxe"), * @OA\Property(property="template_content", type="string", example="#!ipxe\nset timeout 0\nset timeout-style hidden\nset ISODIR __OGLIVE__\nset default 0\nset kernelargs __INFOHOST__\n:try_iso\nkernel http://__SERVERIP__/tftpboot/${ISODIR}/ogvmlinuz ${kernelargs} || goto fallback\ninitrd http://__SERVERIP__/tftpboot/${ISODIR}/oginitrd.img\nboot\n\n:fallback\nset ISODIR ogLive\nkernel http://__SERVERIP__/tftpboot/${ISODIR}/ogvmlinuz ${kernelargs}\ninitrd http://__SERVERIP__/tftpboot/${ISODIR}/oginitrd.img\nboot\n") * ) * ) * ), * @OA\Response( * response=400, * description="Datos no válidos. Faltan los parámetros 'name_template' o 'content_template'.", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="INVALID_INPUT"), * @OA\Property(property="message", type="string", example="Faltan datos requeridos: name_template y content_template son necesarios.") * ) * ), * @OA\Response( * response=403, * description="Intento de acceso no autorizado fuera del directorio de plantillas.", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="UNAUTHORIZED_ACCESS"), * @OA\Property(property="message", type="string", example="Intento de acceso no autorizado fuera del directorio de plantillas.") * ) * ), * @OA\Response( * response=500, * description="Ocurrió un error al crear la plantilla de arranque.", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="FILE_CREATION_ERROR"), * @OA\Property(property="message", type="string", example="Ocurrió un error al crear la plantilla de arranque.") * ) * ) * ) */ 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); } /** * @Route("/ogboot/v1/pxe-templates/{name}", methods={"DELETE"}) * * @OA\Delete( * path="/ogboot/v1/pxe-templates/{name}", * summary="Eliminar Plantilla", * description="Elimina una plantilla de arranque específica utilizando su nombre.", * @OA\Parameter( * name="name", * in="path", * required=true, * description="El nombre de la plantilla de arranque que se desea eliminar.", * @OA\Schema(type="string") * ), * @OA\Response( * response=200, * description="La plantilla de arranque se eliminó correctamente.", * @OA\JsonContent( * @OA\Property(property="success", type="string", example="Plantilla eliminada"), * @OA\Property(property="message", type="string", example="La plantilla mi_plantilla se ha borrado correctamente.") * ) * ), * @OA\Response( * response=404, * description="No se encontró ninguna plantilla de arranque con el nombre especificado.", * @OA\JsonContent( * @OA\Property(property="error", type="string", example="TEMPLATE_NOT_FOUND"), * @OA\Property(property="message", type="string", example="No se encontró la plantilla especificada") * ) * ), * @OA\Response( * response=500, * description="Ocurrió un error al eliminar la plantilla de arranque.", * @OA\JsonContent( * @OA\Property(property="error", type="string", example="FAILED_TO_DELETE_TEMPLATE"), * @OA\Property(property="message", type="string", example="Ocurrió un error al intentar eliminar la plantilla") * ) * ) * ) */ public function deleteTemplate(string $name): Response { $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(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']); } $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']); } return new Response(json_encode([ 'success' => 'Plantilla eliminada', 'message' => "La plantilla $name se ha borrado correctamente" ]), Response::HTTP_OK, ['Content-Type' => 'application/json']); } }