Add more documentation
parent
b53c3875a2
commit
49a1e18c31
274
gitlib/gitlib.py
274
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}")
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue