diff --git a/src/DhcpBundle/Controller/DhcpController.php b/src/DhcpBundle/Controller/DhcpController.php index b11adad..b46f948 100644 --- a/src/DhcpBundle/Controller/DhcpController.php +++ b/src/DhcpBundle/Controller/DhcpController.php @@ -276,139 +276,149 @@ public function getSubnets(): JsonResponse } } - /** - * @OA\Post( - * path="/ogdhcp/v1/subnets", - * summary="Add a new DHCP subnet", - * @OA\RequestBody( - * description="JSON payload to create a new subnet", - * required=true, - * @OA\JsonContent( - * type="object", - * @OA\Property(property="subnetId", type="integer", example=2), - * @OA\Property(property="mask", type="string", example="255.255.255.0"), - * @OA\Property(property="address", type="string", example="192.168.1.0"), - * @OA\Property(property="nextServer", type="string", example="192.168.1.1"), - * @OA\Property(property="bootFileName", type="string", example="pxelinux.0") - * ) - * ), - * @OA\Response( - * response=200, - * description="Subnet added successfully", - * @OA\JsonContent( - * type="object", - * @OA\Property(property="success", type="string", example="Subnet added successfully"), - * @OA\Property(property="message", type="object", - * @OA\Property(property="subnetId", type="integer", example=2), - * @OA\Property(property="subnet", type="string", example="192.168.1.0/24"), - * @OA\Property(property="nextServer", type="string", example="192.168.1.1"), - * @OA\Property(property="bootFileName", type="string", example="pxelinux.0"), - * @OA\Property(property="reservations", type="array", @OA\Items(type="object")) - * ) - * ) - * ), - * @OA\Response( - * response=400, - * description="Error occurred while adding the subnet", - * @OA\JsonContent( - * type="object", - * @OA\Property(property="error", type="string", example="Error: La subred con el nombre 'subnetName' ya existe.") - * ) - * ), - * @OA\Response( - * response=400, - * description="Error occurred while adding the subnet", - * @OA\JsonContent( - * type="object", - * @OA\Property(property="error", type="string", example="Error: La subred con el ID 'subnetId' ya existe.") - * ) - * ) - * ) - * @Route("/ogdhcp/v1/subnets", methods={"POST"}) - */ - - public function addDhcpSubnet(Request $request): JsonResponse - { - try { - $input = json_decode($request->getContent()); - $subnetId = (int) htmlspecialchars($input->subnetId); - $mask = htmlspecialchars($input->mask); - $address = htmlspecialchars($input->address); - $nextServer = htmlspecialchars($input->nextServer); - $bootFileName = htmlspecialchars($input->bootFileName); - } catch (Exception $e) { - $response["message"] = $e->getMessage(); - return new JsonResponse(['error' => $response], 400); - } - - try { - $response = $this->curlKeaService->executeCurlCommand('config-get'); - $subnetName = $address . '/' . $this->curlKeaService->convertMaskToCIDR($mask); - $newSubnet = [ - "id" => $subnetId, - "subnet" => $subnetName, - "next-server" => $nextServer, - "boot-file-name" => $bootFileName, - "reservations" => [] - ]; - - if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) { - $response[0]['arguments']['Dhcp4']['subnet4'] = []; - } - - $subnets = $response[0]['arguments']['Dhcp4']['subnet4']; - - // Verificar si el nombre de la subred o el ID ya existe - $subnetNameExists = array_reduce($subnets, function ($exists, $subnetElement) use ($subnetName) { - return $exists || ($subnetElement['subnet'] === $subnetName); - }, false); - - $subnetIdExists = array_reduce($subnets, function ($exists, $subnetElement) use ($subnetId) { - return $exists || ($subnetElement['id'] === $subnetId); - }, false); - if ($subnetNameExists) { - $responseError = "Error: La subred con el nombre '$subnetName' ya existe."; - return new JsonResponse(['error' => $responseError], 400); - } elseif ($subnetIdExists) { - $responseError = "Error: La subred con el ID '$subnetId' ya existe."; - return new JsonResponse(['error' => $responseError], 400); - } else { - $response[0]['arguments']['Dhcp4']['subnet4'][] = $newSubnet; - // Eliminar el campo 'hash' si existe - if (isset($response[0]['arguments']['hash'])) { - unset($response[0]['arguments']['hash']); - } - $array_encoded = json_encode($response[0]['arguments']); - $configurationParsed = str_replace('\\', '', $array_encoded); - $configuration = json_decode($configurationParsed); - $responseTest = $this->curlKeaService->executeCurlCommand('config-test', $configuration); - - if ($responseTest[0]["result"] == 0) { - $responseSet = $this->curlKeaService->executeCurlCommand('config-set', $configuration); - if ($responseSet == false || $responseSet[0]["result"] != 0) { - $responseError = "Error al guardar la configuración en Kea DHCP: " . $responseSet[0]["text"]; - return new JsonResponse(['error' => $responseError], 400); - } else { - $responseWrite = $this->curlKeaService->executeCurlCommand('config-write', $configuration); - if ($responseWrite == false || $responseWrite[0]["result"] != 0) { - $responseError = "Error al guardar la configuración en Kea DHCP: " . $responseWrite[0]["text"]; - return new JsonResponse(['error' => $responseError], 400); - } else { - $responseSuccess = "Configuración cargada correctamente"; - return new JsonResponse(['success' => $responseSuccess], 200); - } - } - } else { - $responseError = "Error kea configuration invalid: " . $responseTest[0]["text"]; - return new JsonResponse(['error' => $responseError], 400); - } - } - } catch (Exception $e) { - $responseError = "Error al obtener la configuración de Kea DHCP: " . $e->getMessage(); - return new JsonResponse(['error' => $responseError], 400); - } +/** + * @OA\Post( + * path="/ogdhcp/v1/subnets", + * summary="Add a new DHCP subnet", + * @OA\RequestBody( + * description="JSON payload to create a new subnet", + * required=true, + * @OA\JsonContent( + * type="object", + * @OA\Property(property="subnetId", type="integer", example=2), + * @OA\Property(property="mask", type="string", example="255.255.255.0"), + * @OA\Property(property="address", type="string", example="192.168.1.0"), + * @OA\Property(property="nextServer", type="string", example="192.168.1.1"), + * @OA\Property(property="bootFileName", type="string", example="pxelinux.0") + * ) + * ), + * @OA\Response( + * response=200, + * description="Subnet added successfully and returns the created subnet", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="success", type="string", example="Subnet added successfully"), + * @OA\Property(property="message", type="object", + * @OA\Property(property="subnetId", type="integer", example=2), + * @OA\Property(property="subnet", type="string", example="192.168.1.0/24"), + * @OA\Property(property="nextServer", type="string", example="192.168.1.1"), + * @OA\Property(property="bootFileName", type="string", example="pxelinux.0"), + * @OA\Property(property="reservations", type="array", @OA\Items(type="object")) + * ) + * ) + * ), + * @OA\Response( + * response=400, + * description="Error occurred while adding the subnet", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="Error: La subred con el nombre 'subnetName' ya existe.") + * ) + * ), + * @OA\Response( + * response=400, + * description="Error occurred while adding the subnet", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="Error: La subred con el ID 'subnetId' ya existe.") + * ) + * ) + * ) + * @Route("/ogdhcp/v1/subnets", methods={"POST"}) + */ +public function addDhcpSubnet(Request $request): JsonResponse +{ + try { + $input = json_decode($request->getContent()); + $subnetId = (int) htmlspecialchars($input->subnetId); + $mask = htmlspecialchars($input->mask); + $address = htmlspecialchars($input->address); + $nextServer = htmlspecialchars($input->nextServer); + $bootFileName = htmlspecialchars($input->bootFileName); + } catch (Exception $e) { + return new JsonResponse(['error' => $e->getMessage()], 400); } - + + try { + $response = $this->curlKeaService->executeCurlCommand('config-get'); + $subnetName = $address . '/' . $this->curlKeaService->convertMaskToCIDR($mask); + $newSubnet = [ + "id" => $subnetId, + "subnet" => $subnetName, + "next-server" => $nextServer, + "boot-file-name" => $bootFileName, + "reservations" => [] + ]; + + if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) { + $response[0]['arguments']['Dhcp4']['subnet4'] = []; + } + + $subnets = $response[0]['arguments']['Dhcp4']['subnet4']; + + // Verificar si el nombre de la subred o el ID ya existe + $subnetNameExists = array_reduce($subnets, function ($exists, $subnetElement) use ($subnetName) { + return $exists || ($subnetElement['subnet'] === $subnetName); + }, false); + + $subnetIdExists = array_reduce($subnets, function ($exists, $subnetElement) use ($subnetId) { + return $exists || ($subnetElement['id'] === $subnetId); + }, false); + + if ($subnetNameExists) { + return new JsonResponse(['error' => "Error: La subred con el nombre '$subnetName' ya existe."], 400); + } elseif ($subnetIdExists) { + return new JsonResponse(['error' => "Error: La subred con el ID '$subnetId' ya existe."], 400); + } else { + $response[0]['arguments']['Dhcp4']['subnet4'][] = $newSubnet; + + // Eliminar el campo 'hash' si existe + if (isset($response[0]['arguments']['hash'])) { + unset($response[0]['arguments']['hash']); + } + + $array_encoded = json_encode($response[0]['arguments']); + $configurationParsed = str_replace('\\', '', $array_encoded); + $configuration = json_decode($configurationParsed); + $responseTest = $this->curlKeaService->executeCurlCommand('config-test', $configuration); + + if ($responseTest[0]["result"] == 0) { + $responseSet = $this->curlKeaService->executeCurlCommand('config-set', $configuration); + if ($responseSet == false || $responseSet[0]["result"] != 0) { + return new JsonResponse(['error' => "Error al guardar la configuración en Kea DHCP: " . $responseSet[0]["text"]], 400); + } else { + $responseWrite = $this->curlKeaService->executeCurlCommand('config-write', $configuration); + if ($responseWrite == false || $responseWrite[0]["result"] != 0) { + return new JsonResponse(['error' => "Error al guardar la configuración en Kea DHCP: " . $responseWrite[0]["text"]], 400); + } else { + // Realizar una nueva consulta a Kea para obtener la subred recién creada + $configGetResponse = $this->curlKeaService->executeCurlCommand('config-get'); + + // Buscar la subred creada + $createdSubnet = null; + foreach ($configGetResponse[0]['arguments']['Dhcp4']['subnet4'] as $subnet) { + if ($subnet['id'] == $subnetId) { + $createdSubnet = $subnet; + break; + } + } + + if ($createdSubnet === null) { + return new JsonResponse(['error' => "Error: No se pudo encontrar la subred creada."], 400); + } + + return new JsonResponse(['success' => "Subred agregada correctamente", 'message' => $createdSubnet], 200); + } + } + } else { + return new JsonResponse(['error' => "Error en la configuración de Kea: " . $responseTest[0]["text"]], 400); + } + } + } catch (Exception $e) { + return new JsonResponse(['error' => "Error al obtener la configuración de Kea DHCP: " . $e->getMessage()], 500); + } +} + /** * @OA\Delete( * path="/ogdhcp/v1/subnets/{subnetId}", @@ -508,7 +518,7 @@ public function getSubnets(): JsonResponse } } catch (Exception $e) { $responseError = "Error al obtener la configuración de Kea DHCP: " . $e->getMessage(); - return new JsonResponse(['error' => $responseError], 400); + return new JsonResponse(['error' => $responseError], 500); } } @@ -1090,6 +1100,140 @@ public function updateDhcpHost(Request $request, $subnetId): JsonResponse } } +/** + * @OA\Get( + * path="/ogdhcp/v1/subnets/backup", + * summary="Get the latest backup configuration of Kea DHCP (only subnet4)", + * @OA\Response( + * response=200, + * description="Latest backup configuration of Kea DHCP", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="success", type="string", example="Backup configuration retrieved successfully"), + * @OA\Property(property="message", type="object", + * @OA\Property(property="subnet4", type="array", + * @OA\Items( + * type="object", + * @OA\Property(property="id", type="integer", example=1), + * @OA\Property(property="subnet", type="string", example="192.168.1.0/24"), + * @OA\Property(property="pools", type="array", + * @OA\Items( + * type="object", + * @OA\Property(property="pool", type="string", example="192.168.1.10-192.168.1.100") + * ) + * ), + * @OA\Property(property="reservations", type="array", + * @OA\Items( + * type="object", + * @OA\Property(property="ip-address", type="string", example="192.168.1.20"), + * @OA\Property(property="hw-address", type="string", example="00:0c:29:6b:5e:71") + * ) + * ) + * ) + * ) + * ) + * ) + * ), + * @OA\Response( + * response=400, + * description="Error occurred while retrieving the backup", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="No se encontraron archivos de backup") + * ) + * ) + * ) + * @Route("/ogdhcp/v1/subnets/backup", methods={"GET"}) + */ +public function getLatestBackupConfiguration(): JsonResponse +{ + $backup_dir = '/opt/ogdhcp/etc/kea/backup'; + try { + // Obtener los archivos de backup + $backup_files = glob($backup_dir . '/*.conf'); + if (empty($backup_files)) { + $responseError = "No se encontraron archivos de backup"; + return new JsonResponse(['error' => $responseError], 400); + } + + // Ordenar por fecha de modificación y obtener el más reciente + usort($backup_files, function ($a, $b) { + return filemtime($b) - filemtime($a); + }); + + // Leer el archivo más reciente + $backup_file = reset($backup_files); + $config = file_get_contents($backup_file); + $configuration = json_decode($config, true); // Decodificar como arreglo + + if (!isset($configuration['Dhcp4']['subnet4'])) { + $responseError = "No se encontró el parámetro 'subnet4' en el backup"; + return new JsonResponse(['error' => $responseError], 400); + } + + // Devolver solo el parámetro 'subnet4' + $responseSuccess = "Backup configuration retrieved successfully"; + return new JsonResponse(['success' => $responseSuccess, 'message' => ['subnet4' => $configuration['Dhcp4']['subnet4']]], 200); + } catch (Exception $e) { + $responseError = "Error al obtener la configuración de backup: " . $e->getMessage(); + return new JsonResponse(['error' => $responseError], 400); + } +} + +/** + * @OA\Post( + * path="/ogdhcp/v1/backup/restore", + * summary="Restore the latest DHCP configuration from backup", + * description="This endpoint restores the last saved DHCP configuration from a backup file and applies it to the Kea DHCP server.", + * @OA\Response( + * response=200, + * description="Configuration restored successfully", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="success", type="string", example="Configuración cargada correctamente") + * ) + * ), + * @OA\Response( + * response=400, + * description="Error occurred during the restore process", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="No se encontraron archivos de backup") + * ) + * ), + * @OA\Response( + * response=400, + * description="Error occurred during Kea configuration validation", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="Error al comprobar la configuración de Kea: unable to forward command to the dhcp4 service") + * ) + * ), + * @OA\Response( + * response=400, + * description="Error en el proceso de restauración", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string"), + * ), + * examples={ + * "errorBackupNotFound": { + * "summary": "No se encontraron archivos de backup", + * "value": { + * "error": "No se encontraron archivos de backup" + * } + * }, + * "errorConfigTest": { + * "summary": "Error en la configuración de Kea", + * "value": { + * "error": "Error al comprobar la configuración de Kea: Error específico" + * } + * } + * } + * ), + * ) + * @Route("/ogdhcp/v1/backup/restore", methods={"POST"}) + */ public function restoreDhcpConfiguration(): JsonResponse {