ogdhcp/src/DhcpBundle/Controller/DhcpController.php

1170 lines
51 KiB
PHP

<?php
// src/DhcpBundle/Controller/DhcpController.php
namespace App\DhcpBundle\Controller;
use OpenApi\Annotations as OA;
use Psr\Log\LoggerInterface;
use App\DhcpBundle\Service\CurlKeaService;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Exception;
class DhcpController
{
private $logger;
/**
* @OA\Info(title="Ogdhcp API", version="1.0")
*/
/**
* @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="reservations",
* type="array",
* @OA\Items(type="object", description="The reservations in the subnet")
* )
* )
*/
private $curlKeaService;
public function __construct(CurlKeaService $curlKeaService, LoggerInterface $logger)
{
$this->curlKeaService = $curlKeaService;
$this->logger = $logger;
}
/**
* @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
{
try {
// Obtener el uso de disco
$diskUsageResult = $this->getDiskUsage();
if (!$diskUsageResult) {
throw new \Exception('Failed to retrieve disk usage');
}
// Obtener el estado de los servicios de ogDHCP
$servicesStatusResult = $this->getServicesStatus();
if (!$servicesStatusResult) {
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) {
// Si ocurre un error, simplemente dejar $subnetsResult vacío
$subnetsResult = [];
}
// Componer la respuesta
$response = [
'disk_usage' => $diskUsageResult,
'subnets' => $subnetsResult,
'services_status' => $servicesStatusResult
];
return new JsonResponse(['success' => "Información obtenida con éxito", 'message' => $response], Response::HTTP_OK);
} catch (\Exception $e) {
// 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 /opt/ogdhcp | tail -1 | awk '{print $2, $3, $4, $5}'");
if (!$output) {
$this->logger->error("Failed to execute disk usage command");
return null;
}
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="reservations",
* type="array",
* @OA\Items(type="object", description="The reservations in the subnet")
* )
* )
*/
/**
* @OA\Get(
* path="/ogdhcp/v1/subnets",
* @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="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 al obtener las subredes",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string", example="Error al obtener las subredes")
* )
* )
* )
* @Route("/ogdhcp/v1/subnets", methods={"GET"})
*/
public function getSubnets(): JsonResponse
{
try {
$response = $this->curlKeaService->executeCurlCommand('config-get');
if (!$response) {
return new JsonResponse(['error' => 'Error: No se pudo acceder al archivo de configuración de Kea'], 400);
}
$result_code = $response[0]["result"];
if ($result_code == 0) {
if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) {
return new JsonResponse(['error' => 'El campo "subnet4" no está inicializado'], 400);
} else {
$subnets = $response[0]['arguments']['Dhcp4']['subnet4'];
return new JsonResponse([
'success' => 'Subredes obtenidas correctamente',
'message' => $subnets
], 200);
}
} else {
return new JsonResponse(['error' => "Error en la configuración de Kea: " . $response[0]["text"]], 400);
}
} catch (Exception $e) {
return new JsonResponse(['error' => "Error al obtener la configuración de Kea DHCP: " . $e->getMessage()], 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",
* @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.")
* )
* )
* )
* @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); /**
* @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"})
*/
// Controlar las salidas para los diferentes casos
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\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",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string", example="Error: No hay subredes definidas")
* )
* ),
* @OA\Response(
* response=404,
* description="Subnet not found",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string", example="Error: 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 guardar la configuración en Kea DHCP: Unable to save configuration")
* )
* )
* )
* @Route("/ogdhcp/v1/subnets/{subnetId}", methods={"DELETE"})
*/
public function deleteDhcpSubnet(Request $request): JsonResponse
{
$subnetId = (int) $request->get('subnetId');
try {
$response = $this->curlKeaService->executeCurlCommand('config-get');
if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) {
$responseError = "Error: No hay subredes definidas";
return new JsonResponse(['error' => $responseError], 400);
}
$subnetIndex = array_search($subnetId, array_column($response[0]['arguments']['Dhcp4']['subnet4'], 'id'));
if ($subnetIndex === false) {
$responseError = "Error: La subred con el id '$subnetId' no existe";
return new JsonResponse(['error' => $responseError], 400);
} else {
unset($response[0]['arguments']['Dhcp4']['subnet4'][$subnetIndex]);
// Eliminar el campo 'hash' si existe
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"];
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 = "Subred eliminada 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);
}
}
/**
* @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="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",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string")
* )
* )
* )
*/
public function modifyDhcpSubnet(Request $request): JsonResponse
{
$subnetId = (int) $request->get('subnetId');
try {
$input = json_decode($request->getContent());
$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);
if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) {
$responseError = "Error: No hay subredes definidas";
return new JsonResponse(['error' => $responseError], 400);
}
$subnetIndex = array_search($subnetId, array_column($response[0]['arguments']['Dhcp4']['subnet4'], 'id'));
if ($subnetIndex === false) {
$responseError = "Error: La subred con el id '$subnetId' no existe";
return new JsonResponse(['error' => $responseError], 400);
} else {
$response[0]['arguments']['Dhcp4']['subnet4'][$subnetIndex] = [
"id" => $subnetId,
"subnet" => $subnetName,
"next-server" => $nextServer,
"boot-file-name" => $bootFileName,
"reservations" => []
];
$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 = "Subred modificada 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\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\Response(
* response=400,
* description="Error occurred",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string")
* )
* ),
* @OA\Response(
* response=500,
* description="Server error",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string")
* )
* )
* )
* @Route("/ogdhcp/v1/subnets/{subnetId}/hosts", methods={"GET"})
*/
public function getHosts($subnetId): JsonResponse
{
try {
$response = $this->curlKeaService->executeCurlCommand('config-get');
if (!$response) {
$responseError = 'Error: No se pudo acceder al archivo dhcpd.conf';
return new JsonResponse(['error' => $responseError], 400);
} else {
$result_code = $response[0]["result"];
if ($result_code == 0) {
if (!isset($response[0]['arguments']['Dhcp4']['subnet4'])) {
$responseError = 'El campo \'subnet4\' no está inicializado';
return new JsonResponse(['error' => $responseError], 400);
} else {
$subnets = $response[0]['arguments']['Dhcp4']['subnet4'];
foreach ($subnets as $subnet) {
if ($subnet['id'] == $subnetId) {
return new JsonResponse($subnet['reservations'], 200);
}
}
$responseError = 'Error: La subred con el id \'' . $subnetId . '\' no existe.';
return new JsonResponse(['error' => $responseError], 400);
}
} else {
$responseError = "Error kea configuration invalid: " . $response[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/{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",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="success", type="string")
* )
* ),
* @OA\Response(
* response=400,
* description="Error occurred",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string")
* )
* )
* )
* @Route("/ogdhcp/v1/subnets/{subnetId}/hosts", methods={"POST"})
*/
public function addDhcpHost(Request $request): JsonResponse
{
$subnetId = (int) $request->get('subnetId');
try {
$input = json_decode($request->getContent());
$host = htmlspecialchars($input->host);
$macAddress = htmlspecialchars($input->macAddress);
$address = htmlspecialchars($input->address);
} catch (Exception $e) {
$response["message"] = $e->getMessage();
return new JsonResponse(['error' => $response], 400);
}
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['reservations'])) {
$subnet['reservations'] = [];
}
$exists = array_reduce($subnet['reservations'], function ($exists, $reservation) use ($host) {
return $exists || ($reservation['hostname'] === $host);
});
if ($exists) {
$responseError = "Error: El host con el hostname '$host' ya existe en las reservaciones.";
return new JsonResponse(['error' => $responseError], 400);
} else {
$subnet['reservations'][] = $newHost;
break;
}
}
}
if (!$subnetFound) {
$responseError = "Error: No se encontró la subnet con id '$subnetId'.";
return new JsonResponse(['error' => $responseError], 400);
}
$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\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",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string")
* )
* )
* )
* @Route("/ogdhcp/v1/subnets/{subnetId}/hosts", methods={"DELETE"})
*/
public function deleteDhcpHost(Request $request, $subnetId): JsonResponse
{
try {
$input = json_decode($request->getContent());
$macAddress = htmlspecialchars($input->macAddress);
} catch (Exception $e) {
return new JsonResponse(['error' => $e->getMessage()], 400);
}
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) {
return new JsonResponse(['error' => "Error: La subred con el id '$subnetId' no existe."], 404);
}
if (!$hostFound) {
return new JsonResponse(['error' => "Error: El host con la MAC '$macAddress' no existe en las reservaciones."], 404);
}
// 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 {
return new JsonResponse(['success' => "Host eliminado correctamente"], 200);
}
}
} else {
return new JsonResponse(['error' => "Error en la configuración de Kea: " . $responseTest[0]["text"]], 400);
}
} catch (Exception $e) {
return new JsonResponse(['error' => $e->getMessage()], 400);
}
}
/**
* @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", type="object",
* @OA\Property(property="hostname", type="string", example="PC11"),
* @OA\Property(property="macAddress", type="string", example="56:6f:c7:4f:01:01"),
* @OA\Property(property="ipAddress", type="string", example="192.168.1.12")
* )
* )
* ),
* @OA\Response(
* response=400,
* description="Error occurred",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string", example="Invalid input data")
* )
* ),
* @OA\Response(
* response=404,
* description="Host not found",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string", example="Error: The host with MAC '56:6f:c7:4f:00:4f' was not found")
* )
* ),
* @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
{
try {
$input = json_decode($request->getContent());
$host = htmlspecialchars($input->host);
$oldMacAddress = htmlspecialchars($input->oldMacAddress);
$macAddress = htmlspecialchars($input->macAddress);
$address = htmlspecialchars($input->address);
} catch (Exception $e) {
$response["message"] = $e->getMessage();
return new JsonResponse(['error' => $response], 400);
}
try {
// Ejecutar el comando para obtener la configuración actual
$response = $this->curlKeaService->executeCurlCommand('config-get');
$subnetFound = false;
$hostFound = false;
foreach ($response[0]['arguments']['Dhcp4']['subnet4'] as &$subnet) {
if ($subnet['id'] == $subnetId) {
$this->logger->info('FOUND SUBNET');
$subnetFound = true;
if (!isset($subnet['reservations'])) {
$subnet['reservations'] = [];
}
foreach ($subnet['reservations'] as &$reservation) {
$this->logger->info('LOOKING FOR HOST');
if ($reservation['hw-address'] == $oldMacAddress) {
$this->logger->info('FOUND HOST');
$hostFound = true;
$reservation['hw-address'] = $macAddress;
$reservation['ip-address'] = $address;
$reservation['hostname'] = $host;
break;
}
}
}
}
if ($subnetFound && $hostFound) {
$this->logger->info('UPDATING HOST');
// Eliminar el campo 'hash' si existe
if (isset($response[0]['arguments']['hash'])) {
unset($response[0]['arguments']['hash']);
}
// Preparar la configuración modificada
$array_encoded = json_encode($response[0]['arguments']);
$configurationParsed = str_replace('\\', '', $array_encoded);
$configuration = json_decode($configurationParsed);
// Test de la configuración
$responseTest = $this->curlKeaService->executeCurlCommand('config-test', $configuration);
if ($responseTest[0]["result"] == 0) {
// Guardar la configuración
$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 {
// Escribir la configuración en disco
$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 {
return new JsonResponse(['success' => "Host actualizado correctamente"], 200);
}
}
} else {
$responseError = "Error en la configuración de Kea: " . $responseTest[0]["text"];
return new JsonResponse(['error' => $responseError], 400);
}
} elseif (!$subnetFound) {
$responseError = "Error: La subred con el id '$subnetId' no existe.";
return new JsonResponse(['error' => $responseError], 404);
} elseif (!$hostFound) {
$responseError = "Error: El host con la MAC " . $oldMacAddress . " no existe en las reservaciones.";
return new JsonResponse(['error' => $responseError], 404);
}
} catch (Exception $e) {
$responseError = "Error al obtener la configuración de Kea DHCP: " . $e->getMessage();
return new JsonResponse(['error' => $responseError], 400);
}
}
public function restoreDhcpConfiguration(): JsonResponse
{
$backup_dir = '/opt/opengnsys/etc/kea/backup';
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);
$configuration = json_decode($config);
$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 {
$responseSuccess = "Configuración cargada correctamente";
return new JsonResponse(['success' => $responseSuccess], 200);
}
}
}
}
} catch (Exception $e) {
$responseError = "Error al restaurar la configuración: " . $e->getMessage();
return new JsonResponse(['error' => $responseError], 400);
}
}
}