From 998cf16aa9f8c4e1d045d2eaf6c30c512f2ced0d Mon Sep 17 00:00:00 2001 From: lgromero Date: Wed, 3 Jul 2024 13:40:10 +0000 Subject: [PATCH] Updates src from the ogboot_symfony branch --- src/Controller/.gitignore | 0 src/Entity/.gitignore | 0 src/Kernel.php | 11 + src/OgBootBundle/Controller/.gitignore | 0 .../Controller/OgBootController.php | 1227 +++++++++++++++++ src/OgBootBundle/OgBootBundle.php | 10 + .../Service/CurlRequestService.php | 153 ++ src/Repository/.gitignore | 0 8 files changed, 1401 insertions(+) create mode 100644 src/Controller/.gitignore create mode 100644 src/Entity/.gitignore create mode 100644 src/Kernel.php create mode 100644 src/OgBootBundle/Controller/.gitignore create mode 100644 src/OgBootBundle/Controller/OgBootController.php create mode 100644 src/OgBootBundle/OgBootBundle.php create mode 100644 src/OgBootBundle/Service/CurlRequestService.php create mode 100644 src/Repository/.gitignore diff --git a/src/Controller/.gitignore b/src/Controller/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/src/Entity/.gitignore b/src/Entity/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/src/Kernel.php b/src/Kernel.php new file mode 100644 index 0000000..779cd1f --- /dev/null +++ b/src/Kernel.php @@ -0,0 +1,11 @@ +curlRequestService = $curlRequestService; + $this->logger = $logger; + } + +/*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/help", name="help", methods={"GET"}) + */ + public function help(): Response + { + + # $result = $this->curlRequestService->common_request(OG_REST_CMD_POWEROFF, POST, [OG_REST_PARAM_CLIENTS => $ips]); + $result = $this->curlRequestService->callOgLive("help"); + + if ($result) { + return new Response($result, Response::HTTP_OK); + } else { + return new Response('Failed', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + +/** + * @Route("/ogboot/v1/config", name="getConfig", methods={"GET"}) + * @OA\Get( + * path="/ogboot/v1/config", + * summary="Get ogboot configuration", + * @OA\Response( + * response=200, + * description="Configuration retrieved successfully", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="disk_usage", type="object", + * @OA\Property(property="total", type="string"), + * @OA\Property(property="used", type="string"), + * @OA\Property(property="available", type="string"), + * @OA\Property(property="percentage", type="string") + * ), + * @OA\Property(property="default_oglive", type="string"), + * @OA\Property(property="installed_oglives", type="array", @OA\Items(type="string")) + * ) + * ) + * ) + */ +public function getConfig(): Response +{ + // Call oglivecli to get disk usage + $diskUsageResult = $this->curlRequestService->callOgLive("disk_usage"); + + if (is_array($diskUsageResult) && isset($diskUsageResult['success']) && $diskUsageResult['success'] === false) { + return new JsonResponse(['error' => 'Failed to retrieve disk usage', 'details' => $diskUsageResult['error']], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + // Call oglivecli to get installed oglives and default oglives + $ogLiveConfigResult = $this->curlRequestService->callOgLive("list_installed_oglives"); + + if (is_array($ogLiveConfigResult) && isset($ogLiveConfigResult['success']) && $ogLiveConfigResult['success'] === false) { + return new JsonResponse(['error' => 'Failed to retrieve ogLive configuration', 'details' => $ogLiveConfigResult['error']], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + // Call oglivecli to get installed oglives and default oglives + $servicesStatusResult = $this->curlRequestService->callOgLive("check_services_status"); + + if (is_array($servicesStatusResult) && isset($servicesStatusResult['success']) && $servicesStatusResult['success'] === false) { + return new JsonResponse(['error' => 'Failed to retrieve services status', 'details' => $servicesStatusResult['error']], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + $response = [ + 'disk_usage' => $diskUsageResult, + 'default_oglive' => $ogLiveConfigResult['default_oglive'], + 'installed_oglives' => $ogLiveConfigResult['installed_ogLives'], + 'services_status' => $servicesStatusResult + ]; + + return new JsonResponse($response, Response::HTTP_OK); +} + + + + +/** + * @Route("/ogboot/v1/status", name="status", methods={"GET"}) + * @OA\Get( + * path="/ogboot/v1/status", + * summary="Get ogboot status", + * @OA\Response( + * response=200, + * description="Successful operation", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="message", + * type="string", + * description="Status message indicating any issues detected" + * ) + * ) + * ), + * @OA\Response( + * response=500, + * description="Failed to retrieve status" + * ) + * ) + */ + public function status(): Response + { + + # $result = $this->curlRequestService->common_request(OG_REST_CMD_POWEROFF, POST, [OG_REST_PARAM_CLIENTS => $ips]); + $result = $this->curlRequestService->callOgLive("check"); + + if ($result) { + return new Response($result, Response::HTTP_OK); + } else { + return new Response('Failed', Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + +/** + * @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="array", + * @OA\Items(type="object", + * @OA\Property(property="id", type="integer"), + * @OA\Property(property="filename", type="string"), + * @OA\Property(property="installed", type="boolean"), + * @OA\Property(property="compatible", type="boolean") + * ) + * ) + * ), + * @OA\Response( + * response=500, + * description="Failed to retrieve isos", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string"), + * @OA\Property(property="details", type="string") + * ) + * ) + * ) + */ +public function getDownloadMenu(): Response +{ + $downloadsOutput = $this->curlRequestService->callOgLive("download"); + + if (is_array($downloadsOutput) && isset($downloadsOutput['success']) && $downloadsOutput['success'] === false) { + return new JsonResponse(['error' => 'Failed to retrieve downloads', 'details' => $downloadsOutput['error']], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return new JsonResponse($downloadsOutput, 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="Successful operation", + * @OA\JsonContent( + * type="array", + * @OA\Items( + * type="object", + * @OA\Property( + * property="id", + * type="string", + * description="Unique identifier for the ogLive client, generated from linuxISO.sum", + * example="9e49a085ba74f97a81bdf9b3d0785094" + * ), + * @OA\Property( + * property="distribution", + * type="string", + * description="Distribution name of the installed ogLive client" + * ), + * @OA\Property( + * property="kernel", + * type="string", + * description="Kernel version of the installed ogLive client" + * ), + * @OA\Property( + * property="architecture", + * type="string", + * description="Architecture of the installed ogLive client" + * ), + * @OA\Property( + * property="revision", + * type="string", + * description="Revision of the installed ogLive client" + * ), + * @OA\Property( + * property="directory", + * type="string", + * description="Directory name of the installed ogLive client" + * ), + * @OA\Property( + * property="iso", + * type="string", + * description="ISO file name of the installed ogLive client" + * ) + * ) + * ) + * ), + * @OA\Response( + * response=404, + * description="No ogLive clients found" + * ) + * ) + */ +public function getOglives(): Response +{ + $directoryPath = '/opt/ogboot/tftpboot/'; + $pattern = '/^ogLive-([\w.-]+)-r(\d+)$/'; + + $ogLives = []; + + if ($handle = opendir($directoryPath)) { + error_log("Opened directory: $directoryPath"); + + while (false !== ($entry = readdir($handle))) { + error_log("Processing entry: $entry"); + + if ($entry != "." && $entry != ".." && is_dir($directoryPath . $entry)) { + if (preg_match($pattern, $entry, $matches)) { + error_log("Entry matches pattern: $entry"); + + // Evitar directorios .old + if (substr($entry, -4) === '.old') { + error_log("Ignoring old directory: $entry"); + continue; + } + + // Extraer detalles + $distribution = $matches[1]; + $revision = $matches[2]; + $directory = $entry; + $ogLivePath = $directoryPath . $entry; + + // Obtener el ID del archivo linuxISO.sum + $sumFile = $ogLivePath . '/linuxISO.sum'; + $id = ''; + if (file_exists($sumFile)) { + $id = trim(file_get_contents($sumFile)); + } + + // Obtener el nombre del archivo ISO + $iso = ''; + $oldInfoFile = $ogLivePath . '/OLDINFOFILE'; + if (file_exists($oldInfoFile)) { + $iso = trim(file($oldInfoFile, FILE_USE_INCLUDE_PATH | FILE_IGNORE_NEW_LINES)[0]); + } + + // Obtener la versión del kernel + $kernelFile = $ogLivePath . '/ogvmlinuz'; + $kernel = ''; + if (file_exists($kernelFile)) { + $fileOutput = shell_exec("file -bkr $kernelFile"); + if (preg_match('/Linux kernel.*version (\d+\.\d+\.\d+)/', $fileOutput, $kernelMatches)) { + $kernel = $kernelMatches[1]; + } + } + + // Asumir arquitectura como 'amd64'; puedes refinar esto si se necesita más lógica + $architecture = 'amd64'; + + // Construir el objeto + $ogLives[] = [ + 'id' => $id, + 'distribution' => $distribution, + 'kernel' => $kernel, + 'architecture' => $architecture, + 'revision' => $revision, + 'directory' => $directory, + 'iso' => $iso + ]; + } else { + error_log("Entry does not match pattern: $entry"); + } + } else { + error_log("Entry is not a directory: $directoryPath$entry"); + } + } + + closedir($handle); + error_log("Closed directory: $directoryPath"); + } + + if (!empty($ogLives)) { + error_log("Returning ogLives data"); + return new JsonResponse($ogLives, Response::HTTP_OK); + } else { + error_log("No ogLive clients found"); + return new JsonResponse(['error' => 'No ogLive clients found'], Response::HTTP_NOT_FOUND); + } +} + +/** + * @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="distribution", + * type="string", + * description="Distribution name of the default ogLive client" + * ), + * @OA\Property( + * property="kernel", + * type="string", + * description="Kernel version of the default ogLive client" + * ), + * @OA\Property( + * property="architecture", + * type="string", + * description="Architecture of the default ogLive client" + * ), + * @OA\Property( + * property="revision", + * type="string", + * description="Revision of the default ogLive client" + * ), + * @OA\Property( + * property="directory", + * type="string", + * description="Directory name of the default ogLive client" + * ), + * @OA\Property( + * property="iso", + * type="string", + * description="ISO file name of the default ogLive client" + * ) + * ) + * ), + * @OA\Response( + * response=500, + * description="Failed to retrieve information of the default ogLive client" + * ) + * ) + */ +public function getOgliveDefault(Request $request): Response +{ + $result = $this->curlRequestService->callOgLive("get_default"); + + if (is_array($result) && isset($result['error'])) { + return new JsonResponse(['error' => $result['error']], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return new JsonResponse($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="Successful operation", + * @OA\JsonContent( + * type="object", + * @OA\Property( + * property="distribution", + * type="string", + * description="Distribution name of the installed ogLive client" + * ), + * @OA\Property( + * property="kernel", + * type="string", + * description="Kernel version of the installed ogLive client" + * ), + * @OA\Property( + * property="architecture", + * type="string", + * description="Architecture of the installed ogLive client" + * ), + * @OA\Property( + * property="revision", + * type="string", + * description="Revision of the installed ogLive client" + * ), + * @OA\Property( + * property="directory", + * type="string", + * description="Directory name of the installed ogLive client" + * ), + * @OA\Property( + * property="iso", + * type="string", + * description="ISO file name of the installed ogLive client" + * ) + * ) + * ), + * @OA\Response( + * response=404, + * description="ogLive client not found" + * ) + * ) + */ +public function getOglive(string $checksum): Response +{ + $result = $this->curlRequestService->callOgLive("get_info " . escapeshellarg($checksum)); + + if (is_array($result) && isset($result['error'])) { + return new JsonResponse(['error' => $result['error']], Response::HTTP_NOT_FOUND); + } + + return new JsonResponse($result, Response::HTTP_OK); +} + + + + + + + +/** + * @Route("/ogboot/v1/oglives/default", name="setOgliveDefault", methods={"POST"}) + * @OA\Post( + * 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="message", type="string") + * ) + * ), + * @OA\Response( + * response=404, + * description="ogLive client not found" + * ), + * @OA\Response( + * response=500, + * description="Failed to set default ogLive client" + * ) + * ) + */ +public function setOgliveDefault(Request $request): Response +{ + $data = json_decode($request->getContent(), true); + + if (!isset($data['checksum'])) { + return new JsonResponse(['error' => 'Invalid input data'], Response::HTTP_BAD_REQUEST); + } + + $checksum = $data['checksum']; + + // Establecer el ogLive como predeterminado utilizando el checksum proporcionado + $result = $this->curlRequestService->callOgLive("set_default " . escapeshellarg($checksum)); + + if (is_array($result) && isset($result['error'])) { + return new JsonResponse(['error' => $result['error']], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return new JsonResponse(['message' => 'ogLive client set as default successfully'], 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="isoname", + * type="string", + * example="ogLive-focal-5.13.0-27-beta-amd64-r20210706.5b4bf5f.iso", + * description="Name of the ogLive ISO to install" + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="ogLive client installed successfully", + * @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\Response( + * response=500, + * description="Failed to install ogLive client", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string"), + * @OA\Property(property="details", type="string") + * ) + * ) + * ) + */ +public function installOglive(Request $request): Response +{ + $data = json_decode($request->getContent(), true); + + if (!isset($data['isoname'])) { + return new JsonResponse(['error' => 'Invalid input data'], Response::HTTP_BAD_REQUEST); + } + + $isoname = $data['isoname']; + $result = $this->curlRequestService->callOgLive("install " . escapeshellarg($isoname)); + + if (is_array($result) && isset($result['success']) && $result['success'] === false) { + return new JsonResponse(['error' => 'Failed to install ogLive client', 'details' => $result['error']], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + return new JsonResponse(['message' => 'ogLive client installed successfully', 'details' => $result], Response::HTTP_OK); +} + + + +/** + * @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 +{ + // Llamada al servicio para desinstalar el cliente ogLive + error_log("Calling curlRequestService with checksum: $checksum"); + $result = $this->curlRequestService->callOgLive("uninstall " . escapeshellarg($checksum)); + + // Verificar la respuesta del servicio + error_log("Service call result: " . print_r($result, true)); + + if (is_array($result) && isset($result['error'])) { + // Error encontrado en la respuesta + error_log("Error found in response: " . $result['error']); + return new JsonResponse(['error' => $result['error']], Response::HTTP_INTERNAL_SERVER_ERROR); + } elseif (is_array($result) && isset($result['message'])) { + // Respuesta exitosa + error_log("Success response: " . print_r($result, true)); + return new JsonResponse([ + 'message' => $result['message'], + 'details' => $result['details'] ?? '' + ], Response::HTTP_OK); + } else { + // Manejar el caso en que no se recibió una respuesta válida del servicio + error_log("Failed to uninstall ogLive client, empty result from service"); + return new JsonResponse(['error' => 'Failed to uninstall ogLive client'], 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="boot_files", + * type="array", + * @OA\Items( + * type="string", + * description="Boot file" + * ) + * ) + * ) + * ), + * @OA\Response( + * response=500, + * description="Failed to retrieve boot files" + * ) + * ) + */ +public function getBootFiles(): JsonResponse +{ + + $directory = '/opt/ogboot/tftpboot/ipxe_scripts'; + + + $files = scandir($directory); + + + $bootFiles = array_filter($files, function ($file) use ($directory) { + return !is_dir($directory . '/' . $file) && strpos($file, '01-') === 0; + }); + + $response = [ + 'boot_files' => 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="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=500, + * description="Failed to retrieve boot file" + * ) + * ) + */ +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; + + $filePath = "$directory/$fileName"; + + if (!file_exists($filePath)) { + return new JsonResponse(['error' => 'No se encontró ningún archivo de arranque con la dirección MAC especificada'], Response::HTTP_NOT_FOUND); + } + + $content = file_get_contents($filePath); + $templateName = ''; + $contentLines = explode("\n", $content); + foreach ($contentLines as $line) { + if (strpos($line, '#template_name:') !== false) { + $templateName = trim(str_replace('#template_name:', '', $line)); + break; + } + } + + // Extraer los parámetros del contenido del archivo iPXE + preg_match('/set kernelargs (.*)/', $content, $matches); + if (isset($matches[1])) { + $kernelargs = $matches[1]; + 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'] ?? '', + 'oglog' => $params['oglog'] ?? '', + 'ogshare' => $params['ogshare'] ?? '', + 'oglivedir' => $params['oglivedir'] ?? '', + 'ogprof' => $params['ogprof'] ?? '', + 'hardprofile' => $params['hardprofile'] ?? '', + 'ogntp' => $params['ogntp'] ?? '', + 'ogdns' => $params['ogdns'] ?? '', + 'ogproxy' => $params['ogproxy'] ?? '', + 'ogunit' => $params['ogunit'] ?? '', + 'resolution' => $params['vga'] ?? '', + ]; + + return new JsonResponse($result, Response::HTTP_OK); + } + + return new JsonResponse(['error' => 'Failed to parse kernel arguments from the boot file'], Response::HTTP_INTERNAL_SERVER_ERROR); +} + + /** + * @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): JsonResponse + { + $data = json_decode($request->getContent(), true); + + $templateName = $data['template_name'] ?? null; + $mac = $data['mac'] ?? null; + $serverIp = $data['server_ip'] ?? null; + $ogLiveDir = $data['oglivedir'] ?? 'ogLive'; + + if (!$templateName || !$mac || !$serverIp) { + return new JsonResponse(['error' => 'Invalid input'], Response::HTTP_BAD_REQUEST); + } + + $parameters = [ + 'LANG' => $data['lang'] ?? 'es_ES.UTF-8', + 'ip' => $data['ip'] ?? '', + 'router' => $data['router'] ?? '', + 'netmask' => $data['netmask'] ?? '', + 'computer_name' => $data['computer_name'] ?? '', + 'netiface' => $data['netiface'] ?? '', + 'group' => $data['group'] ?? '', + 'ogrepo' => $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' + ]; + + $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; + $templatePath = $templateDir . '/' . $templateName; + + if (!file_exists($templatePath)) { + return new JsonResponse(['error' => 'Template not found'], Response::HTTP_NOT_FOUND); + } + + $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__', '__OGLIVE__'], [$kernelArgs, $serverIp, $ogLiveDir], $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"}) + * @OA\Delete( + * path="/ogboot/v1/pxes/{mac}", + * summary="Eliminar archivo de arranque", + * description="Elimina un archivo de arranque específico utilizando su dirección MAC.", + * @OA\Parameter( + * name="mac", + * in="path", + * description="La dirección MAC del cliente cuyo archivo de arranque se desea eliminar", + * required=true, + * @OA\Schema(type="string", example="00:11:22:33:44:55") + * ), + * @OA\Response( + * response=200, + * description="El archivo de arranque se eliminó correctamente", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="message", type="string", example="El archivo de arranque se eliminó correctamente") + * ) + * ), + * @OA\Response( + * response=404, + * description="No se encontró ningún archivo de arranque con la dirección MAC especificada", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="No se encontró ningún archivo de arranque con la dirección MAC especificada") + * ) + * ), + * @OA\Response( + * response=500, + * description="Error al eliminar el archivo de arranque", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="Error al eliminar el archivo de arranque") + * ) + * ) + * ) + */ + public function deleteBootFile(string $mac): Response + { + $directory = '/opt/ogboot/tftpboot/ipxe_scripts'; + + $fileName = "01-" . $mac; + + $filePath = "$directory/$fileName"; + + if (!file_exists($filePath)) { + + return new JsonResponse(['error' => 'No se encontró ningún archivo de arranque con la dirección MAC especificada'], Response::HTTP_NOT_FOUND); + } + + if (!unlink($filePath)) { + + return new JsonResponse(['error' => 'Error al eliminar el archivo de arranque'], Response::HTTP_INTERNAL_SERVER_ERROR); + } + 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("/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", + * 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="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( + * response=200, + * description="La plantilla de arranque se creó exitosamente.", + * @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\nkernel tftp://SERVERIP/ogLive/ogvmlinuz ${kernelargs}\ninitrd tftp://SERVERIP/ogLive/oginitrd.img\nboot") + * ) + * ), + * @OA\Response( + * response=400, + * description="La solicitud no pudo ser procesada debido a un error en los datos proporcionados en el cuerpo de la solicitud." + * ), + * @OA\Response( + * response=500, + * description="Ocurrió un error al crear la plantilla de arranque." + * ) + * ) + */ + public function createTemplate(Request $request) + { + $data = json_decode($request->getContent(), true); + + if (!isset($data['name_template']) || !isset($data['content_template'])) { + return new Response('La solicitud no pudo ser procesada debido a un error en los datos proporcionados en el cuerpo de la solicitud.', 400); + } + + $nameTemplate = $data['name_template']; + $contentTemplate = $data['content_template']; + $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; + if (!preg_match('/^[a-zA-Z0-9._-]+$/', $nameTemplate)) { + return new Response('Nombre de la plantilla no válido.', 400); + } + + $filePath = $templateDir . '/' . $nameTemplate; + $contentTemplate = str_replace("\\n", "\n", $contentTemplate); + try { + file_put_contents($filePath, $contentTemplate); + } catch (\Exception $e) { + return new Response('Ocurrió un error al crear la plantilla de arranque.', 500); + } + + return new Response(json_encode([ + 'message' => 'Plantilla creada exitosamente.', + 'template' => $contentTemplate + ]), 200, ['Content-Type' => 'application/json']); + } + + + + /** + * @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( + * type="object", + * @OA\Property(property="message", type="string", example="Plantilla eliminada correctamente.") + * ) + * ), + * @OA\Response( + * response=404, + * description="No se encontró ninguna plantilla de arranque con el nombre especificado." + * ), + * @OA\Response( + * response=500, + * description="Ocurrió un error al eliminar la plantilla de arranque." + * ) + * ) + */ + public function deleteTemplate($name) + { + $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; + $filePath = $templateDir . '/' . $name; + + if (!file_exists($filePath)) { + return new Response('No se encontró ninguna plantilla de arranque con el nombre especificado.', 404); + } + + try { + unlink($filePath); + } catch (\Exception $e) { + return new Response('Ocurrió un error al eliminar la plantilla de arranque.', 500); + } + + return new Response(json_encode([ + 'message' => 'Plantilla eliminada correctamente.' + ]), 200, ['Content-Type' => 'application/json']); + } + + +} diff --git a/src/OgBootBundle/OgBootBundle.php b/src/OgBootBundle/OgBootBundle.php new file mode 100644 index 0000000..4a2e467 --- /dev/null +++ b/src/OgBootBundle/OgBootBundle.php @@ -0,0 +1,10 @@ + false, 'output' => 'Error al crear el socket']; + } + + $result = socket_connect($socket, $socketPath); + if ($result === false) { + syslog(LOG_ERR, 'Error al conectar con el socket: ' . socket_strerror(socket_last_error($socket))); + socket_close($socket); + return ['success' => false, 'output' => 'Error al conectar con el socket']; + } + + $command = [ + 'action' => 'download', + 'args' => [$isoname] + ]; + + socket_write($socket, json_encode($command), strlen(json_encode($command))); + + $response = ''; + $status = []; + + while ($buffer = socket_read($socket, 2048)) { + $response .= $buffer; + $status[] = json_decode($buffer, true); + } + + socket_close($socket); + + // Analiza el último estado recibido + $lastStatus = end($status); + if ($lastStatus && $lastStatus['status'] === 'completed') { + return ['success' => true, 'output' => $lastStatus]; + } else { + return ['success' => false, 'output' => $status]; + } +} + + + +public function callOgLive($parameter) +{ + $socketPath = '/var/run/oglive/oglive_daemon.sock'; + + // Registrar el parámetro recibido + file_put_contents('/tmp/serviceOglive.log', 'callOgLive called with parameter: ' . $parameter . PHP_EOL, FILE_APPEND); + + $socket = socket_create(AF_UNIX, SOCK_STREAM, 0); + if ($socket === false) { + $error = 'Error al crear el socket: ' . socket_strerror(socket_last_error()); + file_put_contents('/tmp/serviceOglive.log', 'Socket creation error: ' . $error . PHP_EOL, FILE_APPEND); + return [ + 'success' => false, + 'error' => $error + ]; + } + + $result = socket_connect($socket, $socketPath); + if ($result === false) { + $error = 'Error al conectar con el socket: ' . socket_strerror(socket_last_error($socket)); + file_put_contents('/tmp/serviceOglive.log', 'Socket connection error: ' . $error . PHP_EOL, FILE_APPEND); + socket_close($socket); + return [ + 'success' => false, + 'error' => $error + ]; + } + + $args = array_map('trim', explode(' ', $parameter)); + $action = array_shift($args); + $command = [ + 'action' => $action, + 'args' => $args + ]; + + socket_write($socket, json_encode($command), strlen(json_encode($command))); + + $response = ''; + while ($buffer = socket_read($socket, 2048)) { + $response .= $buffer; + } + + socket_close($socket); + + // Registrar la respuesta en bruto + file_put_contents('/tmp/serviceOglive.log', 'Raw response: ' . $response . PHP_EOL, FILE_APPEND); + + if (empty($response)) { + $error = 'Respuesta vacía del demonio'; + file_put_contents('/tmp/serviceOglive.log', 'Empty response error: ' . $error . PHP_EOL, FILE_APPEND); + return [ + 'success' => false, + 'error' => $error + ]; + } + + $decodedResponse = json_decode($response, true); + + // Registrar cualquier error de decodificación JSON + if (json_last_error() !== JSON_ERROR_NONE) { + $error = 'Error al decodificar JSON: ' . json_last_error_msg(); + file_put_contents('/tmp/serviceOglive.log', 'JSON decode error: ' . $error . PHP_EOL, FILE_APPEND); + return [ + 'success' => false, + 'error' => $error + ]; + } + + if (isset($decodedResponse['success']) && $decodedResponse['success']) { + // Registrar la respuesta decodificada + file_put_contents('/tmp/serviceOglive.log', 'Decoded successful response: ' . json_encode($decodedResponse['output']) . PHP_EOL, FILE_APPEND); + return $decodedResponse['output']; + } else { + $error = $decodedResponse['error'] ?? 'Unknown error'; + file_put_contents('/tmp/serviceOglive.log', 'Error in response: ' . $error . PHP_EOL, FILE_APPEND); + return [ + 'success' => false, + 'error' => $error + ]; + } +} + + + + +} diff --git a/src/Repository/.gitignore b/src/Repository/.gitignore new file mode 100644 index 0000000..e69de29