From 49a1e18c31acf5f77e946f3bb2b09195445b46be Mon Sep 17 00:00:00 2001 From: Vadim Troshchinskiy Date: Wed, 18 Sep 2024 20:18:59 +0200 Subject: [PATCH] Add more documentation --- gitlib/gitlib.py | 274 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 223 insertions(+), 51 deletions(-) diff --git a/gitlib/gitlib.py b/gitlib/gitlib.py index c47e455..b03d3cd 100755 --- a/gitlib/gitlib.py +++ b/gitlib/gitlib.py @@ -78,7 +78,25 @@ class NTFSImplementation(Enum): class NTFSLibrary: + """ + A library for managing NTFS filesystems. + + Attributes: + logger (logging.Logger): Logger for the class. + implementation (NTFSImplementation): The implementation to use for mounting NTFS filesystems. + """ + def __init__(self, implementation): + """ + Initializes the instance with the given implementation. + + Args: + implementation: The implementation to be used by the instance. + + Attributes: + logger (logging.Logger): Logger instance for the class, set to debug level. + implementation: The implementation provided during initialization. + """ self.logger = logging.getLogger("NTFSLibrary") self.logger.setLevel(logging.DEBUG) self.implementation = implementation @@ -87,11 +105,35 @@ class NTFSLibrary: None def create_filesystem(self, device, label): + """ + Creates an NTFS filesystem on the specified device with the given label. + + Args: + device (str): The device path where the NTFS filesystem will be created. + label (str): The label to assign to the NTFS filesystem. + + Returns: + None + + Logs: + Logs the creation process with the device and label information. + """ self.logger.info(f"Creating NTFS in {device} with label {label}") subprocess.run(["/usr/sbin/mkntfs", device, "-Q", "-L", label]) def mount_filesystem(self, device, mountpoint): + """ + Mounts a filesystem on the specified mountpoint using the specified NTFS implementation. + + Args: + device (str): The device path to be mounted (e.g., '/dev/sda1'). + mountpoint (str): The directory where the device will be mounted. + + Raises: + ValueError: If the NTFS implementation is unknown. + + """ self.logger.info(f"Mounting {device} in {mountpoint} using implementation {self.implementation}") if self.implementation == NTFSImplementation.Kernel: subprocess.run(["/usr/bin/mount", "-t", "ntfs3", device, mountpoint], check = True) @@ -117,11 +159,18 @@ class NTFSLibrary: return hex_str def modify_uuid(self, device, uuid): - """Modifica el UUID de un sistema de archivos NTFS + """ + Modify the UUID of an NTFS device. + + This function changes the UUID of the specified NTFS device to the given UUID. + It reads the current UUID from the device, logs the change, and writes the new UUID. Args: - device (_type_): Dispositivo - uuid (_type_): UUID nuevo (8 bytes) + device (str): The path to the NTFS device file. + uuid (str): The new UUID to be set, in hexadecimal string format. + + Raises: + IOError: If there is an error opening or writing to the device file. """ ntfs_uuid_offset = 48 @@ -143,6 +192,30 @@ class OpengnsysGitLibrary: """Libreria de git""" def __init__(self, require_cache = True, ntfs_implementation = NTFSImplementation.Kernel): + """ + Initializes the Git library for OpenGnsys. + + Args: + require_cache (bool): Indicates whether a cache partition is required. Defaults to True. + ntfs_implementation (NTFSImplementation): Specifies the NTFS implementation to use. Defaults to NTFSImplementation.Kernel. + + Raises: + RequirementException: If the cache partition is required but cannot be mounted. + + Attributes: + logger (logging.Logger): Logger instance for the Git library. + mounts (list): List of mounted filesystems. + repo_user (str): Username for the repository. + repo_image_path (str): Path to the repository images. + ntfs_implementation (NTFSImplementation): NTFS implementation being used. + cache_dir (str): Directory for the cache. + default_ignore_list (list): List of default paths to ignore. + fully_ignored_dirs (list): List of directories to fully ignore. + kernel_args (dict): Parsed kernel command line arguments. + repo_server (str): Server address for the repository. + debug_check_for_untracked_files (bool): Flag to check for untracked files for debugging purposes. + """ + self.logger = logging.getLogger("OpengnsysGitLibrary") self.logger.setLevel(logging.DEBUG) self.logger.debug(f"Initializing. Cache = {require_cache}, ntfs = {ntfs_implementation}") @@ -320,13 +393,13 @@ class OpengnsysGitLibrary: f.write("\n") def _parse_kernel_cmdline(self): - """Obtener parámetros de configuración de la linea de comandos del kernel + """Parse the kernel arguments to obtain configuration parameters in Oglive - Opengnsys nos pasa parametros por linea de comando del kernel, por ejemplo: + OpenGnsys passes data in the kernel arguments, for example: [...] group=Aula_virtual ogrepo=192.168.2.1 oglive=192.168.2.1 [...] Returns: - dict: Diccionario de clave/valor de parámetros + dict: Dict of configuration parameters and their values. """ params = {} self.logger.debug("Parsing kernel parameters") @@ -343,6 +416,18 @@ class OpengnsysGitLibrary: return params def _is_filesystem(self, path): + """ + Check if the given path is a filesystem root. + + This method reads the '/proc/mounts' file to determine if the specified + path is a mount point (i.e., a filesystem root). + + Args: + path (str): The path to check. + + Returns: + bool: True if the path is a filesystem root, False otherwise. + """ with open('/proc/mounts', 'r') as mounts: for mount in mounts: parts = mount.split() @@ -358,13 +443,13 @@ class OpengnsysGitLibrary: def _mklostandfound(self, path): - """Recrear el lost+found si es necesario. + """Recreate the lost+found if necessary. - Cuando clonamos en el raíz de un sistema de archivos, al limpiar los contenidos, - eliminamos el lost+found. Este es un directorio especial que requiere el uso de - una herramienta para recrearlo. + When cloning at the root of a filesystem, cleaning the contents + removes the lost+found directory. This is a special directory that requires the use of + a tool to recreate it. - Puede fallar en caso de que el sistema de archivos no lo necesite. + It may fail if the filesystem does not need it. We consider this harmless and ignore it. """ if self._is_filesystem(path): curdir = os.getcwd() @@ -459,16 +544,25 @@ class OpengnsysGitLibrary: self.logger.debug(f"stderr: {result.stderr}") def _grub_install(self, boot_device, root_directory): - """Instalar grub + """ + Install GRUB bootloader on the specified boot device. + + This method checks for the presence of GRUB 2.x and GRUB 1.x installers + and attempts to install the appropriate version. If neither installer is + found, a RequirementException is raised. Args: - device (str): Dispositivo de arranque - root_directory (str): Punto de montaje de sistema raiz + boot_device (str): The device on which to install the GRUB bootloader (e.g., '/dev/sda'). + root_directory (str): The root directory where GRUB files should be installed. Raises: - RequirementException: Si falta binario de GRUB - """ + RequirementException: If neither GRUB 2.x nor GRUB 1.x installer is found. + subprocess.CalledProcessError: If the GRUB installation command fails. + Logs: + Debug information about the installation process, including the return code, + stdout, and stderr of the GRUB installation command. + """ if os.path.exists("/usr/sbin/grub2-install"): self.logger.debug("Installing Grub 2.x (NOT IMPLEMENTED)") elif os.path.exists("/usr/sbin/grub-install"): @@ -483,7 +577,19 @@ class OpengnsysGitLibrary: raise RequirementException("Couldn't find /usr/sbin/grub2-install or /usr/sbin/grub-install") def _efi_install(self, boot_device, root_directory): - """Instalar EFI""" + """ + Install EFI data on the specified boot device. + + Copies EFI data from a metadata directory within the root directory + to the specified boot device. It logs the process of installing the EFI data. + + Args: + boot_device (str): The path to the boot device where EFI data will be installed. + root_directory (str): The root directory containing the metadata and EFI data. + + Raises: + shutil.Error: If an error occurs during the copying of the EFI data. + """ self.logger.info(f"Instalando datos EFI en {boot_device}") meta_dir = os.path.join(root_directory, ".opengnsys-metadata") @@ -493,6 +599,19 @@ class OpengnsysGitLibrary: def _find_boot_device(self): + """ + Searches for the EFI boot partition on the system. + + This method scans the system's partitions to locate the EFI boot partition, + which is identified by the GUID "C12A7328-F81F-11D2-BA4B-00A0C93EC93B". + + Returns: + str: The device node of the EFI partition if found, otherwise None. + + Logs: + - Debug messages indicating the progress of the search. + - A warning message if the EFI partition is not found. + """ disks = [] self.logger.debug("Looking for EFI partition") @@ -539,6 +658,29 @@ class OpengnsysGitLibrary: def _runBashFunction(self, function, arguments): + """ + Executes an OpenGnsys bash function with given arguments. + + This method creates a temporary bash script that sources all `.lib` files in a specific directory, + writes the specified bash function and its arguments to the script, makes the script executable, + and then runs the script. The output and errors from the script execution are captured and logged. + + This is a temporary migration convenience function, it won't be present once the rest of the + code is migrated to Python. + + Args: + function (str): The name of the bash function to execute. + arguments (list): A list of arguments to pass to the bash function. + + Returns: + str: The standard output from the executed bash function. + + Logs: + - Debug information about the bash function and arguments being run. + - The path of the temporary file created. + - The command being executed. + - The standard output and standard error from the script execution. + """ # Create a temporary file self.logger.debug(f"Running bash function: {function} {arguments}") @@ -568,11 +710,8 @@ class OpengnsysGitLibrary: self.logger.debug(f"STDOUT: {output}") self.logger.debug(f"STDERR: {result.stderr}") - # temp_file.delete() - return output -# os.system(temp_file.name) def _getOgRepository(self, name): @@ -624,33 +763,33 @@ class OpengnsysGitLibrary: return results def _create_metadata(self, path): - """Calcular metadatos para un filesystem + """Calculate metadata for a filesystem - Aquí recorremos todo el sistema de archivos para: + Here we traverse the entire filesystem to: - 1. Encontrar directorios vacíos y rellenarlos para que git los conserve. - 2. Obtener todas las ACLs - 3. Obtener todos los atributos extendidos. - 4. Renombrar archivos .gitignore - 5. Buscar puntos de montaje y obtener información sobre ellos - 6. Metadatos adicionales, como el tipo de arranque - 7. NTFS secaudit, que debe realizarse al final del proceso porque hay - que desmontar el filesystem. + 1. Find empty directories and fill them so that git preserves them. + 2. Obtain all ACLs. + 3. Obtain all extended attributes. + 4. Rename .gitignore files. + 5. Find mount points and obtain information about them. + 6. Additional metadata, such as the boot type. + 7. NTFS secaudit, which must be performed at the end of the process because the filesystem needs to be unmounted. - Para archivos vacíos, generamos una lista que podemos usar después para eliminar los archivos - .opengnsys-keep. Esto se hace porque hay casos en los que un archivo inesperado puede causar - problemas. Por ejemplo, sshfs por defecto se niega a montar cosas en un directorio que contiene - archivos. + For empty files, we generate a list that we can use later to delete the + .opengnsys-keep files. This is done because there are cases where an unexpected + file can cause problems. For example, sshfs by default refuses to mount things + in a directory that contains files. - Renombramos los archivos .gitignore en subdirectorios porque git los aplicaría a nuestro proceso. + We rename the .gitignore files in subdirectories because git would apply them + to our process. - Escribimos todos los datos en JSON para asegurarnos de que no hay problemas con espacios, fines de - linea ni otros caracteres especiales. Esto también asegura una entrada por linea, lo que podemos - usar para acelerar el rendimiento, usando el git para obtener la diferencia entre un estado anterior - y el actual. + We write all data in JSON to ensure there are no issues with spaces, line endings, + or other special characters. This also ensures one entry per line, which we can use + to speed up performance by using git to get the difference between a previous state + and the current one. Args: - path (str): Ruta base del sistema de archivos + path (str): Base path of the filesystem """ self.logger.info(f"Creating metadata for {path}") @@ -884,22 +1023,20 @@ class OpengnsysGitLibrary: return return_data def _restore_metadata(self, path, destructive_only=False): - """Restrauracion de metadatos creados por _createmetadata - - + """Restore the metadata created by _create_metadata Args: - path (str): Ruta destino - destructive_only (bool): Solo restaurar lo que se modifique durante un commit + path (str): Destination path + destructive_only (bool): Only restore what is modified during a commit Notes: - El git no maneja archivos de tipo dispositivo o socket correctamente. Por tanto, - debemos guardar datos sobre ellos antes del commit, y eliminarlos antes de que - git pueda verlos y confundirse. + Git does not handle device or socket type files correctly. Therefore, + we must save data about them before the commit, and delete them before + git can see them and get confused. - destructive_only=True solo restaura este tipo de metadatos, los que modificamos - en el sistema de archivos real antes del commit. Esto se hace para dejar el - sistema de archivos en el mismo estado que tenia antes del commit. + destructive_only=True only restores the metadata that we modify + in the real file system before the commit. This is done to leave the + file system in the same state it had before the commit. """ self.logger.debug("Initializing") @@ -1096,6 +1233,25 @@ class OpengnsysGitLibrary: file.truncate() def initRepo(self, device, repo_name): + """ + Initialize a Git repository on a specified device. + + This method mounts the device, initializes a Git repository, configures it, + and sets up a remote origin. It handles both NTFS and other filesystem types. + + Args: + device (str): The device path to initialize the repository on. + repo_name (str): The name of the repository to be created. + + Raises: + RuntimeError: If the .git directory is of an unrecognized file type. + + Notes: + - The method mounts the device to /mnt/{device_basename}. + - The .git directory is created in a cache partition and symlinked to the device. + - The repository is initialized and configured, and an initial commit is made. + - The method sets up a remote origin and pushes the initial commit. + """ self._unmount_device(device) path = os.path.join("/mnt", os.path.basename(device)) @@ -1231,6 +1387,22 @@ class OpengnsysGitLibrary: repo.git.push("--set-upstream", "origin", repo.head.ref, "--force") # force = True) def cloneRepo(self, repo_name, destination, boot_device): + """ + Clones a repository to a specified destination and sets up the bootloader. + + Args: + repo_name (str): The name of the repository to clone. + destination (str): The destination directory where the repository will be cloned. + boot_device (str): The boot device to install the bootloader. + + Raises: + RequirementException: If the repository metadata is incorrect or if the repository's + boot system is incompatible with the current system. + + Logs: + Info: Logs the start of the cloning process. + Debug: Logs the repository URL, EFI compatibility of the repository and the system. + """ self.logger.info(f"Cloning repo: {repo_name} => {destination}")