refs #631 - Add updateRepoInfo.py script

pull/1/head
Gerardo GIl Elizeire 2024-08-14 17:25:58 +02:00
parent 1ec5d4ba3e
commit 2467dd30e7
3 changed files with 285 additions and 2 deletions

View File

@ -1,5 +1,5 @@
OpenGnsys Repository Manager
=======================================
ogRepository - OpenGnsys Repository Manager
===========================================
Este repositorio GIT contiene la estructura de datos del repositorio de imágenes de OpenGnsys.

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Este script obtiene y devuelve la interfaz de red asociada a la IP especificada en el archivo "/opt/opengnsys/etc/ogAdmRepo.cfg" (en la clave "IPlocal").

View File

@ -0,0 +1,282 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Este script actualiza la información de las imágenes del repositorio, reflejándola en el archivo "/opt/opengnsys/etc/repoinfo.json",
añadiendo información de las imágenes nuevas, y borrando la información de las imágenes que fueron eliminadas.
La información es obtenida desde archivos "<imagen_name>.info", que originalmente eran eliminados,
pero que en esta versión son renombrados a "<imagen_name>.info.checked", obteniendo la misma funcionalidad.
En principio, debería hacer lo mismo que el script bash original (cuyo nombre es "checkrepo"), salvo por el detalle que acabamos de comentar.
No recibe ningún parámetro, y debería ser llamado por el propio ogRepoitory cada vez que se elimine una imagen (desde el script "deleteImage.py"),
y por ogCore u ogLive cada vez que se cree una imagen.
"""
# --------------------------------------------------------------------------------------------
# IMPORTS
# --------------------------------------------------------------------------------------------
import os
import json
import subprocess
import shutil
# --------------------------------------------------------------------------------------------
# VARIABLES
# --------------------------------------------------------------------------------------------
repo_path = '/opt/opengnsys/images'
info_file = '/opt/opengnsys/etc/repoinfo.json'
# --------------------------------------------------------------------------------------------
# FUNCTIONS
# --------------------------------------------------------------------------------------------
def create_empty_json():
""" Esta función crea el archivo "repoinfo.json", con la estructura básica que debe contener.
Evidentemente, solo es llamada cuando no existe el archivo.
"""
# Creamos un diccionario con la estructura básica que debe tener el archivo json:
json_data = {"directory": repo_path, "images": [], "ous": []}
# Abrimos el archivo json en modo escritura (creándolo si no existe, como es el caso),
# y le añadimos la estructura almacenada en el diccionario "json_data":
with open(info_file, 'w') as file:
json.dump(json_data, file, indent=2)
def check_files():
""" Esta función recorre el directorio de imágenes, para buscar archivos con extensiones ".img" o ".dsk".
Llama a la función "add_to_json" para que esta añada información de las imágenes no bloqueadas (sin archivo ".lock"),
que además tengan un archivo ".info" asociado (siempre que este no se haya modificado antes que la propia imagen).
Originalmente eliminaba los archivos ".info" después de extraer la información, pero ahora los renombra (agrega ".checked").
"""
# Iteramos recursivamente todos los archivos y directorios de "/opt/opengnsys/images",
# y luego iteramos todos los archivos encontrados (en una iteración anidada):
for root, dirs, files in os.walk(repo_path):
for file in files:
# Si la imagen actual tiene extensión ".img" o ".dsk", construimos la ruta completa ("img_path"):
if file.endswith((".img", ".dsk")):
img_path = os.path.join(root, file)
# Si existe un archivo ".lock" asociado a la imagen actual, pasamos a la siguiente:
if os.path.exists(f"{img_path}.lock"):
continue
# Comprobamos si existe un archivo ".info" asociado a la imagen actual:
info_file = f"{img_path}.info"
if os.path.exists(info_file):
# Si la fecha de modificación del archivo ".info" es anterior a la de la imagen, lo eliminamos (porque estará desactualizado):
if os.path.getmtime(info_file) < os.path.getmtime(img_path):
os.remove(info_file)
print(f"Warning: Deleted outdated file {info_file}")
# En caso contrario, almacenamos el contenido del archivo ".info" (del tipo "PARTCLONE:LZOP:EXTFS:8500000:Ubuntu_20") en la variable "data":
else:
with open(info_file, 'r') as file:
data = file.read()
# Llamamos a la función "add_to_json", para que inserte la información de la imagen en el archivo json
# (pasándole el nombre de la imagen, la extensión, y los datos extraídos del archivo ".info"):
img_name = os.path.relpath(img_path, repo_path)
add_to_json(os.path.splitext(img_name)[0], os.path.splitext(img_name)[1][1:], data)
# Renombramos el archivo ".info" a ".info.checked", para que ya no se añada la información que contiene
# (originalmente se eliminaba el archivo, pero creo que es mejor mantenerlo):
os.rename(info_file, f"{info_file}.checked")
def check_dirs():
""" Esta función recorre el directorio de imágenes, para buscar archivos con nombre "ogimg.info".
Llama a la función "add_to_json" para que esta añada información de las imágenes no bloqueadas (sin archivo ".lock"),
que además tengan un archivo "ogimg.info" asociado (pero no que tipo de imágenes son esas).
"""
# Iteramos recursivamente todos los archivos y directorios de "/opt/opengnsys/images",
# y luego iteramos todos los archivos encontrados (en una iteración anidada):
for root, dirs, files in os.walk(repo_path):
for file in files:
# Si el nombre del archivo actual es "ogimg.info", construimos su ruta completa ("info_path"), y la ruta del directorio ("img_path"):
if file == "ogimg.info":
info_path = os.path.join(root, file)
img_path = os.path.dirname(info_path)
# Si existe un archivo ".lock" asociado a la imagen actual, o si la ruta coincide con "repo_path", pasamos a la siguiente:
if img_path == repo_path or os.path.exists(f"{img_path}.lock"):
continue
# Almacenamos el contenido del archivo "ogimg.info", en la variable "lines":
with open(info_path, 'r') as file:
lines = file.readlines()
# Iteramos las lineas obtenidas, para extraer el tipo de sistema de archivos ("fstype") y el tamaño de los datos ("sizedata"),
# y con ello construimos una cadena "rsync::{fstype}:{sizedata}:" (que almacenamos en "data"):
for line in lines:
if line.startswith("# fstype"):
fstype = line.split("=")[1].strip()
elif line.startswith("# sizedata"):
sizedata = line.split("=")[1].strip()
data = f"rsync::{fstype}:{sizedata}:"
# Llamamos a la función "add_to_json", para que inserte la información de la imagen en el archivo json
# (pasándole el nombre de la imagen, el tipo "dir"", y los datos extraídos del archivo "ogimg.info"):
img_name = os.path.relpath(img_path, repo_path)
add_to_json(img_name, "dir", data)
def add_to_json(image_name, image_type, data):
""" Esta función añade al archivo "repoinfo.json" la información de las imágenes que aun no ha sido introducida en él (imágenes nuevas, básicamente).
El procedimiento es diferente para las imágenes "normales" y para las imágenes basadas en OU.
"""
# Almacenamos el contenido de la variable "data" (del tipo "PARTCLONE:LZOP:EXTFS:8500000:Ubuntu_20") en variables separadas:
clonator, compressor, fstype, datasize, client = data.split(":")
# Si la imagen está dentro de una OU (directorio), extraemos el nombre de la OU y de la imagen:
ou_name = None
if '/' in image_name:
ou_name = os.path.dirname(image_name)
image_name = os.path.basename(image_name)
# Creamos un diccionario con los datos de la imagen, que luego insertaremos en el json:
json_data = {
"name": image_name,
"type": image_type.lower(),
"clientname": client.strip('\n'), # Eliminamos el salto de linea
"clonator": clonator.lower(),
"compressor": compressor.lower(),
"filesystem": fstype.upper(),
"datasize": int(datasize) * 1024 # Convertimos el valor a bytes (desde KB)
}
# Almacenamos el contenido del archivo "repoinfo.json" en la variable "info_data":
with open(info_file, 'r') as file:
info_data = json.load(file)
# Comprobamos si las claves "info_data" (o sea, del archivo json) son las correctas:
if set(info_data.keys()) == {"directory", "images", "ous"}:
# Actualizamos la información de las imágenes "normales" (no basadas en OU):
if ou_name is None:
img_index = next((i for i, img in enumerate(info_data["images"]) if img["name"] == image_name), None)
if img_index is not None:
# Update if image data changes
if info_data["images"][img_index] != json_data:
info_data["images"][img_index] = json_data
else:
# Append a new entry
info_data["images"].append(json_data)
# Actualizamos la información de las imágenes basadas en OU:
else:
ou_index = next((i for i, ou in enumerate(info_data["ous"]) if ou["subdir"] == ou_name), None)
if ou_index is None:
# Append a new OU entry
info_data["ous"].append({"subdir": ou_name, "images": [json_data]})
else:
img_index = next((i for i, img in enumerate(info_data["ous"][ou_index]["images"]) if img["name"] == image_name), None)
if img_index is not None:
# Update if image data changes
if info_data["ous"][ou_index]["images"][img_index] != json_data:
info_data["ous"][ou_index]["images"][img_index] = json_data
else:
# Append a new entry
info_data["ous"][ou_index]["images"].append(json_data)
# Si las claves de "info_data" no son las correctas, creamos toda la estructura:
else:
if ou_name is None:
info_data = {"directory": repo_path, "images": [json_data], "ous": []}
else:
info_data = {"directory": repo_path, "images": [], "ous": [{"subdir": ou_name, "images": [json_data]}]}
# Sobreescribimos el archivo "repoinfo.json", con el contenido de "info_data" (que estará actualizado):
with open(info_file, 'w') as file:
json.dump(info_data, file, indent=2)
def remove_from_json():
""" Esta función carga el contenido del archivo "repoinfo.json", y comprueba la existencia de las imágenes especificadas allí.
Elimina las claves correspondientes a imágenes inexistentes, y posteriormente sobreescribe el archivo "repoinfo.json".
"""
# Almacenamos el contenido del archivo "repoinfo.json", en la variable "json_data":
with open(info_file, 'r') as file:
json_data = json.load(file)
# Iteramos las imágenes de la clave "images" de "json_data", construimos sus rutas y comprobamos su existencia
# (si no existen, las eliminamos de "json_data"):
for i, image in enumerate(json_data.get("images", [])):
img_name = image["name"]
img_type = image["type"]
if img_type != "dir":
img_name = f"{img_name}.{img_type}"
img_path = os.path.join(repo_path, img_name)
if not os.path.exists(img_path) and not os.path.exists(f"{img_path}.lock"):
json_data["images"].pop(i)
# Iteramos las imágenes de la clave "ous" de "json_data", construimos sus rutas y comprobamos su existencia
# (si no existen, las eliminamos de "json_data"):
for j, ou in enumerate(json_data.get("ous", [])):
ou_name = ou["subdir"]
ou_path = os.path.join(repo_path, ou_name)
if not os.path.exists(ou_path):
json_data["ous"].pop(j)
else:
for i, image in enumerate(ou.get("images", [])):
img_name = image["name"]
img_type = image["type"]
if img_type != "dir":
img_name = f"{img_name}.{img_type}"
img_path = os.path.join(ou_path, img_name)
if not os.path.exists(img_path) and not os.path.exists(f"{img_path}.lock"):
ou["images"].pop(i)
# Sobreescribimos el archivo "repoinfo.json", con el contenido de "json_data"
# (una vez eliminadas las imágenes inexistentes):
with open(info_file, 'w') as file:
json.dump(json_data, file, indent=2)
# --------------------------------------------------------------------------------------------
# MAIN
# --------------------------------------------------------------------------------------------
def main():
"""
"""
# Comprobamos si tenemos permisos de escritura sobre el directorio que contiene el archivo "repoinfo.json"
# ("/opt/opengnsys/etc"), y en caso contrario lanzamos una excepción:
if not os.access(os.path.dirname(info_file), os.W_OK):
raise PermissionError(f"Cannot access {info_file}")
# Comprobamos si existe el archivo "repoinfo.json", y en caso contrario lo creamos:
if not os.path.exists(info_file):
print("Creating empty json file...")
create_empty_json()
# Comprobamos si tenemos permisos de escritura sobre el archivo, y en caso contrario lanzamos un error:
if not os.access(info_file, os.W_OK):
raise PermissionError(f"Cannot access {info_file}")
# Llamamos a la función "check_files", para añadir al archivo json las imágenes ".img" o ".dsk" aun no añadidas
# (que son las que tienen asociado un archivo ".info", aun no renombrado o eliminado):
print("Checking file images...")
check_files()
# Llamamos a la función "check_dirs", para añadir al archivo json las imágenes aun no añadidas que tienen asociado un archivo "ogimg.info"
# (no sé qué imágenes son estas, pero de alguna forma están basadas en directorios):
print("Checking dir images...")
check_dirs()
# Llamamos a la función "remove_from_json", para eliminar del archivo json las imágenes que fueron eliminadas del repositorio:
print("Removing deleted images...")
remove_from_json()
# --------------------------------------------------------------------------------------------
if __name__ == "__main__":
main()
# --------------------------------------------------------------------------------------------