Add endpoints to remove leases from subnet and host

pull/12/head
Nicolas Arenas 2026-04-17 19:53:08 +02:00
parent 5db1a54779
commit 54eab1342b
2 changed files with 294 additions and 0 deletions

View File

@ -2209,6 +2209,261 @@ public function restoreDhcpConfiguration(): JsonResponse
} }
/**
* @OA\Delete(
* path="/ogdhcp/v1/subnets/{subnetId}/leases",
* summary="Elimina todos los leases de una subred",
* @OA\Parameter(
* name="subnetId",
* in="path",
* description="ID de la subred cuyos leases se van a eliminar",
* required=true,
* @OA\Schema(type="integer")
* ),
* @OA\Response(
* response=200,
* description="Leases eliminados correctamente",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="success", type="string", example="Leases de la subred eliminados correctamente"),
* @OA\Property(property="message", type="string", example="Leases deleted.")
* )
* ),
* @OA\Response(
* response=404,
* description="No se encontraron leases en la subred",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string", example="No se encontraron leases en la subred con id '44'")
* )
* ),
* @OA\Response(
* response=500,
* description="Error al comunicarse con el agente de control de Kea",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string", example="Error al ejecutar lease4-wipe")
* )
* )
* )
* @Route("/ogdhcp/v1/subnets/{subnetId}/leases", methods={"DELETE"})
*/
public function wipeSubnetLeases(Request $request): JsonResponse
{
$operation = 'wipeSubnetLeases';
$component = 'ogdhcp';
$subnetId = (int) $request->get('subnetId');
try {
$this->logger->info(json_encode([
'severity' => 'INFO',
'operation' => $operation,
'component' => $component,
'params' => ['subnet_id' => $subnetId],
'desc' => 'Iniciando eliminación de leases de la subred.'
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
$response = $this->curlKeaService->executeLeaseCommand('lease4-wipe', ['subnet-id' => $subnetId]);
if (!$response) {
$errorText = "No se obtuvo respuesta del agente de control de Kea al ejecutar lease4-wipe.";
$this->logger->error(json_encode([
'severity' => 'ERROR',
'operation' => $operation,
'component' => $component,
'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR,
'desc' => $errorText,
'params' => ['subnet_id' => $subnetId]
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
return new JsonResponse(['error' => $errorText], Response::HTTP_INTERNAL_SERVER_ERROR);
}
$result = $response[0]['result'];
$text = $response[0]['text'] ?? '';
// result 3 = empty: el comando se ejecutó pero no había leases
if ($result === 3) {
$errorText = "No se encontraron leases en la subred con id '$subnetId'";
$this->logger->info(json_encode([
'severity' => 'INFO',
'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);
}
if ($result !== 0) {
$errorText = "Error al eliminar los leases de la subred '$subnetId': $text";
$this->logger->error(json_encode([
'severity' => 'ERROR',
'operation' => $operation,
'component' => $component,
'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR,
'desc' => $errorText,
'params' => ['subnet_id' => $subnetId, 'kea_result' => $result]
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
return new JsonResponse(['error' => $errorText], Response::HTTP_INTERNAL_SERVER_ERROR);
}
$this->logger->info(json_encode([
'severity' => 'INFO',
'operation' => $operation,
'component' => $component,
'http_code' => Response::HTTP_OK,
'desc' => 'Leases de la subred eliminados correctamente.',
'params' => ['subnet_id' => $subnetId]
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
return new JsonResponse([
'success' => "Leases de la subred eliminados correctamente",
'message' => $text
], Response::HTTP_OK);
} catch (Exception $e) {
$errorText = "Error al ejecutar lease4-wipe: " . $e->getMessage();
$this->logger->error(json_encode([
'severity' => 'ERROR',
'operation' => $operation,
'component' => $component,
'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR,
'desc' => $errorText,
'params' => ['subnet_id' => $subnetId]
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
return new JsonResponse(['error' => $errorText], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
/**
* @OA\Delete(
* path="/ogdhcp/v1/leases/{ipAddress}",
* summary="Elimina el lease de una máquina por su dirección IP",
* @OA\Parameter(
* name="ipAddress",
* in="path",
* description="Dirección IP del lease a eliminar (ej: 192.168.1.11)",
* required=true,
* @OA\Schema(type="string")
* ),
* @OA\Response(
* response=200,
* description="Lease eliminado correctamente",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="success", type="string", example="Lease eliminado correctamente"),
* @OA\Property(property="message", type="string", example="IPv4 lease deleted.")
* )
* ),
* @OA\Response(
* response=404,
* description="No se encontró un lease para la IP indicada",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string", example="No se encontró un lease para la IP '192.168.1.11'")
* )
* ),
* @OA\Response(
* response=500,
* description="Error al comunicarse con el agente de control de Kea",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string", example="Error al ejecutar lease4-del")
* )
* )
* )
* @Route("/ogdhcp/v1/leases/{ipAddress}", methods={"DELETE"}, requirements={"ipAddress"=".+"})
*/
public function deleteHostLease(Request $request): JsonResponse
{
$operation = 'deleteHostLease';
$component = 'ogdhcp';
$ipAddress = $request->get('ipAddress');
try {
$this->logger->info(json_encode([
'severity' => 'INFO',
'operation' => $operation,
'component' => $component,
'params' => ['ip_address' => $ipAddress],
'desc' => 'Iniciando eliminación del lease del host.'
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
$response = $this->curlKeaService->executeLeaseCommand('lease4-del', ['ip-address' => $ipAddress]);
if (!$response) {
$errorText = "No se obtuvo respuesta del agente de control de Kea al ejecutar lease4-del.";
$this->logger->error(json_encode([
'severity' => 'ERROR',
'operation' => $operation,
'component' => $component,
'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR,
'desc' => $errorText,
'params' => ['ip_address' => $ipAddress]
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
return new JsonResponse(['error' => $errorText], Response::HTTP_INTERNAL_SERVER_ERROR);
}
$result = $response[0]['result'];
$text = $response[0]['text'] ?? '';
// result 3 = empty: no se encontró un lease para esa IP
if ($result === 3) {
$errorText = "No se encontró un lease para la IP '$ipAddress'";
$this->logger->info(json_encode([
'severity' => 'INFO',
'operation' => $operation,
'component' => $component,
'http_code' => Response::HTTP_NOT_FOUND,
'desc' => $errorText,
'params' => ['ip_address' => $ipAddress]
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
return new JsonResponse(['error' => $errorText], Response::HTTP_NOT_FOUND);
}
if ($result !== 0) {
$errorText = "Error al eliminar el lease de la IP '$ipAddress': $text";
$this->logger->error(json_encode([
'severity' => 'ERROR',
'operation' => $operation,
'component' => $component,
'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR,
'desc' => $errorText,
'params' => ['ip_address' => $ipAddress, 'kea_result' => $result]
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
return new JsonResponse(['error' => $errorText], Response::HTTP_INTERNAL_SERVER_ERROR);
}
$this->logger->info(json_encode([
'severity' => 'INFO',
'operation' => $operation,
'component' => $component,
'http_code' => Response::HTTP_OK,
'desc' => 'Lease eliminado correctamente.',
'params' => ['ip_address' => $ipAddress]
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
return new JsonResponse([
'success' => "Lease eliminado correctamente",
'message' => $text
], Response::HTTP_OK);
} catch (Exception $e) {
$errorText = "Error al ejecutar lease4-del: " . $e->getMessage();
$this->logger->error(json_encode([
'severity' => 'ERROR',
'operation' => $operation,
'component' => $component,
'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR,
'desc' => $errorText,
'params' => ['ip_address' => $ipAddress]
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
return new JsonResponse(['error' => $errorText], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
} }

View File

@ -61,6 +61,45 @@ class CurlKeaService
} }
} }
public function executeLeaseCommand($command, $arguments)
{
$apiUrl = getenv('DHCP_IP');
if (!$apiUrl) {
$apiUrl = 'http://localhost:8000/';
}
$requestData = [
'command' => $command,
'service' => ['dhcp4'],
'arguments' => $arguments,
];
$jsonData = json_encode($requestData);
try {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Expect:']);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$output = json_decode($response, true);
if ($httpCode >= 200 && $httpCode < 300) {
return $output;
} else {
$error = curl_error($ch);
throw new Exception($error);
}
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
public function convertMaskToCIDR($mask) public function convertMaskToCIDR($mask)
{ {
$bits = 0; $bits = 0;