ogrepository/bin/updateTrashInfo.py

239 lines
12 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Este script actualiza la información de las imágenes de la papelera del repositorio, reflejándola en el archivo "/opt/opengnsys/ogrepository/etc/trashinfo.json",
añadiendo información de las nuevas imágenes eliminadas, y borrando la información de las imágenes que fueron restauradas (o que ya no están en la papelera).
La información es obtenida desde archivos "<imagen_name>.info.checked", cuyo nombre original era "<imagen_name>.info"
(pero que fueron renombrados al ser insertada su información en el archivo ""/opt/opengnsys/ogrepository/etc/repoinfo.json", antes de ser eliminadas).
No recibe ningún parámetro, y no necesita ser llamado explícitamente (porque lo llama el script "updateRepoInfo.py")".
"""
# --------------------------------------------------------------------------------------------
# IMPORTS
# --------------------------------------------------------------------------------------------
import os
import pwd
import grp
import json
import subprocess
import shutil
from systemd import journal
# --------------------------------------------------------------------------------------------
# VARIABLES
# --------------------------------------------------------------------------------------------
trash_path = '/opt/opengnsys/ogrepository/images_trash' # En este caso, no lleva barra final
info_file = '/opt/opengnsys/ogrepository/etc/trashinfo.json'
# --------------------------------------------------------------------------------------------
# FUNCTIONS
# --------------------------------------------------------------------------------------------
def create_trash_folder():
""" Crea el directorio correspondiente a la papelera, y le asigna propietarios y permisos.
Evidentemente, esta función solo es llamada cuando no existe el directorio.
"""
# Obtenemos el UID del usuario "root" y el GID del grupo "opengnsys":
uid = pwd.getpwnam('root').pw_uid
gid = grp.getgrnam('opengnsys').gr_gid
# Creamos el directorio correspondiente a la papelera:
os.mkdir(trash_path)
# Asignamos el usuario y el grupo propietarios del directorio:
os.chown(trash_path, uid, gid)
# Asignamos permisos "775" al directorio :
os.chmod(trash_path, 0o775)
def create_empty_json():
""" Esta función crea el archivo "trashinfo.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": trash_path, "images": []}
# 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 papelera de imágenes, para buscar archivos con extensión ".img".
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.checked" asociado (siempre que este no se haya modificado antes que la propia imagen).
"""
# Iteramos recursivamente todos los archivos y directorios de "/opt/opengnsys/ogrepository/images_trash",
# y luego iteramos todos los archivos encontrados (en una iteración anidada):
for root, dirs, files in os.walk(trash_path):
for file in files:
# Si la imagen actual tiene extensión ".img", construimos la ruta completa ("img_path"):
if file.endswith(".img"):
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.checked" asociado a la imagen actual:
info_file = f"{img_path}.info.checked"
if os.path.exists(info_file):
# Si la fecha de modificación del archivo ".info.checked" 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.checked" (del tipo "PARTCLONE:LZOP:EXTFS:8500000:Ubuntu_20") en la variable "data":
else:
with open(info_file, 'r') as file:
info_data = file.read()
# Almacenamos el contenido de los archivos ".size", ".sum" y ".full.sum":
with open(f"{img_path}.size", 'r') as file:
size = file.read().strip('\n')
with open(f"{img_path}.sum", 'r') as file:
_sum = file.read().strip('\n')
with open(f"{img_path}.full.sum", 'r') as file:
fullsum = file.read().strip('\n')
# 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.checked"):
img_name = os.path.relpath(img_path, trash_path)
add_to_json(os.path.splitext(img_name)[0], os.path.splitext(img_name)[1][1:], info_data, size, _sum, fullsum)
def add_to_json(image_name, image_type, data, size, _sum, fullsum):
""" Esta función añade al archivo "trashinfo.json" la información de las imágenes que aun no ha sido introducida en él (imágenes eliminadas recientemente, básicamente).
"""
# 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(":")
# 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)
"size": int(size),
"sum": _sum,
"fullsum": fullsum
}
# Almacenamos el contenido del archivo "trashinfo.json" en la variable "info_data"
# (y si no tiene contenido creamos la estructura básica):
if os.path.getsize(info_file) > 0:
with open(info_file, 'r') as file:
info_data = json.load(file)
else:
info_data = {"directory": trash_path, "images": []}
# Comprobamos si las claves "info_data" (o sea, del archivo json) son las correctas:
if set(info_data.keys()) == {"directory", "images"}:
# Actualizamos la información de las imágenes:
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)
# Si las claves de "info_data" no son las correctas, creamos toda la estructura:
else:
info_data = {"directory": trash_path, "images": [json_data], "ous": []}
# Sobreescribimos el archivo "trashinfo.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 "trashinfo.json", y comprueba la existencia de las imágenes especificadas allí.
Elimina las claves correspondientes a imágenes inexistentes en la papelera, y posteriormente sobreescribe el archivo "trashinfo.json".
"""
# Almacenamos el contenido del archivo "trashinfo.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(trash_path, img_name)
if not os.path.exists(img_path) and not os.path.exists(f"{img_path}.lock"):
json_data["images"].pop(i)
# Sobreescribimos el archivo "trashinfo.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 existe el directorio correspondiente a la papelera, y en caso contrario lo creamos:
if not os.path.exists(trash_path):
journal.send("updateTrashInfo.py: Creting trash folder...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
print("Creating trash folder...")
create_trash_folder()
# Comprobamos si tenemos permisos de escritura sobre el directorio que contiene el archivo "trashinfo.json"
# ("/opt/opengnsys/etc"), y en caso contrario lanzamos una excepción:
if not os.access(os.path.dirname(info_file), os.W_OK):
journal.send(f"updateTrashInfo.py: Cant't access to '{info_file}' directory", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
raise PermissionError(f"Cannot access {info_file}")
# Comprobamos si existe el archivo "trashinfo.json", y en caso contrario lo creamos:
if not os.path.exists(info_file):
journal.send("updateTrashInfo.py: Creating empty json file...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
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):
journal.send(f"updateTrashInfo.py: Cant't write on '{info_file}'", PRIORITY=journal.LOG_ERR, SYSLOG_IDENTIFIER="ogrepo-api")
raise PermissionError(f"Cannot access {info_file}")
# Llamamos a la función "check_files", para añadir al archivo json las imágenes aun no añadidas
# (que son las que tienen asociado un archivo ".info.checked"):
journal.send("updateTrashInfo.py: Checking file images...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
print("Checking file images...")
check_files()
# Llamamos a la función "remove_from_json", para eliminar del archivo json las imágenes que ya no están en la papelera
# (solo si el archivo tiene contenido, o dará error):
if os.path.getsize(info_file) > 0:
journal.send("updateTrashInfo.py: Removing inexistent images from json file...", PRIORITY=journal.LOG_INFO, SYSLOG_IDENTIFIER="ogrepo-api")
print("Removing inexistent images...")
remove_from_json()
# --------------------------------------------------------------------------------------------
if __name__ == "__main__":
main()
# --------------------------------------------------------------------------------------------