257 lines
9.2 KiB
Python
257 lines
9.2 KiB
Python
import uuid
|
|
import shutil
|
|
import json
|
|
import socket
|
|
import subprocess
|
|
import petname
|
|
import hashlib
|
|
import platform
|
|
import os
|
|
from dotenv import load_dotenv
|
|
from datetime import datetime
|
|
|
|
# Archivo JSON para guardar los datos de los contenedores
|
|
CONTAINER_DATA_FILE = os.getenv("CONTAINER_DATA_FILE")
|
|
# Nombre y etiqueta para la imagen base
|
|
BASE_IMAGE_TAG = os.getenv("BASE_IMAGE_TAG")
|
|
|
|
#Lista de puertos internos
|
|
PUERTOS_INTERNOS = [5173, 5432, 8786, 8787, 8080, 17000]
|
|
|
|
# Numero de conteineres a crear
|
|
NUM_CONTAINERS = 4
|
|
|
|
|
|
|
|
# -------------------------------
|
|
# Función: Comprobar si un puerto está libre con nmap
|
|
# -------------------------------
|
|
def is_port_available(port):
|
|
# Cargar datos existentes si el archivo existe y no está vacío
|
|
if os.path.exists(CONTAINER_DATA_FILE) and os.path.getsize(CONTAINER_DATA_FILE) > 0:
|
|
with open(CONTAINER_DATA_FILE, 'r') as file:
|
|
try:
|
|
data = json.load(file) # Cargar datos existentes
|
|
# Verificar si el puerto ya está en uso en los datos existentes
|
|
for container in data:
|
|
for port_map in container['ports']:
|
|
if port_map['host_port'] == port:
|
|
return False # Puerto en uso
|
|
except json.JSONDecodeError:
|
|
pass # Si hay error al leer, continuar con la comprobación de nmap
|
|
|
|
# Ejecuta nmap para comprobar si el puerto está abierto
|
|
command = f"nmap -p {port} 127.0.0.1"
|
|
print(f"[INFO] Ejecutando nmap para el puerto {port}...")
|
|
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
|
|
|
# Muestra el resultado del comando nmap
|
|
print(f"[DEBUG] Resultado de nmap para el puerto {port}:\n{result.stdout}")
|
|
|
|
# Analiza el resultado para ver si el puerto está cerrado
|
|
if "closed" in result.stdout:
|
|
return True # Puerto libre
|
|
else:
|
|
return False # Puerto ocupado
|
|
|
|
|
|
|
|
# -------------------------------
|
|
# Función: Ejecutar contenedor y guardar datos
|
|
# -------------------------------
|
|
def run_docker_compose(internal_ports=None):
|
|
# Genera un UUID único
|
|
unique_uuid = str(uuid.uuid4())
|
|
# Genera un nombre único para el contenedor
|
|
uuid_palabras = petname.Generate(3, separator='-')
|
|
|
|
|
|
# Nombres únicos para el servicio, contenedor y la red
|
|
service_name = f"{uuid_palabras}"
|
|
container_name = f"{uuid_palabras}"
|
|
network_name = f"alpine_network_{unique_uuid}"
|
|
|
|
# Nombre de la imagen local a usar
|
|
image_tag = BASE_IMAGE_TAG # Usar la imagen local directamente
|
|
|
|
# Nombre del archivo docker-compose nuevo
|
|
new_docker_compose_file = f'docker-compose-{unique_uuid}.yml'
|
|
|
|
try:
|
|
# Configuración personalizada de puertos
|
|
ports_mapping = []
|
|
custom_ports = []
|
|
host_port = 27000 # Puerto inicial
|
|
print(f"[INFO] Iniciando la configuración de puertos a partir del puerto {host_port}")
|
|
|
|
if internal_ports:
|
|
for internal_port in internal_ports:
|
|
print(f"[INFO] Configurando puerto interno: {internal_port}")
|
|
|
|
# Buscar el siguiente puerto disponible usando nmap
|
|
while host_port <= 65000:
|
|
if is_port_available(host_port):
|
|
print(f"[INFO] Asignando puerto {host_port} para el puerto interno {internal_port}...")
|
|
custom_ports.append(f" - \"{host_port}:{internal_port}\"")
|
|
ports_mapping.append({
|
|
"host_port": host_port,
|
|
"internal_port": internal_port
|
|
})
|
|
host_port += 1 # Incrementa el puerto para el siguiente uso
|
|
break
|
|
else:
|
|
print(f"[WARNING] Puerto {host_port} ocupado, probando el siguiente...")
|
|
host_port += 1 # Incrementa si el puerto está ocupado
|
|
|
|
if host_port > 65000:
|
|
raise Exception("Se superó el límite de puertos disponibles.")
|
|
|
|
custom_ports_yaml = "\n".join(custom_ports)
|
|
print(f"[INFO] Puertos personalizados configurados:\n{custom_ports_yaml}")
|
|
else:
|
|
custom_ports_yaml = "" # No agrega puertos si no se especifican
|
|
print("[INFO] No se especificaron puertos internos.")
|
|
|
|
# Genera el contenido del archivo docker-compose dinámicamente
|
|
docker_compose_content = f"""
|
|
version: '3.8'
|
|
|
|
services:
|
|
{service_name}:
|
|
image: {image_tag} # Usar la imagen base directamente
|
|
container_name: {container_name}
|
|
networks:
|
|
- {network_name}
|
|
restart: always
|
|
tty: true
|
|
volumes:
|
|
- ./share:/app/share
|
|
- ./d_apps/{container_name}:/app/host_transfer
|
|
ports:
|
|
{custom_ports_yaml}
|
|
command: tail -f /dev/null # Mantener activo el contenedor
|
|
|
|
networks:
|
|
{network_name}:
|
|
driver: bridge
|
|
"""
|
|
|
|
# Guarda el archivo docker-compose generado
|
|
with open(new_docker_compose_file, 'w') as file:
|
|
file.write(docker_compose_content)
|
|
|
|
# Muestra el contenido generado para depuración
|
|
print(f"[DEBUG] Contenido del archivo {new_docker_compose_file}:\n")
|
|
print(docker_compose_content)
|
|
|
|
# Ejecuta docker-compose con el archivo nuevo y captura la salida
|
|
print("[INFO] Ejecutando docker compose...")
|
|
result = subprocess.run(
|
|
f'docker compose -f {new_docker_compose_file} up -d --no-build',
|
|
shell=True,
|
|
capture_output=True,
|
|
text=True
|
|
)
|
|
|
|
# Muestra el resultado del comando
|
|
if result.returncode != 0:
|
|
print(f"[ERROR] Error al ejecutar docker compose:\n{result.stderr}")
|
|
raise Exception("Error al ejecutar docker-compose")
|
|
else:
|
|
print(f"[INFO] Contenedor iniciado exitosamente:\n{result.stdout}")
|
|
|
|
# Mostrar contenedores activos y detenidos
|
|
print("[DEBUG] Contenedores activos y detenidos:")
|
|
subprocess.run("docker ps -a", shell=True)
|
|
|
|
# Obtener el ID del contenedor creado
|
|
container_id = subprocess.getoutput(f"docker ps -qf name={container_name}")
|
|
if not container_id:
|
|
print(f"[ERROR] No se pudo obtener el ID del contenedor {container_name}.")
|
|
raise Exception("No se pudo obtener el ID del contenedor")
|
|
|
|
print(f"[INFO] ID del contenedor: {container_id}")
|
|
|
|
# Mostrar logs del contenedor si está detenido
|
|
print("[INFO] Mostrando logs del contenedor:")
|
|
subprocess.run(f"docker logs {container_id}", shell=True)
|
|
|
|
# Guardar los datos del contenedor en el archivo JSON
|
|
save_container_data({
|
|
'id': container_id,
|
|
'name': container_name,
|
|
'network': network_name,
|
|
'image': image_tag,
|
|
'compose_file': new_docker_compose_file,
|
|
'creation_timestamp': datetime.now().isoformat(),
|
|
'ports': ports_mapping # Agrega la información de los puertos mapeados
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"[ERROR] Error: {str(e)}")
|
|
|
|
finally:
|
|
# Eliminar el archivo docker-compose generado si existe
|
|
if os.path.exists(new_docker_compose_file):
|
|
print(f"[INFO] Eliminando archivo temporal: {new_docker_compose_file}")
|
|
os.remove(new_docker_compose_file)
|
|
|
|
|
|
|
|
# -------------------------------
|
|
# Función: Guardar datos del contenedor en JSON
|
|
# -------------------------------
|
|
def save_container_data(container_data):
|
|
# Cargar datos existentes si el archivo existe y no está vacío
|
|
if os.path.exists(CONTAINER_DATA_FILE) and os.path.getsize(CONTAINER_DATA_FILE) > 0:
|
|
with open(CONTAINER_DATA_FILE, 'r') as file:
|
|
try:
|
|
data = json.load(file) # Cargar datos existentes
|
|
except json.JSONDecodeError:
|
|
data = [] # Si hay error al leer, inicializar como lista vacía
|
|
else:
|
|
# Crear el archivo vacío y usar una lista vacía
|
|
with open(CONTAINER_DATA_FILE, 'w') as file:
|
|
json.dump([], file)
|
|
data = [] # Inicializar como lista vacía
|
|
|
|
# Agregar nuevo contenedor
|
|
data.append(container_data)
|
|
|
|
# Guardar datos actualizados
|
|
with open(CONTAINER_DATA_FILE, 'w') as file:
|
|
json.dump(data, file, indent=4)
|
|
|
|
|
|
|
|
# -------------------------------
|
|
# Función: Limpiar imágenes huérfanas
|
|
# -------------------------------
|
|
def clean_unused_images():
|
|
print("➤ Eliminando imágenes huérfanas...")
|
|
os.system('docker image prune -f')
|
|
|
|
# -------------------------------
|
|
# Función: Limpiar redes huérfanas
|
|
# -------------------------------
|
|
def clean_unused_networks():
|
|
print("➤ Eliminando redes huérfanas...")
|
|
os.system('docker network prune -f')
|
|
|
|
|
|
######################################### -------------------------------
|
|
# Ejecución del script
|
|
######################################### -------------------------------
|
|
if __name__ == "__main__":
|
|
|
|
|
|
# Crear contenedores y guardarlos en el JSON
|
|
for _ in range(NUM_CONTAINERS):
|
|
run_docker_compose(internal_ports=PUERTOS_INTERNOS)
|
|
print("➤ Contenedores creados y guardados en el JSON")
|
|
|
|
# Limpiar imágenes huérfanas
|
|
clean_unused_images()
|
|
# Limpiar redes huérfanas
|
|
clean_unused_networks()
|