diff --git a/src/OgBootBundle/Controller/OgBootController.php b/src/OgBootBundle/Controller/OgBootController.php index ee2dcb1..1df8420 100644 --- a/src/OgBootBundle/Controller/OgBootController.php +++ b/src/OgBootBundle/Controller/OgBootController.php @@ -404,7 +404,7 @@ Regenerar plantilla - PUT /ogboot/pxe-templates } - //Mostrar menú de descarga de imagen de ogLive - GET /ogboot/images/download + /** * @Route("/ogboot/images/download", name="getDownloadMenu", methods={"GET"}) @@ -430,28 +430,26 @@ Regenerar plantilla - PUT /ogboot/pxe-templates public function getDownloadMenu(): Response { - // Ejecutar el comando "oglivecli download" y obtener la salida + $downloadsOutput = $this->curlRequestService->callOgLive("download"); - // Separar la salida por líneas + $downloads = explode("\n", trim($downloadsOutput)); $result = []; foreach ($downloads as $download) { - // Define el patrón de la expresión regular + $pattern = '/^(\d+)\) (\(\*\))?\s?(\(\+\))?\s?(.+\.iso)$/'; - // Realiza el matcheo de la línea con el patrón + preg_match($pattern, $download, $matches); - - // Si hay coincidencias, extrae la información + if (!empty($matches)) { $id = $matches[1]; $installed = !empty($matches[2]); $compatible = !empty($matches[3]); $filename = $matches[4]; - - // Agrega la información al resultado + $result[] = [ 'id' => $id, 'filename' => $filename, @@ -461,10 +459,9 @@ Regenerar plantilla - PUT /ogboot/pxe-templates } } - // Convertir el array de downloads a JSON $jsonDownloads = json_encode($result); - // Crear una respuesta JSON y devolverla + return new Response($jsonDownloads, Response::HTTP_OK, [ 'Content-Type' => 'application/json' ]); @@ -561,172 +558,353 @@ Regenerar plantilla - PUT /ogboot/pxe-templates } } - /** - * @Route("/ogboot/v1/pxes", name="config", methods={"GET"}) + * @Route("/ogboot/v1/pxes", name="get_boot_files", methods={"GET"}) * @OA\Get( * path="/ogboot/v1/pxes", - * summary="Get ogboot configuration", + * summary="Get all PXE boot files", * @OA\Response( * response=200, * description="Successful operation", * @OA\JsonContent( * type="object", * @OA\Property( - * property="config-file", - * type="string", - * description="Configuration file path" - * ), - * @OA\Property( - * property="download-url", - * type="string", - * description="ogLive download URL" - * ), - * @OA\Property( - * property="download-dir", - * type="string", - * description="ogLive download directory" - * ), - * @OA\Property( - * property="install-dir", - * type="string", - * description="ogLive installation directory" - * ), - * @OA\Property( - * property="default-name", - * type="string", - * description="Default ogLive name" - * ), - * @OA\Property( - * property="min-release", - * type="string", - * description="Minimum compatibility release" + * property="boot_files", + * type="array", + * @OA\Items( + * type="string", + * description="Boot file" + * ) * ) * ) * ), * @OA\Response( * response=500, - * description="Failed to retrieve configuration" + * description="Failed to retrieve boot files" * ) * ) */ public function getBootFiles(): JsonResponse { - // Ruta donde están alojados los archivos de arranque + $directory = '/opt/ogboot/tftpboot/ipxe_scripts'; - // Escanea el directorio y obtiene la lista de archivos de arranque + $files = scandir($directory); - // Filtra los archivos que no son directorios ni archivos ocultos - $bootFiles = array_filter($files, function ($file) { - return !in_array($file, ['.', '..', '.gitkeep']) && !is_dir($file); + + $bootFiles = array_filter($files, function ($file) use ($directory) { + return !is_dir($directory . '/' . $file) && strpos($file, '01-') === 0; }); - // Construye la respuesta JSON con la lista de archivos de arranque $response = [ 'boot_files' => array_values($bootFiles) ]; - // Devuelve la respuesta JSON return new JsonResponse($response, Response::HTTP_OK); } /** * @Route("/ogboot/v1/pxes/{mac}", name="ogboot_boot_file", methods={"GET"}) - */ + * @OA\Get( + * path="/ogboot/v1/pxes/{mac}", + * summary="Obtener archivo de arranque PXE", + * description="Obtiene el contenido del archivo de arranque PXE para una dirección MAC específica.", + * @OA\Parameter( + * name="mac", + * in="path", + * required=true, + * description="Dirección MAC del cliente", + * @OA\Schema(type="string", example="00:50:56:22:11:12") + * ), + * @OA\Response( + * response=200, + * description="Contenido del archivo de arranque PXE", + * @OA\MediaType( + * mediaType="text/plain", + * @OA\Schema(type="string", example="Contenido del archivo PXE...") + * ) + * ), + * @OA\Response( + * response=404, + * description="Archivo no encontrado" + * ) + * ) + */ public function getBootFile(string $mac): Response { - // Ruta donde están alojados los archivos de arranque + $directory = '/opt/ogboot/tftpboot/ipxe_scripts'; - // Genera el nombre del archivo basado en la dirección MAC + $fileName = "01-" . $mac; - // Path completo del archivo + $filePath = "$directory/$fileName"; - // Verifica si el archivo existe + if (!file_exists($filePath)) { - // Devuelve una respuesta 404 si no se encuentra el archivo de arranque + return new Response(null, Response::HTTP_NOT_FOUND); } - // Lee el contenido del archivo $content = file_get_contents($filePath); - // Devuelve el contenido del archivo como texto plano return new Response($content, Response::HTTP_OK, ['Content-Type' => 'text/plain']); } /** - * @Route("/ogboot/v1/pxes", name="ogboot_create_boot_file", methods={"POST"}) + * @Route("/ogboot/v1/pxes", name="create_boot_file", methods={"POST"}) + * @OA\Post( + * path="/ogboot/v1/pxes", + * summary="Create a new PXE boot file", + * @OA\RequestBody( + * required=true, + * @OA\JsonContent( + * type="object", + * @OA\Property(property="template_name", type="string", description="Template name", example="pxe"), + * @OA\Property(property="mac", type="string", description="MAC address", example="00:50:56:22:11:12"), + * @OA\Property(property="lang", type="string", description="Language", example="es_ES.UTF-8"), + * @OA\Property(property="ip", type="string", description="IP address", example="192.168.2.11"), + * @OA\Property(property="server_ip", type="string", description="Server IP address", example="192.168.2.1"), + * @OA\Property(property="router", type="string", description="Router", example="192.168.2.1"), + * @OA\Property(property="netmask", type="string", description="Netmask", example="255.255.255.0"), + * @OA\Property(property="computer_name", type="string", description="Computer name", example="pc11"), + * @OA\Property(property="netiface", type="string", description="Network interface", example="eth0"), + * @OA\Property(property="group", type="string", description="Group", example="Aula_virtual"), + * @OA\Property(property="ogrepo", type="string", description="Repository IP", example="192.168.2.1"), + * @OA\Property(property="oglive", type="string", description="Live server IP", example="192.168.2.1"), + * @OA\Property(property="oglog", type="string", description="Log server IP", example="192.168.2.1"), + * @OA\Property(property="ogshare", type="string", description="Share server IP", example="192.168.2.1"), + * @OA\Property(property="oglivedir", type="string", description="Live directory", example="ogLive"), + * @OA\Property(property="ogprof", type="string", description="Is professor", example="false"), + * @OA\Property(property="hardprofile", type="string", description="Hardware profile", example=""), + * @OA\Property(property="ogntp", type="string", description="NTP server", example=""), + * @OA\Property(property="ogdns", type="string", description="DNS server", example=""), + * @OA\Property(property="ogproxy", type="string", description="Proxy server", example=""), + * @OA\Property(property="ogunit", type="string", description="Unit directory", example=""), + * @OA\Property(property="resolution", type="string", description="Screen resolution", example="788") + * ) + * ), + * @OA\Response( + * response=200, + * description="PXE boot file created successfully", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="message", type="string") + * ) + * ), + * @OA\Response( + * response=400, + * description="Invalid input" + * ), + * @OA\Response( + * response=404, + * description="Template not found" + * ), + * @OA\Response( + * response=500, + * description="Internal server error" + * ) + * ) */ - public function createBootFile(Request $request): Response + public function createBootFile(Request $request): JsonResponse { - // Obtener los datos del cuerpo de la solicitud - $requestData = json_decode($request->getContent(), true); + $data = json_decode($request->getContent(), true); - // Verificar si los datos necesarios están presentes en la solicitud - if (!isset($requestData['mac']) || !isset($requestData['config'])) { - return new JsonResponse(['error' => 'Los datos de la solicitud son incorrectos'], Response::HTTP_BAD_REQUEST); + $templateName = $data['template_name'] ?? null; + $mac = $data['mac'] ?? null; + $serverIp = $data['server_ip'] ?? null; + + if (!$templateName || !$mac || !$serverIp) { + return new JsonResponse(['error' => 'Invalid input'], Response::HTTP_BAD_REQUEST); } - // Directorio donde se almacenarán los archivos de arranque - $directory = '/opt/ogboot/tftpboot/ipxe_scripts'; + $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' => $data['ogrepo'] ?? '', + 'oglive' => $data['oglive'] ?? $serverIp, + 'oglog' => $data['oglog'] ?? $serverIp, + 'ogshare' => $data['ogshare'] ?? $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' + ]; - // Generar el nombre del archivo basado en la dirección MAC - $fileName = "01-" . str_replace(':', '-', $requestData['mac']); + $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; + $templatePath = $templateDir . '/' . $templateName; - // Ruta completa del archivo - $filePath = "$directory/$fileName"; - - // Contenido del archivo de arranque - $fileContent = $requestData['config']; - - // Intentar crear el archivo de arranque - if (file_put_contents($filePath, $fileContent) === false) { - // Devolver un error si no se puede crear el archivo - return new JsonResponse(['error' => 'Error al crear el archivo de arranque'], Response::HTTP_INTERNAL_SERVER_ERROR); + if (!file_exists($templatePath)) { + return new JsonResponse(['error' => 'Template not found'], Response::HTTP_NOT_FOUND); } - // Devolver una respuesta de éxito si el archivo se creó correctamente - return new JsonResponse(['message' => 'El archivo de arranque se creó exitosamente'], Response::HTTP_OK); + $templateContent = file_get_contents($templatePath); + if ($templateContent === false) { + return new JsonResponse(['error' => 'Failed to read template'], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $kernelArgs = 'ro boot=oginit quiet splash irqpoll acpi=on og2nd=sqfs ogprotocol=smb ogactiveadmin=true ogdebug=true ogtmpfs=15 ' . + 'oglivedir=' . $parameters['oglivedir'] . ' ' . + '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'] . ' ' . + 'oglive=' . $serverIp . ' ' . + 'oglog=' . $serverIp . ' ' . + 'ogshare=' . $serverIp . ' ' . + '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'])); + + + $pxeContent = str_replace(['__INFOHOST__', '__SERVERIP__'], [$kernelArgs, $serverIp], $templateContent); + + + $pxeFileName = '01-' . $mac; + + $pxeFilePath = '/opt/ogboot/tftpboot/ipxe_scripts/' . $pxeFileName; + + if (file_put_contents($pxeFilePath, $pxeContent) === false) { + return new JsonResponse(['error' => 'Failed to create PXE boot file'], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return new JsonResponse(['message' => 'PXE boot file created successfully'], Response::HTTP_OK); } - /** * @Route("/ogboot/v1/pxes/{mac}", name="ogboot_delete_boot_file", methods={"DELETE"}) */ public function deleteBootFile(string $mac): Response { - // Directorio donde están almacenados los archivos de arranque $directory = '/opt/ogboot/tftpboot/ipxe_scripts'; - // Generar el nombre del archivo basado en la dirección MAC $fileName = "01-" . str_replace(':', '-', $mac); - // Ruta completa del archivo $filePath = "$directory/$fileName"; - // Verificar si el archivo existe if (!file_exists($filePath)) { - // Devolver un error 404 si el archivo no se encuentra + return new JsonResponse(['error' => 'No se encontró ningún archivo de arranque con la dirección MAC especificada'], Response::HTTP_NOT_FOUND); } - // Intentar eliminar el archivo de arranque if (!unlink($filePath)) { - // Devolver un error 500 si no se puede eliminar el archivo + return new JsonResponse(['error' => 'Error al eliminar el archivo de arranque'], Response::HTTP_INTERNAL_SERVER_ERROR); } - - // Devolver una respuesta de éxito si el archivo se eliminó correctamente return new JsonResponse(['message' => 'El archivo de arranque se eliminó correctamente'], 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="templates", + * type="array", + * @OA\Items(type="string", example="mi_plantilla.ipxe") + * ) + * ) + * ), + * @OA\Response( + * response=500, + * description="Error interno del servidor" + * ) + * ) + */ + public function getAllTemplates(): JsonResponse + { + $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; + + // Verificar si el directorio existe + if (!is_dir($templateDir)) { + return new JsonResponse(['error' => 'Directorio de plantillas no encontrado'], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + // Obtener la lista de archivos en el directorio + $files = scandir($templateDir); + + // Filtrar los archivos válidos (excluir directorios y archivos ocultos) + $templates = array_filter($files, function ($file) use ($templateDir) { + return !in_array($file, ['.', '..']) && !is_dir($templateDir . '/' . $file); + }); + + return new JsonResponse(['templates' => array_values($templates)], Response::HTTP_OK); + } /** - * @Route("", methods={"POST"}) + * @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\MediaType( + * mediaType="text/plain", + * @OA\Schema(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\Response( + * response=500, + * description="Error interno del servidor" + * ) + * ) + */ + public function getTemplate(string $templateName): Response + { + $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; + $filePath = "$templateDir/$templateName"; + + if (!file_exists($filePath)) { + return new Response(null, Response::HTTP_NOT_FOUND); + } + + $content = file_get_contents($filePath); + if ($content === false) { + return new Response('Error al leer la plantilla', Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return new Response($content, Response::HTTP_OK, ['Content-Type' => 'text/plain']); + } + + /** + * @Route("/ogboot/v1/pxe-templates", methods={"POST"}) * * @OA\Post( * path="/ogboot/v1/pxe-templates", @@ -736,8 +914,8 @@ public function getBootFiles(): JsonResponse * 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\n\nset ISODIR ogLive\nset default 0\nset kernelargs INFOHOST\ntime kernel tftp://172.17.8.71/ogLive/ogvmlinuz ${kernelargs}\ntime initrd tftp://172.17.8.71/ogLive/oginitrd.img\nboot") + * @OA\Property(property="name_template", type="string", example="pxe"), + * @OA\Property(property="content_template", 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( @@ -746,7 +924,7 @@ public function getBootFiles(): JsonResponse * @OA\JsonContent( * type="object", * @OA\Property(property="message", type="string", example="Plantilla creada exitosamente."), - * @OA\Property(property="template", type="string", example="#!ipxe\nset timeout 0\nset timeout-style hidden\n\nset ISODIR ogLive\nset default 0\nset kernelargs INFOHOST\ntime kernel tftp://172.17.8.71/ogLive/ogvmlinuz ${kernelargs}\ntime initrd tftp://172.17.8.71/ogLive/oginitrd.img\nboot") + * @OA\Property(property="template", 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( @@ -770,15 +948,12 @@ public function getBootFiles(): JsonResponse $nameTemplate = $data['name_template']; $contentTemplate = $data['content_template']; $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; - - // Validar nombre del archivo if (!preg_match('/^[a-zA-Z0-9._-]+$/', $nameTemplate)) { return new Response('Nombre de la plantilla no válido.', 400); } - // Crear la ruta completa del archivo $filePath = $templateDir . '/' . $nameTemplate; - + $contentTemplate = str_replace("\\n", "\n", $contentTemplate); try { file_put_contents($filePath, $contentTemplate); } catch (\Exception $e) { @@ -792,8 +967,9 @@ public function getBootFiles(): JsonResponse } + /** - * @Route("/{name}", methods={"DELETE"}) + * @Route("/ogboot/v1/pxe-templates/{name}", methods={"DELETE"}) * * @OA\Delete( * path="/ogboot/v1/pxe-templates/{name}", diff --git a/src/OgBootBundle/Service/CurlRequestService.php b/src/OgBootBundle/Service/CurlRequestService.php index efbce14..28ae73a 100644 --- a/src/OgBootBundle/Service/CurlRequestService.php +++ b/src/OgBootBundle/Service/CurlRequestService.php @@ -66,22 +66,13 @@ class CurlRequestService public function callOgLive($parameter) { - #$command = sprintf('%s/bin/oglivecli %s', dirname(dirname(dirname(__DIR__))), escapeshellarg($parameter)); - // Ruta completa al script oglivecli $ogLiveCliPath = sprintf("%s/bin/oglivecli", dirname(dirname(dirname(__DIR__)))); - - // Construye el comando con sudo y la contraseña proporcionada - $sudoPassword = 'qindel'; - // Construye el comando con sudo y la contraseña proporcionada - $command = sprintf( - "echo %s | sudo -S %s %s", - escapeshellarg($sudoPassword), - $ogLiveCliPath, - $parameter // Sin comillas simples alrededor de $parameter - ); + + $command = sprintf("%s %s", $ogLiveCliPath, escapeshellarg($parameter)); + syslog(LOG_INFO, 'command '.$command); $output = shell_exec($command); - + return $output; } }