Merged branch 'nginx_conf' into 'main', ignoring conflicts and keeping 'nginx_conf' changes
parent
3ca7ddd20b
commit
e1b75f2efc
|
@ -31,3 +31,10 @@
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
/phpunit.xml
|
/phpunit.xml
|
||||||
###< symfony/phpunit-bridge ###
|
###< symfony/phpunit-bridge ###
|
||||||
|
|
||||||
|
.venv
|
||||||
|
ogdhcp.code-workspace
|
||||||
|
output.xml
|
||||||
|
log.html
|
||||||
|
report.html
|
||||||
|
__pycache__
|
||||||
|
|
|
@ -4,17 +4,16 @@
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.2.0",
|
"php": ">=8.1",
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
"doctrine/annotations": "^1.6",
|
"doctrine/annotations": "^1.6",
|
||||||
"doctrine/doctrine-bundle": "^2.0",
|
"doctrine/doctrine-bundle": "^2.0",
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.0",
|
"doctrine/doctrine-migrations-bundle": "^3.0",
|
||||||
"doctrine/orm": "^2.7",
|
"doctrine/orm": "^2.7",
|
||||||
|
"nelmio/api-doc-bundle": "^4.9",
|
||||||
"phpdocumentor/reflection-docblock": "^5.0",
|
"phpdocumentor/reflection-docblock": "^5.0",
|
||||||
"phpstan/phpdoc-parser": "^0.4",
|
"phpstan/phpdoc-parser": "^0.4",
|
||||||
"zircote/swagger-php": "3.*",
|
|
||||||
"symfony/runtime": "5.*",
|
|
||||||
"symfony/asset": "5.*",
|
"symfony/asset": "5.*",
|
||||||
"symfony/console": "5.*",
|
"symfony/console": "5.*",
|
||||||
"symfony/doctrine-messenger": "5.*",
|
"symfony/doctrine-messenger": "5.*",
|
||||||
|
@ -32,6 +31,7 @@
|
||||||
"symfony/process": "5.*",
|
"symfony/process": "5.*",
|
||||||
"symfony/property-access": "5.*",
|
"symfony/property-access": "5.*",
|
||||||
"symfony/property-info": "5.*",
|
"symfony/property-info": "5.*",
|
||||||
|
"symfony/runtime": "5.*",
|
||||||
"symfony/security-bundle": "5.*",
|
"symfony/security-bundle": "5.*",
|
||||||
"symfony/serializer": "5.*",
|
"symfony/serializer": "5.*",
|
||||||
"symfony/string": "5.*",
|
"symfony/string": "5.*",
|
||||||
|
@ -41,7 +41,8 @@
|
||||||
"symfony/web-link": "5.*",
|
"symfony/web-link": "5.*",
|
||||||
"symfony/yaml": "5.*",
|
"symfony/yaml": "5.*",
|
||||||
"twig/extra-bundle": "^2.12|^3.0",
|
"twig/extra-bundle": "^2.12|^3.0",
|
||||||
"twig/twig": "^2.12|^3.0"
|
"twig/twig": "^2.12|^3.0",
|
||||||
|
"zircote/swagger-php": "3.*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^8.5",
|
"phpunit/phpunit": "^8.5",
|
||||||
|
|
|
@ -12,4 +12,6 @@ return [
|
||||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||||
App\DhcpBundle\DhcpBundle::class => ['all' => true],
|
App\DhcpBundle\DhcpBundle::class => ['all' => true],
|
||||||
|
Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['all' => true],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
nelmio_api_doc:
|
||||||
|
documentation:
|
||||||
|
info:
|
||||||
|
title: OgDHCP API
|
||||||
|
description: OgDHCP API documentation
|
||||||
|
version: 1.0.0
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Host:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
hostname:
|
||||||
|
type: string
|
||||||
|
description: The hostname of the device
|
||||||
|
example: "pc11"
|
||||||
|
hw-address:
|
||||||
|
type: string
|
||||||
|
description: The hardware address (MAC)
|
||||||
|
example: "56:6f:c7:4f:00:4f"
|
||||||
|
ip-address:
|
||||||
|
type: string
|
||||||
|
description: The IP address assigned to the host
|
||||||
|
example: "192.168.5.11"
|
||||||
|
Subnet:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
description: The ID of the subnet
|
||||||
|
subnet:
|
||||||
|
type: string
|
||||||
|
description: The name of the subnet
|
||||||
|
next-server:
|
||||||
|
type: string
|
||||||
|
description: The next server in the subnet
|
||||||
|
boot-file-name:
|
||||||
|
type: string
|
||||||
|
description: The boot file name for the subnet
|
||||||
|
reservations:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Host'
|
||||||
|
description: The reservations in the subnet
|
||||||
|
|
||||||
|
|
||||||
|
areas: # to filter documented areas
|
||||||
|
path_patterns:
|
||||||
|
- ^/ogdhcp/ # Accepts routes under /api except /api/doc
|
|
@ -1,3 +1,7 @@
|
||||||
#index:
|
#index:
|
||||||
# path: /
|
# path: /
|
||||||
# controller: App\Controller\DefaultController::index
|
# controller: App\Controller\DefaultController::index
|
||||||
|
app.swagger_ui:
|
||||||
|
path: /ogdhcp/api/doc
|
||||||
|
methods: GET
|
||||||
|
defaults: { _controller: nelmio_api_doc.controller.swagger_ui }
|
|
@ -4,7 +4,7 @@
|
||||||
# Put parameters here that don't need to change on each machine where the app is deployed
|
# Put parameters here that don't need to change on each machine where the app is deployed
|
||||||
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
|
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
|
||||||
parameters:
|
parameters:
|
||||||
|
backup_dir: '%kernel.project_dir%/etc/kea/backup'
|
||||||
services:
|
services:
|
||||||
# default configuration for services in *this* file
|
# default configuration for services in *this* file
|
||||||
_defaults:
|
_defaults:
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
<VirtualHost *:80>
|
<VirtualHost *:80>
|
||||||
ServerName localhost
|
ServerName localhost
|
||||||
|
ServerAlias api-test
|
||||||
DocumentRoot /opt/ogdhcp/public
|
DocumentRoot /opt/ogdhcp/public
|
||||||
|
|
||||||
<Directory /opt/ogdhcp/public>
|
<Directory /opt/ogdhcp/public>
|
||||||
|
@ -21,7 +22,7 @@
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
<FilesMatch \.php$>
|
<FilesMatch \.php$>
|
||||||
SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost/"
|
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost/"
|
||||||
</FilesMatch>
|
</FilesMatch>
|
||||||
|
|
||||||
ErrorLog ${APACHE_LOG_DIR}/ogdhcp_error.log
|
ErrorLog ${APACHE_LOG_DIR}/ogdhcp_error.log
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
server {
|
||||||
|
listen 8081;
|
||||||
|
server_name __SERVERIP__ localhost; # IP del servidor
|
||||||
|
|
||||||
|
# Raíz del documento para el proyecto Symfony
|
||||||
|
root __PUBLICDIR__;
|
||||||
|
|
||||||
|
# Bloque para manejar las solicitudes a /ogdhcp
|
||||||
|
location /ogdhcp {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
# Aumentar el tiempo de espera por el install ogdhcp (si es necesario)
|
||||||
|
proxy_read_timeout 600;
|
||||||
|
proxy_connect_timeout 600;
|
||||||
|
proxy_send_timeout 600;
|
||||||
|
send_timeout 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bloque para manejar las solicitudes a index.php
|
||||||
|
location ~ ^/index.php(/|$) {
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_pass unix:/run/php/php__PHPVERSION__-fpm-ogdhcp.sock;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.*)$;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
fastcgi_param DOCUMENT_ROOT $document_root;
|
||||||
|
internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bloque para devolver 404 en cualquier solicitud a archivos PHP que no sean index.php
|
||||||
|
location ~ \.php$ {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Logs de error y acceso para el proyecto Symfony
|
||||||
|
error_log /var/log/nginx/ogdhcp_error.log;
|
||||||
|
access_log /var/log/nginx/ogdhcp_access.log;
|
||||||
|
|
||||||
|
# Manejo de la ruta para la documentación de la API (Swagger)
|
||||||
|
location /ogdhcp/api/doc {
|
||||||
|
try_files $uri /index.php?$query_string;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"interfaces": ["eth0", "eth1"],
|
||||||
|
"ogbootIP": "172.17.8.37",
|
||||||
|
"ogDhcpIP": "172.17.8.37",
|
||||||
|
"ogDhcp_Dir": "/opt/opengnsys/ogdhcp"
|
||||||
|
|
||||||
|
}
|
|
@ -10,7 +10,13 @@ function globalSetup() {
|
||||||
current_dir=$(dirname "$0")
|
current_dir=$(dirname "$0")
|
||||||
PROGRAMDIR=$(readlink -e "$current_dir")
|
PROGRAMDIR=$(readlink -e "$current_dir")
|
||||||
PROGRAMNAME=$(basename "$0")
|
PROGRAMNAME=$(basename "$0")
|
||||||
OPENGNSYS_CLIENT_USER="ogdhcp"
|
OPENGNSYS_CLIENT_USER="opengnsys"
|
||||||
|
current_dir=$(dirname "$0")
|
||||||
|
PROGRAMDIR=$(readlink -e "$current_dir")
|
||||||
|
|
||||||
|
# Ruta del archivo config_ogdhcp.json proporcionado por el usuario
|
||||||
|
CONFIG_FILE="$PROGRAMDIR/config_ogdhcp.json"
|
||||||
|
|
||||||
|
|
||||||
# Comprobar si se ha descargado el paquete comprimido (REMOTE=0) o sólo el instalador (REMOTE=1).
|
# Comprobar si se ha descargado el paquete comprimido (REMOTE=0) o sólo el instalador (REMOTE=1).
|
||||||
if [ -d "$PROGRAMDIR/../installer" ]; then
|
if [ -d "$PROGRAMDIR/../installer" ]; then
|
||||||
|
@ -27,7 +33,7 @@ function globalSetup() {
|
||||||
|
|
||||||
# Directorios de instalación y destino de OpenGnsys.
|
# Directorios de instalación y destino de OpenGnsys.
|
||||||
WORKDIR=/tmp/ogdhcp_installer
|
WORKDIR=/tmp/ogdhcp_installer
|
||||||
INSTALL_TARGET=/opt/ogdhcp
|
INSTALL_TARGET=$(jq -r '.ogDhcp_Dir' "$CONFIG_FILE")
|
||||||
PATH=$PATH:$INSTALL_TARGET/bin
|
PATH=$PATH:$INSTALL_TARGET/bin
|
||||||
|
|
||||||
if command -v service &>/dev/null; then
|
if command -v service &>/dev/null; then
|
||||||
|
@ -41,18 +47,14 @@ function globalSetup() {
|
||||||
ENABLESERVICE="eval update-rc.d \$service defaults"
|
ENABLESERVICE="eval update-rc.d \$service defaults"
|
||||||
DISABLESERVICE="eval update-rc.d \$service disable"
|
DISABLESERVICE="eval update-rc.d \$service disable"
|
||||||
|
|
||||||
APACHESERV=apache2
|
# Variables globales
|
||||||
APACHECFGDIR=/etc/apache2
|
DEFAULTDEV=""
|
||||||
APACHESITESDIR=sites-available
|
NGINX_TEMPLATE="$INSTALL_TARGET/etc/nginxServer.conf.tmpl"
|
||||||
APACHEOGSITE=ogdhcp
|
NGINX_OUTPUT="/etc/nginx/sites-available/ogdhcp.conf"
|
||||||
APACHEUSER="www-data"
|
NGINX_CONF_PATH="/etc/nginx/nginx.conf"
|
||||||
APACHEGROUP="www-data"
|
PHP_FPM_CONF_PATH="/etc/php/__PHPVERSION__/fpm/pool.d/www.conf"
|
||||||
APACHEENABLEMODS="a2enmod headers ssl rewrite proxy_fcgi fastcgi actions alias"
|
NEW_FPM_CONF_PATH="/etc/php/__PHPVERSION__/fpm/pool.d/ogdhcp.conf"
|
||||||
APACHEENABLESSL="a2ensite default-ssl"
|
SOCKET_PATH="/run/php/php__PHPVERSION__-fpm-ogdhcp.sock"
|
||||||
APACHEENABLEOG="a2ensite $APACHEENABLEOG"
|
|
||||||
APACHEMAKECERT="make-ssl-cert generate-default-snakeoil --force-overwrite"
|
|
||||||
|
|
||||||
PHPFPMSERV=php7.2-fpm
|
|
||||||
|
|
||||||
# Registro de incidencias.
|
# Registro de incidencias.
|
||||||
OGLOGFILE="$INSTALL_TARGET/var/log/${PROGRAMNAME%.sh}.log"
|
OGLOGFILE="$INSTALL_TARGET/var/log/${PROGRAMNAME%.sh}.log"
|
||||||
|
@ -80,47 +82,32 @@ function checkDependencies() {
|
||||||
php-bcmath
|
php-bcmath
|
||||||
composer
|
composer
|
||||||
unzip
|
unzip
|
||||||
apache2
|
|
||||||
libapache2-mod-php
|
|
||||||
kea-dhcp4-server
|
kea-dhcp4-server
|
||||||
kea-common
|
kea-common
|
||||||
kea-ctrl-agent
|
kea-ctrl-agent
|
||||||
jq
|
jq
|
||||||
net-tools
|
net-tools
|
||||||
|
nginx
|
||||||
|
|
||||||
)
|
)
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update -y
|
||||||
# Comprobar cada dependencia
|
# Comprobar cada dependencia
|
||||||
for dep in "${DEPENDENCIES[@]}"; do
|
for dep in "${DEPENDENCIES[@]}"; do
|
||||||
if ! dpkg -s "$dep" >/dev/null 2>&1; then
|
if ! dpkg -s "$dep" >/dev/null 2>&1; then
|
||||||
echoAndLog "$dep is not installed. Installing..."
|
echoAndLog "$dep is not installed. Installing..."
|
||||||
sudo apt-get install -y "$dep"
|
apt-get install -y --no-install-recommends "$dep"
|
||||||
else
|
else
|
||||||
echoAndLog "$dep is already installed."
|
echoAndLog "$dep is already installed."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
sed -i '/ConditionFileNotEmpty=\/etc\/kea\/kea-api-password/d' /usr/lib/systemd/system/kea-ctrl-agent.service
|
||||||
|
chown -R _kea:_kea /etc/kea
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl restart kea-ctrl-agent.service
|
||||||
echoAndLog "Dependencies checked."
|
echoAndLog "Dependencies checked."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Función para instalar los paquetes necesarios para KEA-DHCP
|
|
||||||
install_kea() {
|
|
||||||
sudo apt-get install -y isc-kea-common isc-kea-ctrl-agent isc-kea-dhcp4-server isc-kea-dhcp6-server isc-kea-admin
|
|
||||||
}
|
|
||||||
|
|
||||||
# Función para instalar Composer
|
|
||||||
install_composer() {
|
|
||||||
curl -sS https://getcomposer.org/installer | php
|
|
||||||
sudo mv composer.phar /usr/local/bin/composer
|
|
||||||
}
|
|
||||||
|
|
||||||
# Función para instalar Swagger UI
|
|
||||||
install_swagger() {
|
|
||||||
sudo apt-get install -y unzip
|
|
||||||
wget https://github.com/swagger-api/swagger-ui/archive/master.zip
|
|
||||||
unzip master.zip -d /var/www/html/
|
|
||||||
sudo mv /var/www/html/swagger-ui-master /var/www/html/swagger-ui
|
|
||||||
}
|
|
||||||
|
|
||||||
# Obtiene el código fuente del proyecto desde el repositorio de GitHub.
|
# Obtiene el código fuente del proyecto desde el repositorio de GitHub.
|
||||||
function downloadCode() {
|
function downloadCode() {
|
||||||
|
@ -189,22 +176,32 @@ function createDirs() {
|
||||||
# Cambiar permisos de usuario
|
# Cambiar permisos de usuario
|
||||||
echoAndLog "Changing user permission"
|
echoAndLog "Changing user permission"
|
||||||
chown -R "$OPENGNSYS_CLIENT_USER:$OPENGNSYS_CLIENT_USER" "$INSTALL_TARGET"
|
chown -R "$OPENGNSYS_CLIENT_USER:$OPENGNSYS_CLIENT_USER" "$INSTALL_TARGET"
|
||||||
|
|
||||||
# Copiar .env
|
|
||||||
cp -a "$WORKDIR/ogdhcp/.env" "${path_opengnsys_base}/.env"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function create_ogdhcp_project {
|
function create_ogdhcp_project {
|
||||||
# Cambia al usuario ogdhcp y crea el proyecto Symfony
|
# Crea el usuario ogdhcp si no existe
|
||||||
local path_opengnsys_base="$1"
|
local path_opengnsys_base="$1"
|
||||||
composer create-project symfony/website-skeleton "$path_opengnsys_base"
|
if ! id -u opengnsys &>/dev/null; then
|
||||||
pushd "$path_opengnsys_base" || return
|
echoAndLog "${FUNCNAME}(): creating opengnsys user"
|
||||||
# Elimina el archivo composer.lock
|
useradd opengnsys 2>/dev/null
|
||||||
rm composer.lock
|
if [ $? -ne 0 ]; then
|
||||||
popd || return
|
errorAndLog "${FUNCNAME}(): error creating opengnsys user"
|
||||||
echoAndLog "Esqueleto de la aplicación creado y archivo composer.lock eliminado."
|
return 1
|
||||||
}
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Crea el directorio path_opengnsys_base con el usuario opengnsys
|
||||||
|
echoAndLog "${FUNCNAME}(): creating directory $path_opengnsys_base with opengnsys user"
|
||||||
|
sudo mkdir -p "$path_opengnsys_base"
|
||||||
|
sudo chown opengnsys:opengnsys $path_opengnsys_base
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
errorAndLog "${FUNCNAME}(): error while creating directory $path_opengnsys_base"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echoAndLog "Directory $path_opengnsys_base created with opengnsys user"
|
||||||
|
}
|
||||||
|
|
||||||
function copyServerFiles() {
|
function copyServerFiles() {
|
||||||
if [ $# -ne 1 ]; then
|
if [ $# -ne 1 ]; then
|
||||||
|
@ -220,9 +217,6 @@ function copyServerFiles() {
|
||||||
#public
|
#public
|
||||||
src
|
src
|
||||||
etc
|
etc
|
||||||
templates
|
|
||||||
tests
|
|
||||||
vendor
|
|
||||||
.env
|
.env
|
||||||
composer.json
|
composer.json
|
||||||
composer.lock
|
composer.lock
|
||||||
|
@ -234,9 +228,6 @@ function copyServerFiles() {
|
||||||
#public
|
#public
|
||||||
src
|
src
|
||||||
etc
|
etc
|
||||||
templates
|
|
||||||
tests
|
|
||||||
vendor
|
|
||||||
.env
|
.env
|
||||||
composer.json
|
composer.json
|
||||||
composer.lock
|
composer.lock
|
||||||
|
@ -271,53 +262,14 @@ function copyServerFiles() {
|
||||||
popd || return
|
popd || return
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadComposer() {
|
|
||||||
echoAndLog "Downloading composer.phar..."
|
|
||||||
|
|
||||||
# Crear el directorio de trabajo si no existe
|
|
||||||
mkdir -p "$WORKDIR/ogdhcp/bin" || return
|
|
||||||
|
|
||||||
# Cambiar al directorio de trabajo
|
|
||||||
pushd "$WORKDIR/ogdhcp/bin" || return
|
|
||||||
|
|
||||||
# Descargar composer.phar
|
|
||||||
curl -sS https://getcomposer.org/installer | php
|
|
||||||
|
|
||||||
# Comprobar si la descarga fue exitosa
|
|
||||||
if [ ! -f composer.phar ]; then
|
|
||||||
errorAndLog "Failed to download composer.phar"
|
|
||||||
popd
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Crear el directorio de destino si no existe
|
|
||||||
mkdir -p "/opt/ogdhcp/bin" || return
|
|
||||||
|
|
||||||
# Mover composer.phar a /opt/ogdhcp/bin
|
|
||||||
mv composer.phar "/opt/ogdhcp/bin/"
|
|
||||||
|
|
||||||
# Comprobar si el movimiento fue exitoso
|
|
||||||
if [ ! -f "/opt/ogdhcp/bin/composer.phar" ]; then
|
|
||||||
errorAndLog "Failed to move composer.phar to /opt/ogdhcp/bin"
|
|
||||||
popd
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Volver al directorio original
|
|
||||||
popd || return
|
|
||||||
|
|
||||||
echoAndLog "composer.phar downloaded and moved to /opt/ogdhcp/bin"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function runComposer() {
|
function runComposer() {
|
||||||
echoAndLog "Running composer.phar to install dependencies..."
|
echoAndLog "Running composer.phar to install dependencies..."
|
||||||
|
local path_opengnsys_base="$1"
|
||||||
# Cambiar al directorio donde está composer.phar
|
pushd $path_opengnsys_base
|
||||||
pushd /opt/ogdhcp/bin || return
|
pwd
|
||||||
|
|
||||||
# Ejecutar composer.phar
|
# Ejecutar composer.phar
|
||||||
sudo -u "$OPENGNSYS_CLIENT_USER" php composer.phar install
|
sudo -u "$OPENGNSYS_CLIENT_USER" composer --no-interaction install
|
||||||
|
|
||||||
# Comprobar si la ejecución fue exitosa
|
# Comprobar si la ejecución fue exitosa
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
@ -326,92 +278,270 @@ function runComposer() {
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Volver al directorio original
|
|
||||||
popd || return
|
|
||||||
|
|
||||||
echoAndLog "composer.phar ran successfully and dependencies were installed"
|
echoAndLog "composer.phar ran successfully and dependencies were installed"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function install_swagger_ui {
|
|
||||||
# Define la URL del archivo de Swagger UI que quieres descargar
|
|
||||||
swagger_ui_url="https://github.com/swagger-api/swagger-ui/archive/refs/heads/master.zip"
|
|
||||||
|
|
||||||
# Define la ruta donde quieres descomprimir Swagger UI
|
get_first_network_interface_with_traffic() {
|
||||||
swagger_ui_path="/tmp/swagger-ui"
|
while read -r line; do
|
||||||
|
if [[ "$line" == *:* ]]; then
|
||||||
|
interface=$(echo "$line" | cut -d ':' -f 1 | xargs)
|
||||||
|
if [[ "$interface" != "lo" ]]; then
|
||||||
|
received_bytes=$(echo "$line" | awk '{print $2}')
|
||||||
|
transmitted_bytes=$(echo "$line" | awk '{print $10}')
|
||||||
|
if (( received_bytes > 0 || transmitted_bytes > 0 )); then
|
||||||
|
DEFAULTDEV="$interface"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < /proc/net/dev
|
||||||
|
}
|
||||||
|
comment_auth_kea() {
|
||||||
|
KEA_CTRL_AGENT_CONF="/etc/kea/kea-ctrl-agent.conf"
|
||||||
|
|
||||||
|
# Verificar si el bloque de "authentication" ya está comentado
|
||||||
|
if grep -q '^[^#]*"authentication": {' "$KEA_CTRL_AGENT_CONF"; then
|
||||||
|
echo "Comentando el bloque de autenticación en $KEA_CTRL_AGENT_CONF..."
|
||||||
|
|
||||||
# Define la ruta de destino para los archivos de Swagger UI
|
# Comentar solo el bloque de authentication desde la apertura hasta la línea con '},'
|
||||||
destination_path="/opt/ogdhcp/public"
|
sed -i '/"authentication": {/,/^[[:space:]]*},/ {
|
||||||
|
s/^\([[:space:]]*\)\([^#]\)/\1#\2/
|
||||||
|
}' "$KEA_CTRL_AGENT_CONF"
|
||||||
|
|
||||||
# Crea los directorios si no existen
|
echo "Bloque de autenticación comentado correctamente."
|
||||||
mkdir -p "$swagger_ui_path"
|
else
|
||||||
mkdir -p "$destination_path"
|
echo "El bloque de autenticación ya está comentado."
|
||||||
|
fi
|
||||||
|
|
||||||
# Descarga el archivo de Swagger UI
|
# Verificar si el bloque fue comentado correctamente
|
||||||
wget "$swagger_ui_url" -O /tmp/swagger-ui.zip
|
if grep -q '^#\s*"authentication": {' "$KEA_CTRL_AGENT_CONF"; then
|
||||||
|
echo "Confirmación: Bloque de autenticación comentado correctamente."
|
||||||
|
|
||||||
# Descomprime el archivo de Swagger UI en la ruta especificada
|
# Reiniciar el servicio de Kea Control Agent para aplicar los cambios
|
||||||
unzip /tmp/swagger-ui.zip -d "$swagger_ui_path"
|
echo "Reiniciando el agente de Kea Control Agent..."
|
||||||
|
sudo systemctl restart kea-ctrl-agent.service
|
||||||
|
|
||||||
# Copia los archivos de Swagger UI al directorio de destino
|
if systemctl is-active --quiet kea-ctrl-agent.service; then
|
||||||
cp -r "$swagger_ui_path"/swagger-ui-master/dist/* "$destination_path"
|
echo "El agente de Kea Control Agent se ha reiniciado correctamente."
|
||||||
|
else
|
||||||
# Elimina el archivo descargado y el directorio temporal
|
echo "Error: No se pudo reiniciar el agente de Kea Control Agent."
|
||||||
rm /tmp/swagger-ui.zip
|
fi
|
||||||
rm -r "$swagger_ui_path"
|
else
|
||||||
/opt/ogdhcp/vendor/bin/openapi /opt/ogdhcp/src/DhcpBundle/Controller/ -o "$destination_path/swagger.json"
|
echo "Error: No se pudo comentar correctamente el bloque de autenticación."
|
||||||
echo "Swagger UI instalado en $destination_path."
|
fi
|
||||||
|
}
|
||||||
|
# Función para obtener la dirección IP de una interfaz
|
||||||
|
get_ip_address() {
|
||||||
|
local interface="$1"
|
||||||
|
ip -4 addr show "$interface" | grep -oP "(?<=inet\s)\d+(\.\d+){3}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function installWebConsoleApacheConf() {
|
# Función para obtener la versión de PHP instalada
|
||||||
if [ $# -ne 2 ]; then
|
get_php_fpm_version() {
|
||||||
errorAndLog "${FUNCNAME}(): invalid number of parameters"
|
php -v | grep -oP "PHP \K\d+\.\d+"
|
||||||
|
}
|
||||||
|
add_write_permission_apparmor() {
|
||||||
|
APPARMOR_PROFILE="/etc/apparmor.d/usr.sbin.kea-dhcp4"
|
||||||
|
|
||||||
|
# Comprobar si las líneas existen
|
||||||
|
if grep -q "/etc/kea/ r," "$APPARMOR_PROFILE" && grep -q "/etc/kea/** r," "$APPARMOR_PROFILE"; then
|
||||||
|
echo "Modificando permisos en $APPARMOR_PROFILE..."
|
||||||
|
|
||||||
|
# Modificar las líneas /etc/kea/ r, y /etc/kea/** r, añadiendo w
|
||||||
|
sed -i 's#/etc/kea/ r,#/etc/kea/ rw,#g' "$APPARMOR_PROFILE"
|
||||||
|
sed -i 's#/etc/kea/\*\* r,#/etc/kea/** rw,#g' "$APPARMOR_PROFILE"
|
||||||
|
|
||||||
|
echo "Permisos de escritura añadidos correctamente a $APPARMOR_PROFILE."
|
||||||
|
else
|
||||||
|
echo "Las líneas no fueron encontradas o ya están modificadas."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Recargar el perfil de AppArmor para aplicar los cambios
|
||||||
|
echo "Recargando el perfil de AppArmor para kea-dhcp4..."
|
||||||
|
sudo apparmor_parser -r "$APPARMOR_PROFILE"
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "El perfil de AppArmor se recargó correctamente."
|
||||||
|
else
|
||||||
|
echo "Error al recargar el perfil de AppArmor."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Función para configurar Nginx
|
||||||
|
setup_nginx() {
|
||||||
|
local path_opengnsys_base="$1"
|
||||||
|
local public_dir="$path_opengnsys_base/public"
|
||||||
|
#ip_address_server=$(get_ip_address "$DEFAULTDEV")
|
||||||
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||||
|
echo "Error: El archivo de configuración no se encontró."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local path_opengnsys_base="$1"
|
ip_address_server=$(jq -r '.ogDhcpIP' "$CONFIG_FILE")
|
||||||
local path_apache2_confd="$2"
|
|
||||||
local OGHDPCDIR="${path_opengnsys_base}/public"
|
|
||||||
local sockfile
|
|
||||||
|
|
||||||
if [ ! -d "$path_apache2_confd" ]; then
|
if [[ -z "$ip_address_server" ]]; then
|
||||||
errorAndLog "${FUNCNAME}(): path to apache2 conf.d can not found, verify your server installation"
|
echo "Error: No se pudo obtener la dirección IP del servidor desde el archivo de configuración."
|
||||||
return 1
|
exit 1
|
||||||
|
fi
|
||||||
|
php_version=$(get_php_fpm_version)
|
||||||
|
|
||||||
|
if [[ -z "$php_version" ]]; then
|
||||||
|
echo "Error: No se pudo obtener la versión de PHP."
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$path_apache2_confd/{sites-available,sites-enabled}"
|
# Leer y modificar la plantilla de configuración de nginx
|
||||||
|
if [[ ! -f "$NGINX_TEMPLATE" ]]; then
|
||||||
echoAndLog "${FUNCNAME}(): creating apache2 config file.."
|
echo "Error: La plantilla de Nginx no se encontró."
|
||||||
|
exit 1
|
||||||
# Activar PHP-FPM.
|
|
||||||
echoAndLog "${FUNCNAME}(): configuring PHP-FPM"
|
|
||||||
service="$PHPFPMSERV"
|
|
||||||
$ENABLESERVICE; $STARTSERVICE
|
|
||||||
sockfile=$(find /run/php -name "php*.sock" -type s -print 2>/dev/null | tail -1)
|
|
||||||
|
|
||||||
# Activar módulos de Apache.
|
|
||||||
$APACHEENABLEMODS
|
|
||||||
|
|
||||||
# Generar configuración de consola web a partir del archivo de plantilla.
|
|
||||||
if [ -n "$sockfile" ]; then
|
|
||||||
sed -e "s,OGHDPCDIR,$OGHDPCDIR,g" \
|
|
||||||
-e "s,proxy:fcgi:.*,proxy:unix:${sockfile%% *}|fcgi://localhost\",g" \
|
|
||||||
"$WORKDIR/ogdhcp/etc/apache.conf.tmpl" > "$path_apache2_confd/$APACHESITESDIR/${APACHEOGSITE}.conf"
|
|
||||||
else
|
|
||||||
sed -e "s,OGHDPCDIR,$OGHDPCDIR,g" \
|
|
||||||
"$WORKDIR/ogdhcp/server/etc/apache.conf.tmpl" > "$path_apache2_confd/$APACHESITESDIR/${APACHEOGSITE}.conf"
|
|
||||||
fi
|
fi
|
||||||
$APACHEENABLEOG
|
|
||||||
if [ $? -ne 0 ]; then
|
nginx_content=$(<"$NGINX_TEMPLATE")
|
||||||
errorAndLog "${FUNCNAME}(): config file can't be linked to apache conf, verify your server installation"
|
nginx_content="${nginx_content//__SERVERIP__/$ip_address_server}"
|
||||||
return 1
|
nginx_content="${nginx_content//__PHPVERSION__/$php_version}"
|
||||||
fi
|
nginx_content="${nginx_content//__PUBLICDIR__/$public_dir}"
|
||||||
echoAndLog "${FUNCNAME}(): config file created and linked, restarting apache daemon"
|
|
||||||
service="$APACHESERV"
|
# Crear el archivo de configuración de Nginx
|
||||||
$ENABLESERVICE; $STARTSERVICE
|
echo "$nginx_content" > "$NGINX_OUTPUT"
|
||||||
return 0
|
echo "Archivo de configuración de Nginx creado en $NGINX_OUTPUT."
|
||||||
|
|
||||||
|
# Crear el enlace simbólico
|
||||||
|
ln -sf "$NGINX_OUTPUT" /etc/nginx/sites-enabled/ogdhcp.conf
|
||||||
|
echo "Enlace simbólico creado en /etc/nginx/sites-enabled/ogdhcp.conf."
|
||||||
|
|
||||||
|
# Modificar nginx.conf para ejecutar como opengnsys
|
||||||
|
sed -i 's/user www-data;/user opengnsys;/g' "$NGINX_CONF_PATH"
|
||||||
|
echo "Nginx configurado para ejecutarse como opengnsys."
|
||||||
|
|
||||||
|
# Reiniciar Nginx
|
||||||
|
systemctl restart nginx.service
|
||||||
|
echo "Servicio Nginx reiniciado."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Función para modificar el archivo de configuración PHP-FPM
|
||||||
|
modify_php_fpm_config() {
|
||||||
|
php_version=$(get_php_fpm_version)
|
||||||
|
|
||||||
|
if [[ -z "$php_version" ]]; then
|
||||||
|
echo "Error: No se pudo obtener la versión de PHP."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
php_fpm_conf_path="/etc/php/$php_version/fpm/pool.d/www.conf"
|
||||||
|
new_fpm_conf_path="/etc/php/$php_version/fpm/pool.d/ogdhcp.conf"
|
||||||
|
socket_path="/run/php/php$php_version-fpm-ogdhcp.sock"
|
||||||
|
|
||||||
|
# Verificar si el archivo new_fpm_conf_path ya existe
|
||||||
|
if [[ -f "$new_fpm_conf_path" ]]; then
|
||||||
|
echo "El archivo $new_fpm_conf_path ya existe. No se realizarán modificaciones."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copiar el archivo www.conf a opengnsys.conf
|
||||||
|
cp "$php_fpm_conf_path" "$new_fpm_conf_path"
|
||||||
|
|
||||||
|
# Modificar el archivo ogdhcp.conf
|
||||||
|
sed -i 's/\[www\]/[ogdhcp]/g' "$new_fpm_conf_path"
|
||||||
|
sed -i 's/user = www-data/user = opengnsys/g' "$new_fpm_conf_path"
|
||||||
|
sed -i 's/group = www-data/group = opengnsys/g' "$new_fpm_conf_path"
|
||||||
|
sed -i "s|listen =.*|listen = $socket_path|g" "$new_fpm_conf_path"
|
||||||
|
sed -i 's/listen.owner = www-data/listen.owner = opengnsys/g' "$new_fpm_conf_path"
|
||||||
|
sed -i 's/listen.group = www-data/listen.group = opengnsys/g' "$new_fpm_conf_path"
|
||||||
|
|
||||||
|
# Reiniciar PHP-FPM
|
||||||
|
systemctl restart php"$php_version"-fpm.service
|
||||||
|
echo "PHP-FPM reiniciado."
|
||||||
|
|
||||||
|
# Verificar la creación del socket
|
||||||
|
if [[ -S "$socket_path" ]]; then
|
||||||
|
echo "Socket PHP-FPM $socket_path creado correctamente."
|
||||||
|
else
|
||||||
|
echo "Error: El socket PHP-FPM $socket_path no se ha creado."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
configure_kea() {
|
||||||
|
|
||||||
|
# Verificar si jq está instalado
|
||||||
|
if ! command -v jq &> /dev/null; then
|
||||||
|
echo "jq no está instalado. Por favor, instala jq para continuar."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verificar si el archivo de configuración existe
|
||||||
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
|
echo "El archivo $CONFIG_FILE no se encuentra. Asegúrate de que esté disponible antes de la instalación."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Leer los parámetros del archivo JSON usando jq
|
||||||
|
INTERFACES=$(jq -r '.interfaces[]' "$CONFIG_FILE")
|
||||||
|
OGBOOT_IP=$(jq -r '.ogbootIP' "$CONFIG_FILE")
|
||||||
|
|
||||||
|
# Crear la configuración mínima de Kea DHCP
|
||||||
|
KEA_CONFIG="/etc/kea/kea-dhcp4.conf"
|
||||||
|
|
||||||
|
# Hacer una copia de seguridad del archivo kea-dhcp4.conf si ya existe
|
||||||
|
if [ -f "$KEA_CONFIG" ]; then
|
||||||
|
cp "$KEA_CONFIG" "$KEA_CONFIG.backup"
|
||||||
|
echo "Se ha creado una copia de seguridad del archivo de configuración actual en $KEA_CONFIG.backup"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generar la configuración mínima para Kea DHCP
|
||||||
|
cat > "$KEA_CONFIG" << EOL
|
||||||
|
{
|
||||||
|
"Dhcp4": {
|
||||||
|
"interfaces-config": {
|
||||||
|
"interfaces": [ $(
|
||||||
|
for interface in $INTERFACES; do
|
||||||
|
echo "\"$interface\""
|
||||||
|
done | paste -sd "," -
|
||||||
|
) ]
|
||||||
|
},
|
||||||
|
"client-classes": [
|
||||||
|
{
|
||||||
|
"name": "UEFI-64",
|
||||||
|
"test": "not substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'",
|
||||||
|
"boot-file-name": "ipxe.efi",
|
||||||
|
"next-server": "$OGBOOT_IP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Legacy",
|
||||||
|
"test": "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'",
|
||||||
|
"boot-file-name": "undionly.kpxe",
|
||||||
|
"next-server": "$OGBOOT_IP"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"control-socket": {
|
||||||
|
"socket-name": "/run/kea/kea4-ctrl-socket",
|
||||||
|
"socket-type": "unix"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOL
|
||||||
|
|
||||||
|
echo "Se ha generado la configuración mínima de Kea DHCP en $KEA_CONFIG"
|
||||||
|
# Reiniciar el servicio de Kea DHCP y verificar si se reinicia correctamente
|
||||||
|
echo "Reiniciando el servicio Kea DHCP..."
|
||||||
|
sudo systemctl restart kea-dhcp4-server.service
|
||||||
|
|
||||||
|
# Comprobar el estado del servicio Kea DHCP
|
||||||
|
if systemctl is-active --quiet kea-dhcp4-server.service; then
|
||||||
|
echo "Kea DHCP reiniciado correctamente."
|
||||||
|
else
|
||||||
|
echo "Error al reiniciar Kea DHCP."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
####### Algunas funciones útiles de propósito general:
|
####### Algunas funciones útiles de propósito general:
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
@ -459,7 +589,6 @@ mkdir -p $WORKDIR
|
||||||
pushd $WORKDIR
|
pushd $WORKDIR
|
||||||
|
|
||||||
checkDependencies
|
checkDependencies
|
||||||
install_kea
|
|
||||||
# Si es necesario, descarga el repositorio de código en directorio temporal
|
# Si es necesario, descarga el repositorio de código en directorio temporal
|
||||||
if [ $REMOTE -eq 1 ]; then
|
if [ $REMOTE -eq 1 ]; then
|
||||||
downloadCode $GIT_REPO
|
downloadCode $GIT_REPO
|
||||||
|
@ -492,18 +621,39 @@ if [ $? -ne 0 ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
downloadComposer
|
|
||||||
|
|
||||||
runComposer
|
comment_auth_kea
|
||||||
install_swagger_ui
|
|
||||||
# Creando configuración de Apache.
|
|
||||||
installWebConsoleApacheConf $INSTALL_TARGET $APACHECFGDIR
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
errorAndLog "Error configuring Apache for OpenGnsys Admin"
|
errorAndLog "Error while commenting auth block!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sudo apt-get update
|
|
||||||
|
runComposer ${INSTALL_TARGET}
|
||||||
|
setup_nginx $INSTALL_TARGET
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
errorAndLog "Error configuring Nginx for OpenGnsys Admin"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
modify_php_fpm_config
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
errorAndLog "Error configuring PHP-FPM for OpenGnsys Admin"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
add_write_permission_apparmor
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
errorAndLog "Error adding write permission to AppArmor profile"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
configure_kea
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
errorAndLog "Error configuring Kea DHCP initial configuration"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
# install_kea
|
# install_kea
|
||||||
# install_php
|
# install_php
|
||||||
# install_composer
|
# install_composer
|
||||||
|
@ -512,4 +662,4 @@ sudo apt-get update
|
||||||
# Ahora puedes clonar e instalar el componente ogDhcp
|
# Ahora puedes clonar e instalar el componente ogDhcp
|
||||||
# git clone <URL del repositorio de ogDhcp>
|
# git clone <URL del repositorio de ogDhcp>
|
||||||
# cd <directorio de ogDhcp>
|
# cd <directorio de ogDhcp>
|
||||||
# composer install
|
# composer install
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -x
|
||||||
|
URL_REPO="https://ognproject.evlt.uma.es/gitea/opengnsys/ogdhcp.git"
|
||||||
|
BRANCH=${OGDHCP_BRANCH:-"main"}
|
||||||
|
DOWNLOADDIR=${OGDHCP_DOWNLOADDIR:-"/tmp/ogdhcp"}
|
||||||
|
apt install -y git vim
|
||||||
|
git config --global http.sslVerify false
|
||||||
|
git clone -b $BRANCH $URL_REPO $DOWNLOADDIR
|
||||||
|
cd $DOWNLOADDIR/installer
|
||||||
|
|
||||||
|
ogBoot_ServerIP=${1:-"172.17.8.82"}
|
||||||
|
ogDhcp_ServerIP=${2:-"172.17.8.37"}
|
||||||
|
ogDhcp_Dir=${3:-"/opt/opengnsys/ogdhcp"}
|
||||||
|
|
||||||
|
|
||||||
|
cat > config_ogdhcp.json <<EOF
|
||||||
|
{
|
||||||
|
"interfaces": ["eth0", "eth1"],
|
||||||
|
"ogbootIP": "$ogBoot_ServerIP",
|
||||||
|
"ogDhcpIP": "$ogDhcp_ServerIP",
|
||||||
|
"ogDhcp_Dir": "$ogDhcp_Dir"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
chmod 755 ogdhcp_installer.sh
|
||||||
|
./ogdhcp_installer.sh
|
File diff suppressed because it is too large
Load Diff
|
@ -28,7 +28,8 @@ class CurlKeaService
|
||||||
} else {
|
} else {
|
||||||
return "Error: Comando no válido";
|
return "Error: Comando no válido";
|
||||||
}
|
}
|
||||||
if (($command == 'config-set' || $command == 'config-write') && $create_backup) {
|
//f (($command == 'config-set' || $command == 'config-write') && $create_backup) {
|
||||||
|
if ($command == 'config-set' && $create_backup) {
|
||||||
$this->backupConfig();
|
$this->backupConfig();
|
||||||
}
|
}
|
||||||
$jsonData = json_encode($requestData);
|
$jsonData = json_encode($requestData);
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
node {
|
||||||
|
label 'jenkins-slave'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options {
|
||||||
|
// Deshabilita ejecuciones concurrentes
|
||||||
|
disableConcurrentBuilds()
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
ESXI_PASS = credentials('VI_PASSWORD')
|
||||||
|
QINDEL_PASS = credentials('jenkins-user-slave-password')
|
||||||
|
PATH = "/home/qindel/bin/ovftool:${env.PATH}"
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Prepare environment') {
|
||||||
|
steps {
|
||||||
|
dir ('tests/API-dhcp') {
|
||||||
|
echo "Install vagrant plugin"
|
||||||
|
sh '''
|
||||||
|
if ! vagrant plugin list | grep -q vagrant-vmware-esxi; then
|
||||||
|
echo "Vagrant plugin vagrant-vmware-esxi not found. Installing..."
|
||||||
|
vagrant plugin install vagrant-vmware-esxi
|
||||||
|
else
|
||||||
|
echo "Vagrant plugin vagrant-vmware-esxi is already installed."
|
||||||
|
fi
|
||||||
|
'''
|
||||||
|
echo "Deploy API server for DHCP with Vagrant"
|
||||||
|
sh 'vagrant up --provider=vmware_esxi --provision'
|
||||||
|
echo 'Create Python venv to work with robotframework'
|
||||||
|
sh '''
|
||||||
|
python3 -m venv robotframework
|
||||||
|
. robotframework/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
'''
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check Installation') {
|
||||||
|
steps {
|
||||||
|
dir ('tests/API-dhcp') {
|
||||||
|
echo "Get IP of API server for DHCP with Vagrant"
|
||||||
|
sh '''
|
||||||
|
set -e
|
||||||
|
new_ip=$(vagrant ssh -c "hostname -I" | tr -d '\r\n' | sed 's/^[^0-9]*//' | sed 's/[[:space:]]*$//')
|
||||||
|
echo $new_ip > ip.txt
|
||||||
|
echo "$QINDEL_PASS" | sudo -S bash -c "echo '$new_ip api-test' >> /etc/hosts"
|
||||||
|
echo "IP: $new_ip"
|
||||||
|
curl -X 'GET' "http://$new_ip/ogdhcp/v1/subnets" -H 'accept: /'
|
||||||
|
'''
|
||||||
|
script {
|
||||||
|
def new_ip = readFile('ip.txt').trim()
|
||||||
|
env.NEW_IP = new_ip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Run API tests') {
|
||||||
|
steps {
|
||||||
|
dir ('tests/API-dhcp') {
|
||||||
|
|
||||||
|
echo 'Running API tests'
|
||||||
|
|
||||||
|
sh '''
|
||||||
|
. robotframework/bin/activate
|
||||||
|
robot --variable BASE_URL:http://${NEW_IP}/ogdhcp/v1 -d results/ robot/
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
// Aquí incluirías los comandos para ejecutar tus pruebas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
success {
|
||||||
|
// Si el trabajo ha sido exitoso, destruir la máquina de Vagrant
|
||||||
|
echo "El trabajo ha finalizado con éxito. Destruyendo máquina Vagrant..."
|
||||||
|
dir ('tests/API-dhcp') {
|
||||||
|
sh 'vagrant destroy -f'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
always {
|
||||||
|
// Recoger los resultados de los tests
|
||||||
|
dir ('tests/API-dhcp') {
|
||||||
|
robot outputPath: 'results',
|
||||||
|
outputFileName: 'output.xml',
|
||||||
|
logFileName: 'log.html',
|
||||||
|
reportFileName: 'report.html',
|
||||||
|
passThreshold: 100.0,
|
||||||
|
unstableThreshold: 75.0
|
||||||
|
}
|
||||||
|
// Siempre se ejecutará, independientemente del resultado
|
||||||
|
script {
|
||||||
|
// Elimina la entrada del /etc/hosts
|
||||||
|
sh "echo '$QINDEL_PASS' | sudo -S sed -i '/api-test/d' /etc/hosts"
|
||||||
|
def recipientEmail = env.BUILD_USER_EMAIL ?: getCommitterEmail()
|
||||||
|
|
||||||
|
mail to: recipientEmail,
|
||||||
|
subject: "Jenkins Job Completed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
|
||||||
|
body: """
|
||||||
|
El job '${env.JOB_NAME}' con número de build ${env.BUILD_NUMBER} ha finalizado.
|
||||||
|
|
||||||
|
Estado: ${currentBuild.currentResult}
|
||||||
|
|
||||||
|
Revisa los detalles del build en: ${env.BUILD_URL}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getCommitterEmail() {
|
||||||
|
return sh(
|
||||||
|
script: 'git log -1 --pretty=format:"%ae"',
|
||||||
|
returnStdout: true
|
||||||
|
).trim()
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
#
|
||||||
|
# Fully documented Vagrantfile available
|
||||||
|
# in the wiki: https://github.com/josenk/vagrant-vmware-esxi/wiki
|
||||||
|
|
||||||
|
$script = <<SCRIPT
|
||||||
|
SERVER_NAME=localhost
|
||||||
|
echo "Provisioning with shell script..."
|
||||||
|
cd /vagrant/installer
|
||||||
|
chmod +x ogdhcp_installer.sh && ./ogdhcp_installer.sh
|
||||||
|
SCRIPT
|
||||||
|
|
||||||
|
Vagrant.configure('2') do |config|
|
||||||
|
config.vm.box = 'dummy'
|
||||||
|
VM_TEMPLATE = 'template-ubuntu24'
|
||||||
|
# Use rsync and NFS synced folders. (or use the option to disable them)
|
||||||
|
# https://www.vagrantup.com/docs/synced-folders/
|
||||||
|
#config.vm.synced_folder('.', '/vagrant', type: 'rsync')
|
||||||
|
config.vm.synced_folder('../../', '/vagrant', type: 'rsync')
|
||||||
|
|
||||||
|
# Vagrant can configure additional network interfaces using a static IP or
|
||||||
|
# DHCP. Use public_network or private_network to manually set a static IP and
|
||||||
|
# optionally netmask. ESXi doesn't use the concept of public or private
|
||||||
|
# networks so both are valid here. The primary network interface is considered the
|
||||||
|
# "vagrant management" interface and cannot be changed and this plugin
|
||||||
|
# supports 10 NICS, so you can specify 9 entries here!
|
||||||
|
#
|
||||||
|
# https://www.vagrantup.com/docs/networking/public_network.html
|
||||||
|
# https://www.vagrantup.com/docs/networking/private_network.html
|
||||||
|
#
|
||||||
|
# *** Invalid settings could cause 'vagrant up' to fail ***
|
||||||
|
#config.vm.network 'private_network', ip: '192.168.10.170', netmask: '255.255.255.0'
|
||||||
|
#config.vm.network 'private_network', ip: '192.168.11.170'
|
||||||
|
#config.vm.network 'public_network', ip: '192.168.12.170'
|
||||||
|
|
||||||
|
#
|
||||||
|
# Provider (esxi) settings
|
||||||
|
#
|
||||||
|
config.vm.provision 'shell', inline: $script
|
||||||
|
config.vm.provider :vmware_esxi do |esxi|
|
||||||
|
|
||||||
|
# REQUIRED! ESXi hostname/IP
|
||||||
|
esxi.esxi_hostname = 'esxi-jenkins.evlt.uma.es'
|
||||||
|
|
||||||
|
# ESXi username
|
||||||
|
esxi.esxi_username = 'root'
|
||||||
|
|
||||||
|
# IMPORTANT! Set ESXi password.
|
||||||
|
# 1) 'prompt:'
|
||||||
|
# 2) 'file:' or 'file:my_secret_file'
|
||||||
|
# 3) 'env:' or 'env:my_secret_env_var'
|
||||||
|
# 4) 'key:' or key:~/.ssh/some_ssh_private_key'
|
||||||
|
# 5) or esxi.esxi_password = 'my_esxi_password'
|
||||||
|
#
|
||||||
|
esxi.esxi_password = 'env:ESXI_PASS'
|
||||||
|
|
||||||
|
# SSH port.
|
||||||
|
#esxi.esxi_hostport = 22
|
||||||
|
|
||||||
|
# HIGHLY RECOMMENDED! ESXi Virtual Network
|
||||||
|
# You should specify an ESXi Virtual Network! If it's not specified, the
|
||||||
|
# default is to use the first found. You can specify up to 10 virtual
|
||||||
|
# networks using an array format.
|
||||||
|
esxi.esxi_virtual_network = ['vLan_742']
|
||||||
|
|
||||||
|
# OPTIONAL. Specify a Disk Store
|
||||||
|
#esxi.esxi_disk_store = 'DS_001'
|
||||||
|
|
||||||
|
# OPTIONAL. Resource Pool
|
||||||
|
# Vagrant will NOT create a Resource pool it for you.
|
||||||
|
esxi.esxi_resource_pool = '/'
|
||||||
|
|
||||||
|
# Optional. Specify a VM to clone instead of uploading a box.
|
||||||
|
# Vagrant can use any stopped VM as the source 'box'. The VM must be
|
||||||
|
# registered, stopped and must have the vagrant insecure ssh key installed.
|
||||||
|
# If the VM is stored in a resource pool, it must be specified.
|
||||||
|
# See wiki: https://github.com/josenk/vagrant-vmware-esxi/wiki/How-to-clone_from_vm
|
||||||
|
esxi.clone_from_vm = VM_TEMPLATE
|
||||||
|
|
||||||
|
# OPTIONAL. Guest VM name to use.
|
||||||
|
# The Default will be automatically generated.
|
||||||
|
#esxi.guest_name = 'Custom-Guest-VM_Name'
|
||||||
|
|
||||||
|
# OPTIONAL. When automatically naming VMs, use this prefix.
|
||||||
|
#esxi.guest_name_prefix = 'V-'
|
||||||
|
|
||||||
|
# OPTIONAL. Set the guest username login. The default is 'vagrant'.
|
||||||
|
#esxi.guest_username = 'vagrant'
|
||||||
|
|
||||||
|
# OPTIONAL. Memory size override
|
||||||
|
#esxi.guest_memsize = '2048'
|
||||||
|
|
||||||
|
# OPTIONAL. Virtual CPUs override
|
||||||
|
#esxi.guest_numvcpus = '2'
|
||||||
|
|
||||||
|
# OPTIONAL & RISKY. Specify up to 10 MAC addresses
|
||||||
|
# The default is ovftool to automatically generate a MAC address.
|
||||||
|
# You can specify an array of MAC addresses using upper or lower case,
|
||||||
|
# separated by colons ':'.
|
||||||
|
#esxi.guest_mac_address = ['00:50:56:aa:bb:cc', '00:50:56:01:01:01','00:50:56:02:02:02','00:50:56:BE:AF:01' ]
|
||||||
|
|
||||||
|
# OPTIONAL & RISKY. Specify a guest_nic_type
|
||||||
|
# The validated list of guest_nic_types are 'e1000', 'e1000e', 'vmxnet',
|
||||||
|
# 'vmxnet2', 'vmxnet3', 'Vlance', and 'Flexible'.
|
||||||
|
#esxi.guest_nic_type = 'e1000'
|
||||||
|
|
||||||
|
# OPTIONAL. Specify a disk type.
|
||||||
|
# If unspecified, it will be set to 'thin'. Otherwise, you can set to
|
||||||
|
# 'thin', 'thick', or 'eagerzeroedthick'
|
||||||
|
#esxi.guest_disk_type = 'thick'
|
||||||
|
|
||||||
|
# OPTIONAL. Boot disk size.
|
||||||
|
# If unspecified, the boot disk size will be the same as the original
|
||||||
|
# box. You can specify a larger boot disk size in GB. The extra disk space
|
||||||
|
# will NOT automatically be available to your OS. You will need to
|
||||||
|
# create or modify partitions, LVM and/or filesystems.
|
||||||
|
#esxi.guest_boot_disk_size = 50
|
||||||
|
|
||||||
|
# OPTIONAL. Create additional storage for guests.
|
||||||
|
# You can specify an array of up to 13 virtual disk sizes (in GB) that you
|
||||||
|
# would like the provider to create once the guest has been created. You
|
||||||
|
# can optionally specify the size and datastore using a hash.
|
||||||
|
#esxi.guest_storage = [ 10, 20, { size: 30, datastore: 'datastore1' } ]
|
||||||
|
|
||||||
|
# OPTIONAL. specify snapshot options.
|
||||||
|
#esxi.guest_snapshot_includememory = 'true'
|
||||||
|
#esxi.guest_snapshot_quiesced = 'true'
|
||||||
|
|
||||||
|
# RISKY. guest_guestos
|
||||||
|
# https://github.com/josenk/vagrant-vmware-esxi/wiki/VMware-ESXi-6.5-guestOS-types
|
||||||
|
#esxi.guest_guestos = 'centos-64'
|
||||||
|
|
||||||
|
# OPTIONAL. guest_virtualhw_version
|
||||||
|
# ESXi 6.7 supports these versions. 4,7,8,9,10,11,12,13 & 14.
|
||||||
|
#esxi.guest_virtualhw_version = '9'
|
||||||
|
|
||||||
|
# OPTIONAL. Guest Autostart
|
||||||
|
# Guest VM will autostart when esxi host is booted. 'true' or 'false'(default)
|
||||||
|
#esxi.guest_autostart = 'false'
|
||||||
|
|
||||||
|
# RISKY. guest_custom_vmx_settings
|
||||||
|
#esxi.guest_custom_vmx_settings = [['vhv.enable','TRUE'], ['floppy0.present','TRUE']]
|
||||||
|
|
||||||
|
# OPTIONAL. local_lax
|
||||||
|
#esxi.local_lax = 'true'
|
||||||
|
|
||||||
|
# OPTIONAL. Guest IP Caching
|
||||||
|
#esxi.local_use_ip_cache = 'True'
|
||||||
|
|
||||||
|
# DANGEROUS! Allow Overwrite
|
||||||
|
# If unspecified, the default is to produce an error if overwriting
|
||||||
|
# VMs and packages.
|
||||||
|
#esxi.local_allow_overwrite = 'True'
|
||||||
|
|
||||||
|
# Advanced Users.
|
||||||
|
# If set to 'True', all WARNINGS will produce a FAILURE and Vagrant will stop.
|
||||||
|
#esxi.local_failonwarning = 'True'
|
||||||
|
|
||||||
|
# Plugin debug output.
|
||||||
|
# Please send any bug reports with this debug output...
|
||||||
|
#esxi.debug = 'true'
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,374 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from flask import Flask, jsonify, request
|
||||||
|
import ipaddress
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_to_cidr(address, mask):
|
||||||
|
"""
|
||||||
|
Convert an IP address and subnet mask to CIDR notation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
address (str): The IP address in dotted decimal format (e.g., '192.168.1.1').
|
||||||
|
mask (str): The subnet mask in dotted decimal format (e.g., '255.255.255.0').
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The CIDR notation of the network (e.g., '192.168.1.1/24').
|
||||||
|
None: If there is an error in conversion, returns None and prints an error message.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If the provided address or mask is invalid.
|
||||||
|
"""
|
||||||
|
# Convertir dirección y máscara a formato CIDR
|
||||||
|
try:
|
||||||
|
# Convertir la máscara de red de formato largo a formato corto
|
||||||
|
# Cada octeto se convierte a su representación binaria y se cuenta el número de bits '1'
|
||||||
|
cidr_mask = ipaddress.IPv4Network(f"0.0.0.0/{mask}").prefixlen
|
||||||
|
red_objeto = f"{address}/{cidr_mask}"
|
||||||
|
return red_objeto
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"Error al convertir a CIDR: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
subnets_collection = [{"id": 1,"subnet": "192.168.1.0/24", "next-server": "192.168.1.1", "boot-file-name": "pxelinux.0", "reservations": [] }]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Endpoint GET /ogdhcp/v1/status
|
||||||
|
"""
|
||||||
|
Endpoint to get the status of the DHCP service.
|
||||||
|
|
||||||
|
This endpoint returns a JSON response with the current status of the DHCP service,
|
||||||
|
including disk usage, subnets configuration, and the status of various services.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: A JSON response with the following structure:
|
||||||
|
"total": str, # Total disk space
|
||||||
|
"used": str, # Used disk space
|
||||||
|
"available": str, # Available disk space
|
||||||
|
"percentage": str # Percentage of disk space used
|
||||||
|
"id": int, # Subnet ID
|
||||||
|
"subnet": str, # Subnet address
|
||||||
|
"pools": [
|
||||||
|
"pool": str # IP address pool range
|
||||||
|
"reservations": [
|
||||||
|
"ip-address": str, # Reserved IP address
|
||||||
|
"hw-address": str # Hardware address associated with the reservation
|
||||||
|
]
|
||||||
|
...
|
||||||
|
"kea-ctrl-agent": str, # Status of kea-ctrl-agent service
|
||||||
|
"kea-dhcp4": str, # Status of kea-dhcp4 service
|
||||||
|
"nginx": str # Status of nginx service
|
||||||
|
HTTP Status Code:
|
||||||
|
200: If the request was successful.
|
||||||
|
"""
|
||||||
|
@app.route('/ogdhcp/v1/status', methods=['GET'])
|
||||||
|
def get_status():
|
||||||
|
# Simular respuesta de éxito
|
||||||
|
return jsonify({
|
||||||
|
"success": "Mensaje éxito",
|
||||||
|
"message": {
|
||||||
|
"disk_usage": {
|
||||||
|
"total": "20G",
|
||||||
|
"used": "15G",
|
||||||
|
"available": "5G",
|
||||||
|
"percentage": "75%"
|
||||||
|
},
|
||||||
|
"subnets": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"subnet": "192.168.1.0/24",
|
||||||
|
"pools": [{"pool": "192.168.1.10-192.168.1.100"}],
|
||||||
|
"reservations": [{"ip-address": "192.168.1.20", "hw-address": "00:0c:29:6b:5e:71"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"subnet": "10.0.0.0/24",
|
||||||
|
"pools": [{"pool": "10.0.0.10-10.0.0.100"}],
|
||||||
|
"reservations": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services_status": {
|
||||||
|
"kea-ctrl-agent": "active",
|
||||||
|
"kea-dhcp4": "active",
|
||||||
|
"nginx": "active"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
# Endpoint GET /ogdhcp/v1/subnets
|
||||||
|
|
||||||
|
@app.route('/ogdhcp/v1/subnets', methods=['GET'])
|
||||||
|
def get_subnets():
|
||||||
|
# Simular respuesta de éxito
|
||||||
|
return jsonify({
|
||||||
|
"success": "Subredes obtenidas correctamente",
|
||||||
|
"message": subnets_collection
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
# Endpoint POST /ogdhcp/v1/subnets
|
||||||
|
"""
|
||||||
|
Create a new subnet.
|
||||||
|
Endpoint: /ogdhcp/v1/subnets
|
||||||
|
Method: POST
|
||||||
|
Request Body (JSON):
|
||||||
|
{
|
||||||
|
"id": <str>, # Unique identifier for the subnet
|
||||||
|
"address": <str>, # IP address of the subnet
|
||||||
|
"mask": <str>, # Subnet mask
|
||||||
|
"nextServer": <str>, # (Optional) Next server IP address
|
||||||
|
"bootFileName": <str> # (Optional) Boot file name
|
||||||
|
}
|
||||||
|
Responses:
|
||||||
|
200 OK:
|
||||||
|
{
|
||||||
|
"message": <new_subnet>
|
||||||
|
}
|
||||||
|
400 Bad Request:
|
||||||
|
- "Invalid JSON"
|
||||||
|
- "Missing 'id', 'address' or 'mask' key"
|
||||||
|
- {
|
||||||
|
"error": "Error: La subred con el id '<id>' ya existe."
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
"error": "Error: La subred con la dirección '<subnet>' ya existe."
|
||||||
|
}
|
||||||
|
- "Invalid data format"
|
||||||
|
Description:
|
||||||
|
This endpoint allows the creation of a new subnet. It expects a JSON payload with the subnet details.
|
||||||
|
The function checks if the provided JSON is valid and contains the required keys ('id', 'address', 'mask').
|
||||||
|
It also ensures that the subnet ID and subnet address do not already exist in the collection.
|
||||||
|
If the validation passes, the new subnet is added to the collection and a success response is returned.
|
||||||
|
"""
|
||||||
|
@app.route('/ogdhcp/v1/subnets', methods=['POST'])
|
||||||
|
def create_subnet():
|
||||||
|
try:
|
||||||
|
new_subnet = json.loads(request.data)
|
||||||
|
new_subnet["next-server"] = new_subnet.get("nextServer")
|
||||||
|
new_subnet["boot-file-name"] = new_subnet.get("bootFileName")
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return "Invalid JSON", 400
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if isinstance(new_subnet, dict):
|
||||||
|
if "id" in new_subnet and "address" in new_subnet and "mask" in new_subnet:
|
||||||
|
new_subnet_cidr = convert_to_cidr(new_subnet["address"], new_subnet["mask"])
|
||||||
|
# Comprobar si el id ya existe en la colección
|
||||||
|
for subnet in subnets_collection:
|
||||||
|
if subnet.get("id") == new_subnet["id"]:
|
||||||
|
return jsonify({
|
||||||
|
"error": f"Error: La subred con el id '{new_subnet['id']}' ya existe."
|
||||||
|
}), 400
|
||||||
|
if subnet.get("subnet") == new_subnet_cidr:
|
||||||
|
return jsonify({
|
||||||
|
"error": f"Error: La subred con la dirección '{new_subnet_cidr}' ya existe."
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
# Si el id no existe, continuar con la creación de la subred
|
||||||
|
new_subnet["subnet"] = convert_to_cidr(new_subnet["address"], new_subnet["mask"])
|
||||||
|
subnets_collection.append(new_subnet)
|
||||||
|
return jsonify({
|
||||||
|
"success": "Subred agregada correctamente",
|
||||||
|
"message": new_subnet
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
else:
|
||||||
|
return "Missing 'id', 'address' or 'mask' key", 400
|
||||||
|
else:
|
||||||
|
return "Invalid data format", 400
|
||||||
|
|
||||||
|
|
||||||
|
# Endpoint DELETE /ogdhcp/v1/subnets/<subnetId>
|
||||||
|
"""
|
||||||
|
Deletes a subnet from the subnets_collection based on the provided subnetId.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
subnetId (int): The ID of the subnet to be deleted.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: A JSON response indicating the success or failure of the deletion.
|
||||||
|
- If the subnet is successfully deleted, returns a JSON response with a success message and HTTP status 200.
|
||||||
|
- If the subnet with the given ID does not exist, returns a JSON response with an error message and HTTP status 404.
|
||||||
|
"""
|
||||||
|
@app.route('/ogdhcp/v1/subnets/<int:subnetId>', methods=['DELETE'])
|
||||||
|
def delete_subnet(subnetId):
|
||||||
|
subnet_to_delete = None
|
||||||
|
for subnet in subnets_collection:
|
||||||
|
if int(subnet["id"]) == subnetId:
|
||||||
|
subnet_to_delete = subnet
|
||||||
|
break
|
||||||
|
if subnet_to_delete:
|
||||||
|
print (f"Subnet Collection {subnets_collection}")
|
||||||
|
print (f"Subnet to delete {subnet_to_delete}")
|
||||||
|
subnets_collection.remove(subnet_to_delete)
|
||||||
|
print (f"Subnet Collection {subnets_collection}")
|
||||||
|
return jsonify({
|
||||||
|
"success": "Subred eliminada correctamente"
|
||||||
|
}), 200
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
"error": f"Error: La subred con el id '{subnetId}' no existe"
|
||||||
|
}), 404
|
||||||
|
|
||||||
|
# Endpoint PUT /ogdhcp/v1/subnets/<subnetId>
|
||||||
|
"""
|
||||||
|
Updates a subnet with the given subnetId based on the provided JSON data in the request.
|
||||||
|
Args:
|
||||||
|
subnetId (str): The ID of the subnet to be updated.
|
||||||
|
Returns:
|
||||||
|
Response: A JSON response indicating the success or failure of the update operation.
|
||||||
|
- On success: Returns a JSON response with a success message and the updated subnet data, with a status code of 200.
|
||||||
|
- On failure: Returns a JSON response with an error message and a status code of 400.
|
||||||
|
Raises:
|
||||||
|
json.JSONDecodeError: If the request data is not valid JSON.
|
||||||
|
The function performs the following steps:
|
||||||
|
1. Parses the JSON data from the request.
|
||||||
|
2. Validates the presence of required fields ('address' and 'mask') in the JSON data.
|
||||||
|
3. Converts the 'address' and 'mask' fields to a 'subnet' field in CIDR notation.
|
||||||
|
4. Searches for the subnet with the given subnetId in the subnets_collection.
|
||||||
|
5. Updates the subnet fields ('subnet', 'nextServer', 'bootFileName') if they are present in the JSON data.
|
||||||
|
6. Returns a success response if the subnet is found and updated.
|
||||||
|
7. Returns an error response if the subnet is not found.
|
||||||
|
"""
|
||||||
|
@app.route('/ogdhcp/v1/subnets/<int:subnetId>', methods=['PUT'])
|
||||||
|
def update_subnet(subnetId):
|
||||||
|
|
||||||
|
|
||||||
|
if subnetId == 0:
|
||||||
|
return jsonify({
|
||||||
|
"error": "Error al guardar la configuración en Kea DHCP: Unable to save configuration"
|
||||||
|
}), 400
|
||||||
|
try:
|
||||||
|
modify_data = json.loads(request.data)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return "Invalid JSON", 400
|
||||||
|
|
||||||
|
print ("Modify data", modify_data)
|
||||||
|
print ("Modify data type", type(modify_data))
|
||||||
|
if modify_data.get("address") != None and modify_data.get("mask") == None:
|
||||||
|
print ("Address", modify_data.get("address"))
|
||||||
|
return jsonify({
|
||||||
|
"error": f"Error: La máscara de red es requerida con el parametro 'address'"
|
||||||
|
}), 400
|
||||||
|
if modify_data.get("mask") != None and modify_data.get("address") == None:
|
||||||
|
return jsonify({
|
||||||
|
"error": f"Error: La dirección de red es requerida con el parametro 'mask'"
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
subnet_to_update = None
|
||||||
|
|
||||||
|
for subnet in subnets_collection:
|
||||||
|
# Casting subnet["id"] to int to avoid type mismatch
|
||||||
|
if str(subnet["id"]) == str(subnetId):
|
||||||
|
# subnet_to_update = subnet
|
||||||
|
if modify_data.get("subnet"):
|
||||||
|
subnet["subnet"] = modify_data["subnet"]
|
||||||
|
|
||||||
|
if modify_data.get("nextServer"):
|
||||||
|
subnet["next-server"] = modify_data["nextServer"]
|
||||||
|
|
||||||
|
if modify_data.get("bootFileName"):
|
||||||
|
subnet["boot-file-name"] = modify_data["bootFileName"]
|
||||||
|
subnet_to_update = subnet
|
||||||
|
break
|
||||||
|
|
||||||
|
if subnet_to_update:
|
||||||
|
# subnet_to_update.update(modify_data)
|
||||||
|
print ("Subnet to update", subnet_to_update)
|
||||||
|
return jsonify({
|
||||||
|
"success": "Subred modificada correctamente",
|
||||||
|
"message": subnet_to_update
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Si no se encuentra la subred, devolver un error
|
||||||
|
print (f"Subnet Collection {subnets_collection}")
|
||||||
|
response = jsonify({
|
||||||
|
"error": f"Error: La subred con el id '{subnetId}' no existe"
|
||||||
|
})
|
||||||
|
response.status_code = 404
|
||||||
|
response.headers["Content-Type"] = "application/json"
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
# Endpoint GET /ogdhcp/v1/subnets/<subnetId>/hosts
|
||||||
|
@app.route('/ogdhcp/v1/subnets/<int:subnetId>/hosts', methods=['GET'])
|
||||||
|
def get_hosts(subnetId):
|
||||||
|
# Simular respuesta de éxito
|
||||||
|
return jsonify({
|
||||||
|
"success": "Hosts retrieved successfully",
|
||||||
|
"message": [
|
||||||
|
{"ip-address": "192.168.1.10", "hw-address": "00:0c:29:6b:5e:71", "hostname": "host1"},
|
||||||
|
{"ip-address": "192.168.1.20", "hw-address": "00:0c:29:6b:5e:72", "hostname": "host2"}
|
||||||
|
]
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
# Endpoint POST /ogdhcp/v1/subnets/<subnetId>/hosts
|
||||||
|
@app.route('/ogdhcp/v1/subnets/<int:subnetId>/hosts', methods=['POST'])
|
||||||
|
def create_host(subnetId):
|
||||||
|
# Simular respuesta de éxito
|
||||||
|
return jsonify({
|
||||||
|
"success": "Host agregado correctamente",
|
||||||
|
"message": {
|
||||||
|
"id": 1,
|
||||||
|
"subnet": "192.168.1.0/24",
|
||||||
|
"next-server": "192.168.1.1",
|
||||||
|
"boot-file-name": "pxelinux.0",
|
||||||
|
"reservations": [
|
||||||
|
{"hostname": "pc11", "hw-address": "56:6f:c7:4f:00:4f", "ip-address": "172.30.4.11"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
# Endpoint DELETE /ogdhcp/v1/subnets/<subnetId>/hosts
|
||||||
|
@app.route('/ogdhcp/v1/subnets/<int:subnetId>/hosts', methods=['DELETE'])
|
||||||
|
def delete_host(subnetId):
|
||||||
|
# Simular respuesta de éxito
|
||||||
|
return jsonify({
|
||||||
|
"success": "Host eliminado correctamente",
|
||||||
|
"message": {
|
||||||
|
"id": 1,
|
||||||
|
"subnet": "192.168.1.0/24",
|
||||||
|
"next-server": "192.168.1.1",
|
||||||
|
"boot-file-name": "pxelinux.0",
|
||||||
|
"reservations": [
|
||||||
|
{"hostname": "host2", "hw-address": "00:0c:29:6b:5e:72", "ip-address": "172.30.4.12"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
# Endpoint PUT /ogdhcp/v1/subnets/<subnetId>/hosts
|
||||||
|
@app.route('/ogdhcp/v1/subnets/<int:subnetId>/hosts', methods=['PUT'])
|
||||||
|
def update_host(subnetId):
|
||||||
|
# Simular respuesta de éxito
|
||||||
|
return jsonify({
|
||||||
|
"success": "Host actualizado correctamente",
|
||||||
|
"message": {
|
||||||
|
"id": 1,
|
||||||
|
"subnet": "192.168.1.0/24",
|
||||||
|
"next-server": "192.168.1.1",
|
||||||
|
"boot-file-name": "pxelinux.0",
|
||||||
|
"reservations": [
|
||||||
|
{"hostname": "pc11", "hw-address": "56:6f:c7:4f:01:01", "ip-address": "192.168.1.11"},
|
||||||
|
{"hostname": "host2", "hw-address": "00:0c:29:6b:5e:72", "ip-address": "192.168.1.12"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
# Endpoint POST /ogdhcp/v1/backup
|
||||||
|
@app.route('/ogdhcp/v1/backup', methods=['POST'])
|
||||||
|
def backup_config():
|
||||||
|
# Simular respuesta de éxito
|
||||||
|
return jsonify({
|
||||||
|
"success": "Configuración cargada correctamente"
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(debug=True, port=8006)
|
|
@ -0,0 +1,2 @@
|
||||||
|
robotframework
|
||||||
|
robotframework-requests
|
|
@ -0,0 +1,215 @@
|
||||||
|
*** Settings ***
|
||||||
|
Documentation This is a basic skeleton for a Robot Framework test suite.
|
||||||
|
Library Collections
|
||||||
|
Library RequestsLibrary
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*** Variables ***
|
||||||
|
${BASE_URL} http://localhost:8006/ogdhcp/v1
|
||||||
|
${headers} Create Dictionary Content-Type application/json
|
||||||
|
${modified_subnet} Create Dictionary mask=255.255.192.0 address=192.168.1.0 nextServer=192.168.1.1 bootFileName=pxelinux.0
|
||||||
|
${wrong_subnet_data_netmask} Create Dictionary mask=333.333.333.333 address=192.168.1.0 nextServer=192.168.1.1 bootFileName=pxelinux.0
|
||||||
|
${wrong_subnet_data_address} Create Dictionary mask=255.255.255.0 address=444.168.1.0 nextServer=192.168.1.1 bootFileName=pxelinux.0
|
||||||
|
${wrong_subnet_data_server} Create Dictionary mask=255.255.255.0 address=192.168.1.0 nextServer=555.168.1.1 bootFileName=pxelinux.0
|
||||||
|
${invalid_net_id} 999
|
||||||
|
${valid_net_id} 2
|
||||||
|
${subnet_error} 0
|
||||||
|
|
||||||
|
*** Test Cases ***
|
||||||
|
Get Status of the DHCP server
|
||||||
|
[Documentation] Get status of the dhcp server and check services status
|
||||||
|
[Tags] status
|
||||||
|
${response}= GET ${BASE_URL}/status
|
||||||
|
Status Should Be 200 ${response}
|
||||||
|
Log ${response.json()}
|
||||||
|
# Convertir la respuesta a JSON usando ${response.json()}
|
||||||
|
${json}= Convert to Dictionary ${response.json()}
|
||||||
|
Dictionary Should Contain Key ${json} success
|
||||||
|
Dictionary Should Contain Key ${json} message
|
||||||
|
Should Contain ${response.json()['message']} disk_usage
|
||||||
|
Should Contain ${response.json()['message']} subnets
|
||||||
|
Should Contain ${response.json()['message']['disk_usage']} total
|
||||||
|
|
||||||
|
|
||||||
|
Get All Subnets
|
||||||
|
[Documentation] Este test verifica que la API retorna las subredes correctamente con el código 200.
|
||||||
|
[Tags] subnets
|
||||||
|
${response}= GET ${BASE_URL}/subnets
|
||||||
|
# Verificar código de estado HTTP
|
||||||
|
Should Be Equal As Numbers ${response.status_code} 200
|
||||||
|
${json}= Convert To Dictionary ${response.json()}
|
||||||
|
# Verificar que la respuesta contiene las claves 'success' y 'message'
|
||||||
|
Dictionary Should Contain Key ${json} success
|
||||||
|
Dictionary Should Contain Key ${json} message
|
||||||
|
# Validar el mensaje de éxito
|
||||||
|
Should Be Equal ${json['success']} Subredes obtenidas correctamente
|
||||||
|
# Verificar que 'message' es una lista
|
||||||
|
# Should Be True ${json['message']} is list
|
||||||
|
# Verificar que cada subred en 'message' tiene los campos esperados
|
||||||
|
FOR ${subred} IN @{json['message']}
|
||||||
|
Dictionary Should Contain Key ${subred} id
|
||||||
|
Dictionary Should Contain Key ${subred} subnet
|
||||||
|
END
|
||||||
|
|
||||||
|
Post a new subnet
|
||||||
|
[Documentation] Post a new subnet
|
||||||
|
[Tags] subnets
|
||||||
|
${new_subnet} Create Dictionary
|
||||||
|
... subnetId=${valid_net_id}
|
||||||
|
... mask=255.255.255.0
|
||||||
|
... address=192.168.2.0
|
||||||
|
... nextServer=192.168.2.1
|
||||||
|
... bootFileName=pxelinux.0
|
||||||
|
${headers}= Create Dictionary Content-Type=application/json
|
||||||
|
${response}= POST ${BASE_URL}/subnets json=${new_subnet} headers=${headers}
|
||||||
|
Status Should Be 200 ${response}
|
||||||
|
${json}= Convert to Dictionary ${response.json()}
|
||||||
|
Dictionary Should Contain Key ${json} success
|
||||||
|
Dictionary Should Contain Key ${json} message
|
||||||
|
Should Contain ${json["success"]} Subred agregada correctamente
|
||||||
|
Should Be Equal As Numbers ${json["message"]["id"]} ${valid_net_id}
|
||||||
|
|
||||||
|
|
||||||
|
Post a new subnet with existing id
|
||||||
|
[Documentation] Post a new subnet with invalid data
|
||||||
|
[Tags] subnets
|
||||||
|
${new_subnet} Create Dictionary
|
||||||
|
... subnetId=2
|
||||||
|
... mask=255.255.255.0
|
||||||
|
... address=192.168.3.0
|
||||||
|
... nextServer=192.168.3.1
|
||||||
|
... bootFileName=pxelinux.0
|
||||||
|
${headers}= Create Dictionary Content-Type=application/json
|
||||||
|
${response}= POST ${BASE_URL}/subnets json=${new_subnet} headers=${headers} expected_status=400
|
||||||
|
Status Should Be 400 ${response}
|
||||||
|
${json}= Convert to Dictionary ${response.json()}
|
||||||
|
Dictionary Should Contain Key ${json} error
|
||||||
|
Should Contain ${response.json()["error"]} La subred con el id '${valid_net_id}' ya existe.
|
||||||
|
|
||||||
|
Post a new subnet with existing address
|
||||||
|
[Documentation] Post a new subnet with invalid data
|
||||||
|
[Tags] subnets
|
||||||
|
${headers}= Create Dictionary Content-Type=application/json
|
||||||
|
${new_subnet_invalid} Create Dictionary
|
||||||
|
... subnetId=3
|
||||||
|
... mask=255.255.255.0
|
||||||
|
... address=192.168.1.0
|
||||||
|
... nextServer=192.168.1.1
|
||||||
|
... bootFileName=pxelinux.0
|
||||||
|
|
||||||
|
${response}= POST ${BASE_URL}/subnets json=${new_subnet_invalid} headers=${headers} expected_status=400
|
||||||
|
Status Should Be 400 ${response}
|
||||||
|
${json}= Convert to Dictionary ${response.json()}
|
||||||
|
Dictionary Should Contain Key ${json} error
|
||||||
|
Should Contain ${response.json()['error']} La subred con la dirección '192.168.1.0/24' ya existe
|
||||||
|
|
||||||
|
|
||||||
|
Modify an existing subnet by id
|
||||||
|
[Documentation] Modify a subnet by id
|
||||||
|
[Tags] subnets
|
||||||
|
${headers}= Create Dictionary Content-Type=application/json
|
||||||
|
${modified_subnet}= Create Dictionary
|
||||||
|
... mask=255.255.192.0
|
||||||
|
... address=192.168.1.0
|
||||||
|
... nextServer=192.168.1.1
|
||||||
|
... bootFileName=pxelinux.0
|
||||||
|
# Modificar la subred con id=2
|
||||||
|
${response}= PUT ${BASE_URL}/subnets/2 json=${modified_subnet} headers=${headers} expected_status=200
|
||||||
|
Status Should Be 200 ${response}
|
||||||
|
${json}= Convert to Dictionary ${response.json()}
|
||||||
|
# Verificar que la respuesta contiene clave 'success'
|
||||||
|
Dictionary Should Contain Key ${json} success
|
||||||
|
|
||||||
|
# Validar el contenido del mensaje de éxito
|
||||||
|
Should Be Equal ${json['success']} Subred modificada correctamente
|
||||||
|
Should Be Equal ${json['message']['id']} 2
|
||||||
|
|
||||||
|
|
||||||
|
Modify a subnet with invalid id
|
||||||
|
[Documentation] Este test verifica que la modificación de una subred falla cuando el ID no existe.
|
||||||
|
[Tags] subnets
|
||||||
|
${headers}= Create Dictionary Content-Type=application/json
|
||||||
|
${modificar_subred}= Create Dictionary mask=255.255.192.0 address=192.168.1.0 nextServer=192.168.1.1 bootFileName=pxelinux.0
|
||||||
|
${response}= PUT ${BASE_URL}/ogdhcp/v1/subnets/${invalid_net_id} json=${modificar_subred} headers=${headers} expected_status=404
|
||||||
|
|
||||||
|
# Verificar código de estado HTTP
|
||||||
|
Should Be Equal As Numbers ${response.status_code} 404
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Modify subnet with error to save configuration
|
||||||
|
[Documentation] Este test verifica que la modificación falla cuando hay un error al guardar la configuración en Kea DHCP.
|
||||||
|
[Tags] subnets
|
||||||
|
${headers}= Create Dictionary Content-Type=application/json
|
||||||
|
${modificar_subred}= Create Dictionary subnet="192.168.1.0" mask="255.255.255.0" nextServer="192.168.1.1" bootFileName="pxelinux.0"
|
||||||
|
${response}= PUT ${BASE_URL}/subnets/${subnet_error} json=${modificar_subred} headers=${headers} expected_status=400
|
||||||
|
|
||||||
|
# Verificar código de estado HTTP
|
||||||
|
Should Be Equal As Numbers ${response.status_code} 400
|
||||||
|
|
||||||
|
${json}= Convert To Dictionary ${response.json()}
|
||||||
|
|
||||||
|
# Verificar que la respuesta contiene el error esperado
|
||||||
|
Should Be Equal ${json['error']} Error al guardar la configuración en Kea DHCP: Unable to save configuration
|
||||||
|
|
||||||
|
Modify subnet with invalid configuration address without netmask
|
||||||
|
[Documentation] Este test verifica que la modificación falla cuando hay un error en la configuración de Kea DHCP.
|
||||||
|
[Tags] subnets
|
||||||
|
${headers}= Create Dictionary Content-Type=application/json
|
||||||
|
${modificar_subred}= Create Dictionary address="192.168.1.0" nextServer="192.168.1.1" bootFileName="pxelinux.0"
|
||||||
|
|
||||||
|
${response}= PUT ${BASE_URL}/subnets/2 json=${modificar_subred} headers=${headers} expected_status=400
|
||||||
|
|
||||||
|
# Verificar código de estado HTTP
|
||||||
|
Should Be Equal As Numbers ${response.status_code} 400
|
||||||
|
|
||||||
|
${json}= Convert To Dictionary ${response.json()}
|
||||||
|
|
||||||
|
# Verificar que la respuesta contiene el error esperado
|
||||||
|
Should Be Equal ${json['error']} Falta un parámetro requerido: mask
|
||||||
|
|
||||||
|
Modify subnet with invalid configuration netmask without address
|
||||||
|
[Documentation] Este test verifica que la modificación falla cuando hay un error en la configuración de Kea DHCP.
|
||||||
|
[Tags] subnets
|
||||||
|
${headers}= Create Dictionary Content-Type=application/json
|
||||||
|
${modificar_subred}= Create Dictionary mask="255.255.255.0" nextServer="192.168.1.1" bootFileName="pxelinux.0"
|
||||||
|
|
||||||
|
${response}= PUT ${BASE_URL}/subnets/2 json=${modificar_subred} headers=${headers} expected_status=400
|
||||||
|
|
||||||
|
# Verificar código de estado HTTP
|
||||||
|
Should Be Equal As Numbers ${response.status_code} 400
|
||||||
|
|
||||||
|
${json}= Convert To Dictionary ${response.json()}
|
||||||
|
|
||||||
|
# Verificar que la respuesta contiene el error esperado
|
||||||
|
Should Be Equal ${json['error']} Falta un parámetro requerido: address
|
||||||
|
|
||||||
|
Delete subnet by id
|
||||||
|
[Documentation] Este test verifica que la subred se elimina correctamente con el código 200.
|
||||||
|
[Tags] subnets
|
||||||
|
${response}= DELETE ${BASE_URL}/subnets/${valid_net_id}
|
||||||
|
# Verificar código de estado HTTP
|
||||||
|
Should Be Equal As Numbers ${response.status_code} 200
|
||||||
|
${json}= Convert To Dictionary ${response.json()}
|
||||||
|
# Verificar que la respuesta contiene la clave 'success'
|
||||||
|
Dictionary Should Contain Key ${json} success
|
||||||
|
# Validar el mensaje de éxito
|
||||||
|
Should Be Equal ${json['success']} Subred eliminada correctamente
|
||||||
|
|
||||||
|
Delete subnet - Error: wrong subnet Id
|
||||||
|
[Documentation] Este test verifica que la eliminación falla si la subred con el ID no existe.
|
||||||
|
[Tags] subnets
|
||||||
|
${response}= DELETE ${BASE_URL}/subnets/${invalid_net_id} expected_status=404
|
||||||
|
# Verificar código de estado HTTP
|
||||||
|
Should Be Equal As Numbers ${response.status_code} 404
|
||||||
|
${json}= Convert To Dictionary ${response.json()}
|
||||||
|
# Verificar que la respuesta contiene la clave 'error'
|
||||||
|
Dictionary Should Contain Key ${json} error
|
||||||
|
# Validar el mensaje de error
|
||||||
|
Should Be Equal ${json['error']} La subred con el id '999' no existe
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue