curlKeaService = $curlKeaService; $this->logger = $logger; $this->backup_dir = $params->get('backup_dir'); } /** * @Route("/ogdhcp/v1/status", name="getDhcpStatus", methods={"GET"}) * @OA\Get( * path="/ogdhcp/v1/status", * summary="Get ogDHCP 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"), * @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")), * @OA\Property(property="services_status", type="object", * @OA\Property(property="dhcp_daemon", type="string"), * @OA\Property(property="nginx", type="string"), * @OA\Property(property="tftpboot", type="string") * ) * ) * ) * ) */ public function getDhcpStatus(): Response { $operation = 'getDhcpStatus'; $component = 'ogdhcp'; try { // Obtener el uso de disco $diskUsageResult = $this->getDiskUsage(); if (!$diskUsageResult) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'params' => [], 'desc' => 'Failed to retrieve disk usage' ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); throw new \Exception('Failed to retrieve disk usage'); } // Obtener el estado de los servicios de ogDHCP $servicesStatusResult = $this->getServicesStatus(); if (!$servicesStatusResult) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'params' => [], 'desc' => 'Failed to retrieve services status' ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); throw new \Exception('Failed to retrieve services status'); } // Intentar obtener las subredes, pero si falla devolver un array vacío $subnetsResult = []; try { $subnetsResult = $this->getSubnetsService(); } catch (\Exception $e) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'params' => [], 'desc' => 'Failed to retrieve subnets' ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); $subnetsResult = []; } // Componer la respuesta $response = [ 'disk_usage' => $diskUsageResult, 'subnets' => $subnetsResult, 'services_status' => $servicesStatusResult ]; $this->logger->info(json_encode([ 'severity' => 'INFO', 'operation' => $operation, 'component' => $component, 'params' => [], 'desc' => 'DHCP status retrieved successfully' ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['success' => "Información obtenida con éxito", 'message' => $response], Response::HTTP_OK); } catch (\Exception $e) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'params' => [], 'desc' => $e->getMessage() ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); // Capturar la excepción y devolver el mensaje de error return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); } } private function getDiskUsage(): array { // Simular la salida del comando df para obtener el uso del disco $output = shell_exec("df -h " . realpath($this->backup_dir) . " | tail -1 | awk '{print $2, $3, $4, $5}'"); if (!$output) { $this->logger->error("Failed to execute disk usage command"); return []; } list($total, $used, $available, $percentage) = explode(' ', $output); return [ 'total' => trim($total), 'used' => trim($used), 'available' => trim($available), 'percentage' => trim($percentage), ]; } private function getServicesStatus(): array { $services = [ 'kea-dhcp4-server' => $this->getServiceStatus('kea-dhcp4-server.service'), 'kea-ctrl-agent' => $this->getServiceStatus('kea-ctrl-agent.service'), 'nginx' => $this->getServiceStatus('nginx.service') ]; return $services; } private function getServiceStatus(string $service): string { // Ejecutar el comando systemctl para verificar el estado del servicio $output = shell_exec("systemctl is-active " . escapeshellarg($service)); // Si el comando retorna "active", el servicio está corriendo if (trim($output) === 'active') { return 'active'; } else { return 'inactive'; } } private function getSubnetsService(): ?array { try { // Ejecutar el comando para obtener la configuración $response = $this->curlKeaService->executeCurlCommand('config-get'); // Verificar si la respuesta es nula if (!$response) { throw new \Exception('Error: No se pudo acceder a la configuración de Kea. Respuesta nula.'); } // Verificar el código de resultado $result_code = $response[0]["result"]; if ($result_code != 0) { throw new \Exception("Error en la configuración de Kea: " . $response[0]["text"]); } // Verificar si el campo 'subnet4' está presente en la respuesta if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) { throw new \Exception("El campo 'subnet4' no está inicializado. Respuesta: " . json_encode($response[0]['arguments'])); } // Subredes encontradas, devolver resultado return $response[0]['arguments']['Dhcp4']['subnet4']; } catch (\Exception $e) { // Escalar la excepción para que sea gestionada por la función que llama throw new \Exception("Error al obtener las subredes: " . $e->getMessage()); } } /** * @OA\Schema( * schema="Subnet", * type="object", * @OA\Property(property="id", type="integer", description="The ID of the subnet"), * @OA\Property(property="subnet", type="string", description="The name of the subnet"), * @OA\Property(property="next-server", type="string", description="The next server in the subnet"), * @OA\Property(property="boot-file-name", type="string", description="The boot file name for the subnet"), * @OA\Property(property="DNS", type="string", description="DNS subnet"), * @OA\Property(property="subnetName", type="string", description="subnetName subnet"), * @OA\Property( * property="reservations", * type="array", * @OA\Items(type="object", description="The reservations in the subnet") * ) * ) */ /** * @OA\Get( * path="/ogdhcp/v1/subnets", * summary="Obtiene todas las subredes DHCP", * @OA\Response( * response=200, * description="Devuelve todas las subredes", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="Subredes obtenidas correctamente"), * @OA\Property(property="message", 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="next-server", type="string", example="192.168.1.1"), * @OA\Property(property="DNS", type="string", example="192.168.1.1"), * @OA\Property(property="boot-file-name", type="string", example="pxelinux.0"), * @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.11"), * @OA\Property(property="hw-address", type="string", example="00:0c:29:6b:5e:71"), * @OA\Property(property="hostname", type="string", example="PC11") * ) * ) * ) * ) * ) * ), * @OA\Response( * response=400, * description="No subnets found", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="El campo subnet4 no está inicializado") * ) * ), * @OA\Response( * response=500, * description="Error occurred in kea configuration", * @OA\JsonContent( * oneOf={ * @OA\Schema( * @OA\Property(property="error", type="string", example="No se pudo acceder al archivo de configuración de Kea") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error: La subred con el ID 'subnetId' ya existe.") * ) * } * ), * @OA\Examples( * example="noResponse", * summary="No response from kea configuration", * value={"error": "No se pudo acceder al archivo de configuración de Kea"} * ), * @OA\Examples( * example="keaError", * summary="Kea agent down", * value={"error": "Error al obtener la configuración de Kea DHCP"} * ) * ), * ) * @Route("/ogdhcp/v1/subnets", methods={"GET"}) */ public function getSubnets(): JsonResponse { $operation = 'getSubnets'; $component = 'ogdhcp'; try { $this->logger->info(json_encode([ 'severity' => 'INFO', 'operation' => $operation, 'component' => $component, 'params' => [], 'desc' => 'Iniciando la obtención de subredes.' ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); $response = $this->curlKeaService->executeCurlCommand('config-get'); if (!$response) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'desc' => 'No se pudo acceder al archivo de configuración de Kea.' ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => 'No se pudo acceder al archivo de configuración de Kea'], Response::HTTP_INTERNAL_SERVER_ERROR); } $result_code = $response[0]["result"]; if ($result_code == 0) { if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => 'El campo "subnet4" no está inicializado.' ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => 'El campo "subnet4" no está inicializado'], Response::HTTP_BAD_REQUEST); } else { $subnets = $response[0]['arguments']['Dhcp4']['subnet4']; $this->logger->info(json_encode([ 'severity' => 'INFO', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_OK, 'params' => ['subnets_count' => count($subnets)], 'desc' => 'Subredes obtenidas correctamente.' ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse([ 'success' => 'Subredes obtenidas correctamente', 'message' => $subnets ], Response::HTTP_OK); } } else { $errorText = $response[0]["text"] ?? 'Unknown error'; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'desc' => "Error al obtener la configuración de Kea DHCP: " . $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => "Error al obtener la configuración de Kea DHCP: " . $errorText], Response::HTTP_INTERNAL_SERVER_ERROR); } } catch (Exception $e) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'desc' => "Excepción al obtener la configuración de Kea DHCP: " . $e->getMessage() ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => "Error al obtener la configuración de Kea DHCP: " . $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); } } /** * @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="subnetName", type="string", example="Aula informatica"), * @OA\Property(property="DNS", type="string", example="192.168.1.1"), * @OA\Property(property="bootFileName", type="string", example="pxelinux.0"), * @OA\Property(property="router", type="string", example="192.168.1.254") * ) * ), * @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", ref="#/components/schemas/Subnet") * ) * ), * @OA\Response( * response=400, * description="Error occurred while adding the subnet", * @OA\JsonContent( * oneOf={ * @OA\Schema( * @OA\Property(property="error", type="string", example="La subred con el nombre 'subnetName' ya existe.") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="La subred con el ID 'subnetId' ya existe.") * ) * } * ), * @OA\Examples( * example="parameterMissing", * summary="Missing required parameter", * value={"error": "Falta un parámetro requerido: $paramFaltante"} * ), * @OA\Examples( * example="nameConflict", * summary="Subnet name already exists", * value={"error": "La subred con la ip 'subnetName' ya existe."} * ), * @OA\Examples( * example="idConflict", * summary="Subnet ID already exists", * value={"error": "La subred con el ID 'subnetId' ya existe."} * ), * @OA\Examples( * example="subnetCreatedNotFound", * summary="Subnet created not found", * value={"error": "No se pudo encontrar la subred creada"} * ) * ), * @OA\Response( * response=500, * description="Server error", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="Error al guardar la configuración en Kea DHCP.") * ) * ) * ) * @Route("/ogdhcp/v1/subnets", methods={"POST"}) */ public function addDhcpSubnet(Request $request): JsonResponse { $operation = 'addDhcpSubnet'; $component = 'ogdhcp'; try { $input = json_decode($request->getContent()); $subnetId = (int) htmlspecialchars($input->subnetId); $mask = htmlspecialchars($input->mask); $address = htmlspecialchars($input->address); // Verificar si next-server, boot-file-name y router están presentes $nextServer = isset($input->nextServer) ? htmlspecialchars($input->nextServer) : null; $bootFileName = isset($input->bootFileName) ? htmlspecialchars($input->bootFileName) : null; $router = isset($input->router) ? htmlspecialchars($input->router) : null; $DNS = isset($input->DNS) ? htmlspecialchars($input->DNS) : null; $subnetName = isset($input->subnetName) ? htmlspecialchars($input->subnetName) : null; // Calcular el gatewayIP si no se proporciona el router $gatewayIP = $router ?: substr($address, 0, strrpos($address, '.')) . '.1'; } catch (Exception $e) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => 'Error al procesar los datos de entrada para agregar una subred: ' . $e->getMessage() ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); if (strpos($e->getMessage(), 'Undefined property') !== false) { preg_match('/Undefined property: stdClass::\$(\w+)/', $e->getMessage(), $matches); $paramFaltante = $matches[1] ?? 'desconocido'; return new JsonResponse(['error' => "Falta un parámetro requerido: $paramFaltante"], Response::HTTP_BAD_REQUEST); } return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_BAD_REQUEST); } try { $response = $this->curlKeaService->executeCurlCommand('config-get'); $subnetCIDR = $address . '/' . $this->curlKeaService->convertMaskToCIDR($mask); $newSubnet = [ "id" => $subnetId, "subnet" => $subnetCIDR, "reservations" => [], "option-data" => [ [ "name" => "routers", "code" => 3, "data" => $gatewayIP ] ] ]; // Agregar "user-context" solo si $subnetName tiene valor if (!empty($subnetName)) { $newSubnet["user-context"] = [ "subnetName" => $subnetName ]; } if (!empty($DNS)) { $newSubnet["option-data"][] = [ "name" => "domain-name-servers", "code" => 6, "space" => "dhcp4", "csv-format" => true, "data" => $DNS ]; } if ($nextServer) { $newSubnet["next-server"] = $nextServer; } if ($bootFileName) { $newSubnet["boot-file-name"] = $bootFileName; } if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) { $response[0]['arguments']['Dhcp4']['subnet4'] = []; } $subnets = $response[0]['arguments']['Dhcp4']['subnet4']; $subnetNameExists = array_reduce($subnets, fn($exists, $subnetElement) => $exists || ($subnetElement['subnet'] === $subnetCIDR), false); $subnetIdExists = array_reduce($subnets, fn($exists, $subnetElement) => $exists || ($subnetElement['id'] === $subnetId), false); if ($subnetNameExists) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => "La subred con la IP '$subnetCIDR' ya existe." ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => "La subred con la ip '$subnetCIDR' ya existe."], Response::HTTP_BAD_REQUEST); } elseif ($subnetIdExists) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => "La subred con el ID '$subnetId' ya existe." ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => "La subred con el ID '$subnetId' ya existe."], Response::HTTP_BAD_REQUEST); } else { $response[0]['arguments']['Dhcp4']['subnet4'][] = $newSubnet; 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 || $responseSet[0]["result"] != 0) { $errorText = $responseSet[0]["text"] ?? 'Unknown error'; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => "Error al guardar la configuración en Kea DHCP: $errorText" ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => "Error al guardar la configuración en Kea DHCP: $errorText"], Response::HTTP_BAD_REQUEST); } else { $responseWrite = $this->curlKeaService->executeCurlCommand('config-write', $configuration); if (!$responseWrite || $responseWrite[0]["result"] != 0) { $errorText = $responseWrite[0]["text"] ?? 'Unknown error'; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => "Error al escribir la configuración en Kea DHCP: $errorText" ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => "Error al escribir la configuración en Kea DHCP: $errorText"], Response::HTTP_BAD_REQUEST); } else { $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) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => "No se pudo encontrar la subred creada." ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => "No se pudo encontrar la subred creada"], Response::HTTP_BAD_REQUEST); } $this->logger->info(json_encode([ 'severity' => 'INFO', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_OK, 'desc' => 'Subred agregada correctamente.', 'params' => ['subnet' => $createdSubnet] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['success' => "Subred agregada correctamente", 'message' => $createdSubnet], Response::HTTP_OK); } } } else { $errorText = $responseTest[0]["text"] ?? 'Unknown error'; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => "Error en la configuración de Kea: $errorText" ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => "Error en la configuración de Kea: $errorText"], Response::HTTP_BAD_REQUEST); } } } catch (Exception $e) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'desc' => "Excepción al obtener la configuración de Kea DHCP: " . $e->getMessage() ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => "Error al obtener la configuración de Kea DHCP: " . $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); } } /** * @OA\Delete( * path="/ogdhcp/v1/subnets/{subnetId}", * summary="Delete a DHCP subnet", * @OA\Parameter( * name="subnetId", * in="path", * description="ID of the subnet to delete", * required=true, * @OA\Schema(type="integer") * ), * @OA\Response( * response=200, * description="Subnet deleted successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="Subred eliminada correctamente") * ) * ), * @OA\Response( * response=400, * description="Error occurred while deleting the subnet", * @OA\JsonContent( * oneOf={ * @OA\Schema( * @OA\Property(property="error", type="string", example="No hay subredes definidas") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error configuracion de kea invalido") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error al guardar la configuración en Kea DHCP") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error al escribir en la configuración en Kea DHCP") * ) * } * ), * @OA\Examples( * example="noSubnets", * summary="Subnet name already exists", * value={"error": "No hay subredes definidas"} * ), * @OA\Examples( * example="invalidKea", * summary="Error in config-test in kea", * value={"error": "Error configuracion de kea invalido"} * ), * @OA\Examples( * example="saveKea", * summary="Error in config-set in kea", * value={"error": "Error al guardar la configuración en Kea DHCP"} * ), * @OA\Examples( * example="writeKea", * summary="Error in config-write in kea", * value={"error": "Error al escribir en la configuración en Kea DHCP"} * ) * ), * @OA\Response( * response=404, * description="Subnet not found", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="La subred con el id '2' no existe") * ) * ), * @OA\Response( * response=500, * description="Error saving configuration in Kea DHCP", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="Error al obtener la configuración de Kea DHCP") * ) * ) * ) * @Route("/ogdhcp/v1/subnets/{subnetId}", methods={"DELETE"}) */ public function deleteDhcpSubnet(Request $request): JsonResponse { $operation = 'deleteDhcpSubnet'; $component = 'ogdhcp'; $subnetId = (int) $request->get('subnetId'); try { $response = $this->curlKeaService->executeCurlCommand('config-get'); if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) { $responseError = "No hay subredes definidas"; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $responseError ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $responseError], Response::HTTP_BAD_REQUEST); } $subnetIndex = array_search($subnetId, array_column($response[0]['arguments']['Dhcp4']['subnet4'], 'id')); if ($subnetIndex === false) { $responseError = "La subred con el id '$subnetId' no existe"; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_NOT_FOUND, 'desc' => $responseError, 'params' => ['subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $responseError], Response::HTTP_NOT_FOUND); } else { unset($response[0]['arguments']['Dhcp4']['subnet4'][$subnetIndex]); if (isset($response[0]['arguments']['hash'])) { unset($response[0]['arguments']['hash']); } $response[0]['arguments']['Dhcp4']['subnet4'] = array_values($response[0]['arguments']['Dhcp4']['subnet4']); $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"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $responseError ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $responseError], Response::HTTP_BAD_REQUEST); } else { $responseWrite = $this->curlKeaService->executeCurlCommand('config-write', $configuration); if ($responseWrite == false || $responseWrite[0]["result"] != 0) { $responseError = "Error al escribir en la configuración en Kea DHCP: " . $responseWrite[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $responseError ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $responseError], Response::HTTP_BAD_REQUEST); } else { $responseSuccess = "Subred eliminada correctamente"; $this->logger->info(json_encode([ 'severity' => 'INFO', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_OK, 'desc' => $responseSuccess, 'params' => ['subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['success' => $responseSuccess], Response::HTTP_OK); } } } else { $responseError = "Error configuración de Kea inválido: " . $responseTest[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $responseError ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $responseError], Response::HTTP_BAD_REQUEST); } } } catch (Exception $e) { $responseError = "Error al obtener la configuración de Kea DHCP: " . $e->getMessage(); $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'desc' => $responseError ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $responseError], Response::HTTP_INTERNAL_SERVER_ERROR); } } /** * @Route("/ogdhcp/v1/subnets/{subnetId}", methods={"PUT"}) * @OA\Put( * path="/ogdhcp/v1/subnets/{subnetId}", * summary="Modify a DHCP subnet", * @OA\Parameter( * name="subnetId", * in="path", * description="ID of the subnet to modify", * required=true, * @OA\Schema(type="integer") * ), * @OA\RequestBody( * description="Data to modify the subnet", * required=true, * @OA\JsonContent( * type="object", * @OA\Property(property="mask", type="string"), * @OA\Property(property="address", type="string"), * @OA\Property(property="DNS", type="string"), * @OA\Property(property="subnetName", type="string"), * @OA\Property(property="router", type="string", example="192.168.1.254"), * @OA\Property(property="nextServer", type="string"), * @OA\Property(property="bootFileName", type="string") * ) * ), * @OA\Response( * response=200, * description="Subnet modified successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string") * ) * ), * @OA\Response( * response=400, * description="Error occurred while modifying the subnet", * @OA\JsonContent( * oneOf={ * @OA\Schema(@OA\Property(property="error", type="string", example="Falta un parámetro requerido: $paramFaltante")), * @OA\Schema(@OA\Property(property="error", type="string", example="No hay subredes definidas")), * @OA\Schema(@OA\Property(property="error", type="string", example="Error configuración de Kea inválido")), * @OA\Schema(@OA\Property(property="error", type="string", example="Error al guardar la configuración en Kea DHCP")), * @OA\Schema(@OA\Property(property="error", type="string", example="Error al escribir en la configuración en Kea DHCP")) * } * ) * ), * @OA\Response( * response=404, * description="Subnet not found", * @OA\JsonContent(type="object", @OA\Property(property="error", type="string", example="La subred con el id '2' no existe")) * ), * @OA\Response( * response=500, * description="Error saving configuration in Kea DHCP", * @OA\JsonContent(type="object", @OA\Property(property="error", type="string", example="Error al obtener la configuración de Kea DHCP")) * ) * ) */ public function modifyDhcpSubnet(Request $request): JsonResponse { $subnetId = (int) $request->get('subnetId'); $operation = 'modifyDhcpSubnet'; $component = 'ogdhcp'; try { $input = json_decode($request->getContent()); $mask = htmlspecialchars($input->mask); $address = htmlspecialchars($input->address); // Verificar si next-server, boot-file-name y router están presentes $nextServer = isset($input->nextServer) ? htmlspecialchars($input->nextServer) : null; $bootFileName = isset($input->bootFileName) ? htmlspecialchars($input->bootFileName) : null; $router = isset($input->router) ? htmlspecialchars($input->router) : null; $DNS = isset($input->DNS) ? htmlspecialchars($input->DNS) : null; $subnetName = isset($input->subnetName) ? htmlspecialchars($input->subnetName) : null; // Calcular el gateway añadiendo .1 al final de la subred si no se proporciona el router $gateway = $router ?: preg_replace('/\d+$/', '1', $address); } catch (Exception $e) { if (strpos($e->getMessage(), 'Undefined property') !== false) { preg_match('/Undefined property: stdClass::\$(\w+)/', $e->getMessage(), $matches); $paramFaltante = $matches[1] ?? 'desconocido'; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => "Falta un parámetro requerido: $paramFaltante" ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => "Falta un parámetro requerido: $paramFaltante"], Response::HTTP_BAD_REQUEST); } $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $e->getMessage() ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_BAD_REQUEST); } try { $response = $this->curlKeaService->executeCurlCommand('config-get'); $subnetCIDR = $address . '/' . $this->curlKeaService->convertMaskToCIDR($mask); if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) { $errorText = "No hay subredes definidas"; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } $subnetIndex = array_search($subnetId, array_column($response[0]['arguments']['Dhcp4']['subnet4'], 'id')); if ($subnetIndex === false) { $errorText = "La subred con el id '$subnetId' no existe"; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_NOT_FOUND, 'desc' => $errorText, 'params' => ['subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_NOT_FOUND); } else { // Obtener la subred actual para mantener las reservas existentes $existingSubnet = $response[0]['arguments']['Dhcp4']['subnet4'][$subnetIndex]; // Modificar la subred existente $newSubnet = [ "id" => $subnetId, "subnet" => $subnetCIDR, "reservations" => $existingSubnet['reservations'], // Mantener las reservas existentes "option-data" => [ [ "name" => "routers", "code" => 3, "data" => $gateway ] ] ]; // Agregar "user-context" solo si $subnetName tiene valor if (!empty($subnetName)) { $newSubnet["user-context"] = [ "subnetName" => $subnetName ]; } // Agregar DNS si está presente if (!empty($DNS)) { $newSubnet["option-data"][] = [ "name" => "domain-name-servers", "code" => 23, "space" => "dhcp4", "csv-format" => true, "data" => $DNS ]; } // Añadir next-server si está presente, o eliminarlo de las reservas si no está if ($nextServer) { $newSubnet["next-server"] = $nextServer; } else { foreach ($newSubnet['reservations'] as &$reservation) { unset($reservation['next-server']); } } // Añadir boot-file-name si está presente, o eliminarlo de las reservas si no está if ($bootFileName) { $newSubnet["boot-file-name"] = $bootFileName; } else { foreach ($newSubnet['reservations'] as &$reservation) { unset($reservation['boot-file-name']); } } // Actualizar las reservas con los nuevos valores de next-server y boot-file-name foreach ($newSubnet['reservations'] as &$reservation) { if ($nextServer) { $reservation['next-server'] = $nextServer; } if ($bootFileName) { $reservation['boot-file-name'] = $bootFileName; } } // Reemplazar la subred en la configuración $response[0]['arguments']['Dhcp4']['subnet4'][$subnetIndex] = $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) { $errorText = "Error al guardar la configuración en Kea DHCP: " . $responseSet[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } else { $responseWrite = $this->curlKeaService->executeCurlCommand('config-write', $configuration); if ($responseWrite == false || $responseWrite[0]["result"] != 0) { return new JsonResponse(['error' => "Error al escribir en la configuración en Kea DHCP: " . $responseWrite[0]["text"]], 400); } else { // Volvemos a consultar la configuración para devolver la subred modificada $updatedResponse = $this->curlKeaService->executeCurlCommand('config-get'); $updatedSubnet = array_filter($updatedResponse[0]['arguments']['Dhcp4']['subnet4'], function ($subnet) use ($subnetId) { return $subnet['id'] == $subnetId; }); $responseSuccess = "Subred modificada correctamente"; $this->logger->info(json_encode([ 'severity' => 'INFO', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_OK, 'desc' => $responseSuccess, 'params' => ['subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['success' => $responseSuccess], Response::HTTP_OK); } } } else { $errorText = "Error configuración de Kea inválida: " . $responseTest[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } } } catch (Exception $e) { $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'desc' => $e->getMessage() ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => "Error al obtener la configuración de Kea DHCP: " . $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); } } /** * @OA\Get( * path="/ogdhcp/v1/subnets/{subnetId}/hosts", * summary="Get all hosts in a subnet", * @OA\Parameter( * name="subnetId", * in="path", * description="The ID of the subnet", * required=true, * @OA\Schema(type="integer") * ), * @OA\Response( * response=200, * description="List of hosts in the subnet", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="Hosts obtenidos correctamente"), * @OA\Property(property="message", type="array", * @OA\Items(ref="#/components/schemas/Host") * ) * ) * ), * @OA\Response( * response=400, * description="Error occurred", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="Error: La subred con el id 'subnetId' no existe.") * ) * ), * @OA\Response( * response=500, * description="Server error when loading Kea DHCP configuration", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="Error al obtener la configuración de Kea DHCP") * ) * ) * ) * @Route("/ogdhcp/v1/subnets/{subnetId}/hosts", methods={"GET"}) */ public function getHosts($subnetId): JsonResponse { $operation = 'getHosts'; $component = 'ogdhcp'; try { $response = $this->curlKeaService->executeCurlCommand('config-get'); if (!$response) { $responseError = "No hay subredes definidas"; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $responseError ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $responseError], Response::HTTP_BAD_REQUEST); } $result_code = $response[0]["result"]; if ($result_code == 0) { if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) { $errorText = "El campo 'subnet4' no está inicializado."; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); throw new \Exception($errorText); } $subnets = $response[0]['arguments']['Dhcp4']['subnet4']; foreach ($subnets as $subnet) { if ($subnet['id'] == $subnetId) { $this->logger->info(json_encode([ 'severity' => 'INFO', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_OK, 'desc' => 'Hosts obtenidos correctamente.', 'params' => ['subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse([ 'success' => 'Hosts obtenidos correctamente', 'message' => $subnet['reservations'] ], Response::HTTP_OK); } } $errorText = "La subred con el id '$subnetId' no existe."; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText, 'params' => ['subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } else { $responseError = "No hay subredes definidas"; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $responseError ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $responseError], Response::HTTP_BAD_REQUEST); } } catch (\Exception $e) { $errorText = "Error al obtener la configuración de Kea DHCP: " . $e->getMessage(); $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_INTERNAL_SERVER_ERROR); } } /** * @OA\Post( * path="/ogdhcp/v1/subnets/{subnetId}/hosts", * summary="Add a DHCP host to a subnet", * @OA\Parameter( * name="subnetId", * in="path", * description="ID of the subnet to add the host to", * required=true, * @OA\Schema( * type="integer" * ) * ), * @OA\RequestBody( * description="Data for the new host", * required=true, * @OA\JsonContent( * type="object", * @OA\Property(property="host", type="string", example="pc11"), * @OA\Property(property="macAddress", type="string", example="56:6f:c7:4f:00:4f"), * @OA\Property(property="address", type="string", example="172.30.4.11") * ) * ), * @OA\Response( * response=200, * description="Host added successfully and returns the new host details", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="Host added successfully"), * @OA\Property(property="message", ref="#/components/schemas/Host") * ) * ), * @OA\Response( * response=400, * description="Error occurred while adding the host", * @OA\JsonContent( * oneOf={ * @OA\Schema( * @OA\Property(property="error", type="string", example="No hay subredes definidas") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="El host con el hostname 'host' ya existe en las reservaciones.") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error configuracion de kea invalido") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error al guardar la configuración en Kea DHCP") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error al escribir en la configuración en Kea DHCP") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="No se pudo encontrar el host recién creado") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Falta un parámetro requerido: $paramFaltante") * ) * } * ), * @OA\Examples( * example="parameterMissing", * summary="Missing required parameter", * value={"error": "Falta un parámetro requerido: $paramFaltante"} * ), * @OA\Examples( * example="noSubnet", * summary="No subnets initialized", * value={"error": "No se encontró la subnet con id '$subnetId'"} * ), * @OA\Examples( * example="hostExists", * summary="Host already exists", * value={"error": "El host con el hostname '$host' ya existe en las reservaciones."} * ), * @OA\Examples( * example="invalidKea", * summary="Error in config-test in kea", * value={"error": "Error configuracion de kea invalido"} * ), * @OA\Examples( * example="saveKea", * summary="Error in config-set in kea", * value={"error": "Error al guardar la configuración en Kea DHCP"} * ), * @OA\Examples( * example="writeKea", * summary="Error in config-write in kea", * value={"error": "Error al escribir en la configuración en Kea DHCP"} * ), * @OA\Examples( * example="notFound", * summary="Host created not found", * value={"error": "No se pudo encontrar el host recién creado"} * ) * ) * ) * @Route("/ogdhcp/v1/subnets/{subnetId}/hosts", methods={"POST"}) */ public function addDhcpHost(Request $request, $subnetId): JsonResponse { $operation = 'addDhcpHost'; $component = 'ogdhcp'; try { $input = json_decode($request->getContent()); $host = htmlspecialchars($input->host); $macAddress = htmlspecialchars($input->macAddress); $address = htmlspecialchars($input->address); } catch (Exception $e) { $errorText = $e->getMessage(); $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); if (strpos($errorText, 'Undefined property') !== false) { preg_match('/Undefined property: stdClass::\$(\w+)/', $errorText, $matches); $paramFaltante = $matches[1] ?? 'desconocido'; return new JsonResponse(['error' => "Falta un parámetro requerido: $paramFaltante"], Response::HTTP_BAD_REQUEST); } return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } try { $response = $this->curlKeaService->executeCurlCommand('config-get'); $newHost = [ "hostname" => $host, "hw-address" => $macAddress, "ip-address" => $address ]; $subnetFound = false; foreach ($response[0]['arguments']['Dhcp4']['subnet4'] as &$subnet) { if ($subnet['id'] == $subnetId) { $subnetFound = true; if (isset($subnet['boot-file-name'])) { $newHost['boot-file-name'] = $subnet['boot-file-name']; } if (isset($subnet['next-server'])) { $newHost['next-server'] = $subnet['next-server']; } if (!isset($subnet['reservations'])) { $subnet['reservations'] = []; } $exists = array_reduce($subnet['reservations'], function ($exists, $reservation) use ($host) { return $exists || ($reservation['hostname'] === $host); }); if ($exists) { $errorText = "El host con el hostname '$host' ya existe en las reservaciones."; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText, 'params' => ['host' => $host, 'subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } else { $subnet['reservations'][] = $newHost; break; } } } if (!$subnetFound) { $errorText = "No se encontró la subnet con id '$subnetId'."; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText, 'params' => ['subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } 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) { $errorText = "Error al guardar la configuración en Kea DHCP: " . $responseSet[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } else { $responseWrite = $this->curlKeaService->executeCurlCommand('config-write', $configuration); if ($responseWrite == false || $responseWrite[0]["result"] != 0) { $errorText = "Error al escribir en la configuración en Kea DHCP: " . $responseWrite[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } else { $updatedResponse = $this->curlKeaService->executeCurlCommand('config-get'); $createdHost = null; foreach ($updatedResponse[0]['arguments']['Dhcp4']['subnet4'] as $subnet) { if ($subnet['id'] == $subnetId) { foreach ($subnet['reservations'] as $reservation) { if ($reservation['hw-address'] === $macAddress) { $createdHost = $reservation; break; } } } } if ($createdHost) { $this->logger->info(json_encode([ 'severity' => 'INFO', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_OK, 'desc' => 'Host agregado correctamente.', 'params' => ['host' => $host, 'subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['success' => "Host agregado correctamente", 'message' => $createdHost], Response::HTTP_OK); } else { $errorText = "No se pudo encontrar el host recién creado."; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText, 'params' => ['host' => $host, 'subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } } } } else { $errorText = "Error configuración de Kea inválida: " . $responseTest[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } } catch (Exception $e) { $errorText = "Error al obtener la configuración de Kea DHCP: " . $e->getMessage(); $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_INTERNAL_SERVER_ERROR); } } /** * @OA\Delete( * path="/ogdhcp/v1/subnets/{subnetId}/hosts", * summary="Delete a DHCP host from a specific subnet", * @OA\Parameter( * name="subnetId", * in="path", * description="The ID of the subnet", * required=true, * @OA\Schema(type="integer") * ), * @OA\RequestBody( * description="Data for the host to delete", * required=true, * @OA\JsonContent( * type="object", * @OA\Property(property="macAddress", type="string", example="56:6f:c7:4f:00:4f") * ) * ), * @OA\Response( * response=200, * description="Host deleted successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string") * ) * ), * @OA\Response( * response=404, * description="Host or subnet not found", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string") * ) * ), * @OA\Response( * response=400, * description="Error occurred while adding the host", * @OA\JsonContent( * oneOf={ * @OA\Schema( * @OA\Property(property="error", type="string", example="No se encontró la subnet con id '$subnetId'") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="El host con la MAC '$mac' no existe en las reservaciones") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error configuracion de kea invalido") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error al guardar la configuración en Kea DHCP") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error al escribir en la configuración en Kea DHCP") * ) * } * ), * @OA\Examples( * example="noSubnet", * summary="No subnets initialized", * value={"error": "No se encontró la subnet con id '$subnetId'"} * ), * @OA\Examples( * example="hostNotFound", * summary="Host not found", * value={"error": "El host con la MAC '$mac' no existe en las reservaciones"} * ), * @OA\Examples( * example="invalidKea", * summary="Error in config-test in kea", * value={"error": "Error configuracion de kea invalido"} * ), * @OA\Examples( * example="saveKea", * summary="Error in config-set in kea", * value={"error": "Error al guardar la configuración en Kea DHCP"} * ), * @OA\Examples( * example="writeKea", * summary="Error in config-write in kea", * value={"error": "Error al escribir en la configuración en Kea DHCP"} * ) * ) * ) * @Route("/ogdhcp/v1/subnets/{subnetId}/hosts", methods={"DELETE"}) */ public function deleteDhcpHost(Request $request, $subnetId): JsonResponse { $operation = 'deleteDhcpHost'; $component = 'ogdhcp'; try { $input = json_decode($request->getContent()); $macAddress = htmlspecialchars($input->macAddress); } catch (Exception $e) { $errorText = $e->getMessage(); $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } try { $response = $this->curlKeaService->executeCurlCommand('config-get'); $subnetFound = false; $hostFound = false; foreach ($response[0]['arguments']['Dhcp4']['subnet4'] as &$subnet) { if ($subnet['id'] == $subnetId) { $subnetFound = true; if (!isset($subnet['reservations'])) { $subnet['reservations'] = []; } foreach ($subnet['reservations'] as $key => $reservation) { if ($reservation['hw-address'] === $macAddress) { unset($subnet['reservations'][$key]); $subnet['reservations'] = array_values($subnet['reservations']); $hostFound = true; break; } } } } if (!$subnetFound) { $errorText = "No se encontró la subnet con id '$subnetId'"; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText, 'params' => ['subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } if (!$hostFound) { $errorText = "El host con la MAC '$macAddress' no existe en las reservaciones"; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText, 'params' => ['mac_address' => $macAddress, 'subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } 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) { $errorText = "Error al guardar la configuración en Kea DHCP: " . $responseSet[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } else { $responseWrite = $this->curlKeaService->executeCurlCommand('config-write', $configuration); if ($responseWrite == false || $responseWrite[0]["result"] != 0) { $errorText = "Error al escribir en la configuración en Kea DHCP: " . $responseWrite[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } else { $this->logger->info(json_encode([ 'severity' => 'INFO', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_OK, 'desc' => 'Host eliminado correctamente.', 'params' => ['mac_address' => $macAddress, 'subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['success' => "Host eliminado correctamente"], Response::HTTP_OK); } } } else { $errorText = "Error configuración de Kea inválida: " . $responseTest[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } } catch (Exception $e) { $errorText = "Error al obtener la configuración de Kea DHCP: " . $e->getMessage(); $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_INTERNAL_SERVER_ERROR); } } /** * @OA\Put( * path="/ogdhcp/v1/subnets/{subnetId}/hosts", * summary="Update a DHCP host", * @OA\Parameter( * name="subnetId", * in="path", * description="The ID of the subnet", * required=true, * @OA\Schema(type="integer") * ), * @OA\RequestBody( * description="Data for the host to update", * required=true, * @OA\JsonContent( * type="object", * @OA\Property(property="oldMacAddress", type="string", example="56:6f:c7:4f:00:4f", description="Old MAC address of the host to update"), * @OA\Property(property="macAddress", type="string", example="56:6f:c7:4f:01:01", description="New MAC address"), * @OA\Property(property="address", type="string", example="192.168.1.11", description="New IP address"), * @OA\Property(property="hostname", type="string", example="PC11", description="New or existing hostname") * ) * ), * @OA\Response( * response=200, * description="Host updated successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="Host updated successfully"), * @OA\Property(property="message", ref="#/components/schemas/Host") * ) * ), * @OA\Response( * response=400, * description="Error occurred while updating the host", * @OA\JsonContent( * oneOf={ * @OA\Schema( * @OA\Property(property="error", type="string", example="No se encontró la subnet con id '$subnetId'") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="El host con la MAC '$oldMacAddress' no existe en las reservaciones") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error configuración de kea inválida") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error al guardar la configuración en Kea DHCP") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error al escribir la configuración en Kea DHCP") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Falta un parámetro requerido: $paramFaltante") * ) * } * ) * ), * @OA\Response( * response=500, * description="Server error", * @OA\JsonContent( * type="object", * @OA\Property(property="error", type="string", example="Internal server error") * ) * ) * ) * @Route("/ogdhcp/v1/subnets/{subnetId}/hosts", methods={"PUT"}) */ public function updateDhcpHost(Request $request, $subnetId): JsonResponse { $operation = 'updateDhcpHost'; $component = 'ogdhcp'; try { $input = json_decode($request->getContent()); $host = htmlspecialchars($input->hostname); $oldMacAddress = htmlspecialchars($input->oldMacAddress); $macAddress = htmlspecialchars($input->macAddress); $address = htmlspecialchars($input->address); } catch (Exception $e) { $errorText = $e->getMessage(); $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); if (strpos($e->getMessage(), 'Undefined property') !== false) { preg_match('/Undefined property: stdClass::\$(\w+)/', $e->getMessage(), $matches); $paramFaltante = $matches[1] ?? 'desconocido'; return new JsonResponse(['error' => "Falta un parámetro requerido: $paramFaltante"], Response::HTTP_BAD_REQUEST); } return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } try { $response = $this->curlKeaService->executeCurlCommand('config-get'); $subnetFound = false; $hostFound = false; $updatedHost = null; foreach ($response[0]['arguments']['Dhcp4']['subnet4'] as &$subnet) { if ($subnet['id'] == $subnetId) { $subnetFound = true; if (!isset($subnet['reservations'])) { $subnet['reservations'] = []; } foreach ($subnet['reservations'] as &$reservation) { if ($reservation['hw-address'] == $oldMacAddress) { $hostFound = true; $bootFileName = $reservation['boot-file-name'] ?? $subnet['boot-file-name'] ?? null; $nextServer = $reservation['next-server'] ?? $subnet['next-server'] ?? null; $reservation['hw-address'] = $macAddress; $reservation['ip-address'] = $address; $reservation['hostname'] = $host; if ($bootFileName !== null) { $reservation['boot-file-name'] = $bootFileName; } if ($nextServer !== null) { $reservation['next-server'] = $nextServer; } $updatedHost = $reservation; break; } } } } if ($subnetFound && $hostFound) { 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) { $errorText = "Error al guardar la configuración en Kea DHCP: " . $responseSet[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } else { $responseWrite = $this->curlKeaService->executeCurlCommand('config-write', $configuration); if ($responseWrite == false || $responseWrite[0]["result"] != 0) { $errorText = "Error al escribir la configuración en Kea DHCP: " . $responseWrite[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } else { $this->logger->info(json_encode([ 'severity' => 'INFO', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_OK, 'desc' => 'Host actualizado correctamente.', 'params' => ['host' => $host, 'subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['success' => "Host actualizado correctamente", 'message' => $updatedHost], Response::HTTP_OK); } } } else { $errorText = "Error en la configuración de Kea: " . $responseTest[0]["text"]; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } } elseif (!$subnetFound) { $errorText = "Error: La subred con el id '$subnetId' no existe."; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText, 'params' => ['subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } elseif (!$hostFound) { $errorText = "Error: El host con la MAC '$oldMacAddress' no existe en las reservaciones."; $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_BAD_REQUEST, 'desc' => $errorText, 'params' => ['old_mac_address' => $oldMacAddress, 'subnet_id' => $subnetId] ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_BAD_REQUEST); } } catch (Exception $e) { $errorText = "Error al obtener la configuración de Kea DHCP: " . $e->getMessage(); $this->logger->error(json_encode([ 'severity' => 'ERROR', 'operation' => $operation, 'component' => $component, 'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'desc' => $errorText ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return new JsonResponse(['error' => $errorText], Response::HTTP_INTERNAL_SERVER_ERROR); } return new JsonResponse(['error' => 'Unexpected error occurred'], Response::HTTP_INTERNAL_SERVER_ERROR); } /** * @OA\Get( * path="/ogdhcp/v1/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 obtenido con exito"), * @OA\Property(property="message", type="array", * @OA\Items(ref="#/components/schemas/Host") * ) * ) * ), * @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/backup", methods={"GET"}) */ public function getLatestBackupConfiguration(): JsonResponse { #$backup_dir = '/opt/ogdhcp/etc/kea/backup'; $backup_dir = $this->backup_dir; 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); if (!isset($configuration['Dhcp4']['subnet4'])) { $responseError = "No se encontró el parámetro 'subnet4' en el backup"; return new JsonResponse(['error' => $responseError], 400); } $responseSuccess = "Backup obtenido con exito"; 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", * 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, returning all currently loaded subnets.", * @OA\Response( * response=200, * description="Configuration restored successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="success", type="string", example="Configuración cargada correctamente"), * @OA\Property(property="message", type="array", * @OA\Items(ref="#/components/schemas/Host") * ) * ) * ), * @OA\Response( * response=400, * description="Error occurred while restoring the configuration", * @OA\JsonContent( * oneOf={ * @OA\Schema( * @OA\Property(property="error", type="string", example="No se encontraron archivos de backup") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error configuracion de kea invalido") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error al guardar la configuración en Kea DHCP") * ), * @OA\Schema( * @OA\Property(property="error", type="string", example="Error al escribir en la configuración en Kea DHCP") * ) * } * ), * @OA\Examples( * example="noBackup", * summary="No backup files found", * value={"error": "No se encontraron archivos de backup"} * ), * @OA\Examples( * example="invalidKea", * summary="Error in config-test in kea", * value={"error": "Error configuracion de kea invalido"} * ), * @OA\Examples( * example="saveKea", * summary="Error in config-set in kea", * value={"error": "Error al guardar la configuración en Kea DHCP"} * ), * @OA\Examples( * example="writeKea", * summary="Error in config-write in kea", * value={"error": "Error al escribir en la configuración en Kea DHCP"} * ) * ) * ) * @Route("/ogdhcp/v1/backup", methods={"POST"}) */ public function restoreDhcpConfiguration(): JsonResponse { #$backup_dir = '/opt/ogdhcp/etc/kea/backup'; $backup_dir = $this->backup_dir; try { $backup_files = glob($backup_dir . '/*.conf'); if (empty($backup_files)) { $response = "No se encontraron archivos de backup"; return new JsonResponse(['error' => $response], 400); } else { usort($backup_files, function ($a, $b) { return filemtime($b) - filemtime($a); }); $backup_file = reset($backup_files); $config = file_get_contents($backup_file); // Eliminar el campo 'hash' si existe $configuration = json_decode($config); if (isset($configuration->hash)) { unset($configuration->hash); } $test_command = 'config-test'; $test_output = $this->curlKeaService->executeCurlCommand($test_command, $configuration); if ($test_output == false || $test_output[0]["result"] != 0) { $responseError = "Error al comprobar la configuración de Kea: " . $test_output[0]["text"]; return new JsonResponse(['error' => $responseError], 400); } else { $set_command = 'config-set'; $set_output = $this->curlKeaService->executeCurlCommand($set_command, $configuration, false); if ($set_output == false || $set_output[0]["result"] != 0) { $responseError = "Error al guardar la última configuración de Kea: " . $set_output[0]["text"]; return new JsonResponse(['error' => $responseError], 400); } else { unlink($backup_file); $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 { // Consultar las subredes cargadas $config_get = $this->curlKeaService->executeCurlCommand('config-get'); if ($config_get == false || $config_get[0]["result"] != 0) { return new JsonResponse(['error' => 'Error al obtener la configuración de Kea DHCP'], 500); } $subnets = $config_get[0]['arguments']['Dhcp4']['subnet4'] ?? []; return new JsonResponse(['success' => "Configuración cargada correctamente", 'subnets' => $subnets], 200); } } } } } catch (Exception $e) { $responseError = "Error al restaurar la configuración: " . $e->getMessage(); return new JsonResponse(['error' => $responseError], 400); } } }