diff --git a/ogclient/interfaceAdm/CrearImagenGit b/ogclient/interfaceAdm/CrearImagenGit index 10ec270..87c66ee 100755 --- a/ogclient/interfaceAdm/CrearImagenGit +++ b/ogclient/interfaceAdm/CrearImagenGit @@ -2,6 +2,7 @@ import sys import resource import logging +import argparse import ogGlobals @@ -41,6 +42,9 @@ def create_image(disk_num, partition_num, repo, image_name): og_git.progress_callback = OgProgressPrinterWeb() device = DiskLib.ogDiskToDev(disk_num, partition_num) + if device is None: + sys.exit(SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, f"Failed to translate disk {disk_num} partition {partition_num} to a device")) + if og_git.initRepo(device, image_name): return 0 else: @@ -59,19 +63,26 @@ def main(): print(f"No se pudo aumentar el límite de archivos abiertos: {e}") + parser = argparse.ArgumentParser( + prog = "OpenGnsys Git Image Create", + description = "Creates a git repository from a partition" + ) - if len(sys.argv) < 5: - sys.exit(SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, "Incorrect number of arguments. Usage: CrearImagenGit disk_num partition_num image_name repo")) + parser.add_argument("--disk", type=int, metavar="DISK", required=True, help="Disk ID") + parser.add_argument("--partition", type=int, metavar="PART", required=True, help="Disk partition") + parser.add_argument("--repository", type=str, metavar="REPO", required=True, help="Address of the Git repository to clone") + parser.add_argument("--image-name", type=str, metavar="REPO", required=True, help="Name of the new image at the repository") + parser.add_argument("--tag", type=str, metavar="TAG", required=False, help="Tag to automatically create") + parser.add_argument("--message", type=str, metavar="MSG", required=False, help="Commit message") - # repo - repositorio, ip address. Opcional porque oglive lo recibe como parametro de kernel + parser.add_help = True + + args = parser.parse_args() + + #disk_num, partition_num, image_name, repo, tag = sys.argv[1:6] - disk_num, partition_num, image_name, repo = sys.argv[1:5] - - - opengnsys_log_dir = "/opt/opengnsys/log" - ip_address = NetLib.ogGetIpAddress() - logFilePath = f"{opengnsys_log_dir}/{ip_address}.log" + logFilePath = "/var/log/opengnsys.CrearImagenGit.log" ogLog = OgLogger() ogLog.setLevel(logging.DEBUG) @@ -91,8 +102,11 @@ def main(): logger.info("Starting CrearImagenGit") + # TODO: + # repo = repositorio, oggit@server.com:/oggit + # image = nombre repo - retval = create_image(disk_num, partition_num, repo, image_name) + retval = create_image(args.disk, args.partition, args.repository, args.image_name) diff --git a/ogclient/interfaceAdm/ModificarImagenGit b/ogclient/interfaceAdm/ModificarImagenGit index a3860d0..4e99a03 100755 --- a/ogclient/interfaceAdm/ModificarImagenGit +++ b/ogclient/interfaceAdm/ModificarImagenGit @@ -4,7 +4,7 @@ import subprocess import sys import time import resource - +import argparse soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE) @@ -67,34 +67,56 @@ class OgLogger(logging.StreamHandler): -def commit_image(disk_num, partition_num, image_name, msg): +def commit_image(disk_num, partition_num, repository, branch, options, msg): ntfs_impl = NTFSImplementation.NTFS3G og_git = OpengnsysGitLibrary(ntfs_implementation = ntfs_impl) og_git.progress_callback = OgProgressPrinterWeb() device = DiskLib.ogDiskToDev(disk_num, partition_num) - og_git.commit(device = device, message = msg) - og_git.push(device = device) + if device is None: + sys.exit(SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, f"Failed to translate disk {disk_num} partition {partition_num} to a device")) + + force_push = False + if options: + opts = options.split(",") + if "force_push" in opts: + force_push = True + + try: + if branch: + if not og_git.create_branch(device = device, name = branch): + sys.exit(SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, f"Failed to create branch")) + + og_git.commit(device = device, message = msg) + og_git.push(device = device, force = force_push) + except Exception as ex: + sys.exit(SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, f"Exception during commit: {ex}")) + return 0 def main(): - if len(sys.argv) != 5: - import json - args = json.dumps(sys.argv) - sys.exit(SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, f"Incorrect number of arguments. Usage: ModificarImagenGit disk_num partition_num image_name repo msg. Received args: {args}")) - - # repo - repositorio, ip address. Opcional porque oglive lo recibe como parametro de kernel - # tag - tag a crear automaticamente. Opcional, no necesitamos crear un tag siempre. - disk_num, partition_num, image_name, msg = sys.argv[1:5] + parser = argparse.ArgumentParser( + prog = "OpenGnsys Git Image Modify", + description = "Commits changes to a partition to a git repository" + ) + parser.add_argument("--disk", type=int, metavar="DISK", required=True, help="Disk ID") + parser.add_argument("--partition", type=int, metavar="PART", required=True, help="Disk partition") + parser.add_argument("--repository", type=str, metavar="REPO", required=True, help="Address of the Git repository to clone") - opengnsys_log_dir = "/opt/opengnsys/log" - ip_address = NetLib.ogGetIpAddress() - logFilePath = f"{opengnsys_log_dir}/{ip_address}.log" + parser.add_argument("--branch", type=str, metavar="BRANCH", required=False, help="Branch to automatically create") + parser.add_argument("--options", type=str, metavar="OPTS", required=False, help="Options to branch creation (forcepush)") + parser.add_argument("--message", type=str, metavar="MSG", required=False, help="Commit message") + + parser.add_help = True + + args = parser.parse_args() + + logFilePath = "/var/log/opengnsys.ModificarImagenGit.log" ogLog = OgLogger() ogLog.setLevel(logging.DEBUG) @@ -114,7 +136,7 @@ def main(): logger.info("Starting ModificarImagenGit") - retval = commit_image(disk_num, partition_num, image_name, msg) + retval = commit_image(args.disk, args.partition, args.repository, args.branch, args.options, args.message) logger.info("ModificarImagenGit done, return code %i", retval) diff --git a/ogclient/interfaceAdm/RestaurarImagenGit b/ogclient/interfaceAdm/RestaurarImagenGit index 6aa7ad7..0fd79f2 100755 --- a/ogclient/interfaceAdm/RestaurarImagenGit +++ b/ogclient/interfaceAdm/RestaurarImagenGit @@ -10,6 +10,10 @@ import DiskLib from GitLib import OpengnsysGitLibrary, NTFSImplementation, OgProgressPrinterWeb +import argparse + + + class OgLogger(logging.StreamHandler): def emit(self, record): log_types = ["command"] @@ -42,20 +46,25 @@ if __name__ == "__main__": print(f"No se pudo aumentar el límite de archivos abiertos: {e}") - if len(sys.argv) < 6: - print("Usage: python RestaurarImagenGit ") - sys.exit(1) - disk = sys.argv[1] - partition = sys.argv[2] - repo = sys.argv[3] - ipaddr = sys.argv[4] - gitref = sys.argv[5] - proto = sys.argv[6] + parser = argparse.ArgumentParser( + prog = "OpenGnsys Git Image Restore", + description = "Restores an image from Git" + ) - opengnsys_log_dir = "/opt/opengnsys/log" - ip_address = NetLib.ogGetIpAddress() - logFilePath = f"{opengnsys_log_dir}/{ip_address}.log" + parser.add_argument("--disk", type=int, metavar="DISK", required=True, help="Disk ID") + parser.add_argument("--partition", type=int, metavar="PART", required=True, help="Disk partition") + parser.add_argument("--repository", type=str, metavar="REPO", required=True, help="Address of the Git repository to clone") + parser.add_argument("--image-name", type=str, metavar="REPO", required=True, help="Name of the new image at the repository") + + parser.add_argument("--branch", type=str, metavar="BRANCH", required=True, help="Branch to check out") + parser.add_argument("--commit", type=str, metavar="COMMIT_ID", required=True, help="Commit to check out") + parser.add_help = True + + args = parser.parse_args() + + + logFilePath = "/var/log/opengnsys.RestaurarImagenGit.log" ogLog = OgLogger() ogLog.setLevel(logging.DEBUG) @@ -81,9 +90,11 @@ if __name__ == "__main__": og_git = OpengnsysGitLibrary(ntfs_implementation = ntfs_impl) og_git.progress_callback = OgProgressPrinterWeb() - device = DiskLib.ogDiskToDev(disk, partition) + device = DiskLib.ogDiskToDev(args.disk, args.partition) + if device is None: + sys.exit(SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, f"Failed to translate disk {args.disk} partition {args.partition} to a device")) - og_git.cloneRepo(repo, device, device) + og_git.cloneRepo(args.image_name, destination = device, boot_device = device, ref = args.commit, branch = args.branch) logger.info("RestaurarImagenGit Finished.") diff --git a/ogclient/lib/python3/GitLib/__init__.py b/ogclient/lib/python3/GitLib/__init__.py index d9665fd..c86afc1 100755 --- a/ogclient/lib/python3/GitLib/__init__.py +++ b/ogclient/lib/python3/GitLib/__init__.py @@ -47,6 +47,7 @@ from GitLib.disk import * from GitLib.ntfs import * from GitLib.kernel import parse_kernel_cmdline +import CacheLib #import requests @@ -286,7 +287,8 @@ class OpengnsysGitLibrary: self.repo_image_path = "oggit" self.ntfs_implementation = ntfs_implementation - self.cache_dir = self._runBashFunction("ogMountCache", []) + self.cache_dir = CacheLib.ogMountCache() + # Si no hay cache, se va a crear el .git en el FS directamente if (not self.cache_dir) and require_cache: @@ -348,6 +350,7 @@ class OpengnsysGitLibrary: self.repo_server = self.kernel_args["ogrepo"] self.ip_address = self.kernel_args["ip"] + self.initial_branch = "main" if not self.repo_server: self.logger.warning("ogrepo kernel argument wasn't passed, or was empty. Defaulting to oglive.") @@ -602,64 +605,6 @@ class OpengnsysGitLibrary: except OSError as e: self.logger.warning('Failed to delete %s. Error: %s', file_path, e) - - 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}") - - with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_file: - temp_file.write("#!/bin/bash\n") - temp_file.write("for lib in /opt/opengnsys/client/lib/engine/bin/*.lib ; do\n") - temp_file.write(" source $lib\n") - temp_file.write("done\n") - - #temp_file.write("source /opt/oglive/rootfs/opt/opengnsys/lib/engine/bin/Cache.lib") - #temp_file.write("source /opt/oglive/rootfs/opt/opengnsys/lib/engine/bin/Git.lib") - - temp_file.write(f"{function} \"$@\"\n") - - # Make the temporary file executable - os.chmod(temp_file.name, 0o755) - - self.logger.debug(f"File: {temp_file.name}") - - - # Run the temporary file - command = [temp_file.name] + arguments - self.logger.debug(f"Running: {command} {arguments}") - result = subprocess.run(command, shell=False, capture_output=True, text=True, check=True) - output = result.stdout.strip() - - self.logger.debug(f"STDOUT: {output}") - self.logger.debug(f"STDERR: {result.stderr}") - - return output - - - def _getOgRepository(self, name): return f"{self.repo_user}@{self.repo_server}:{self.repo_image_path}/{name}.git" @@ -1370,7 +1315,7 @@ class OpengnsysGitLibrary: os.symlink(real_git_dir, git_dir) - with git.Repo.init(path) as repo: + with git.Repo.init(path, initial_branch = self.initial_branch) as repo: # On NTFS, we have to unmount the filesystem to do secaudit. # Gitpython objects existing at that time may mean a dangling git process that prevents # the required unmounting. @@ -1392,7 +1337,7 @@ class OpengnsysGitLibrary: self.logger.debug("Fetching origin, callback=%s", self.progress_callback) origin.fetch(progress=self.progress_callback) - repo.heads.master.set_tracking_branch(origin.refs.master) + repo.heads.main.set_tracking_branch(origin.refs.main) metadata_ret = self._create_metadata(path, initial_creation=True) @@ -1479,7 +1424,7 @@ class OpengnsysGitLibrary: self.logger.info("initRepo done") return True - def cloneRepo(self, repo_name, destination, boot_device): + def cloneRepo(self, repo_name, destination, boot_device, ref = None, branch = None): """ Clones a repository to a specified destination and sets up the bootloader. @@ -1542,12 +1487,40 @@ class OpengnsysGitLibrary: self.logger.info("Cloning repository from %s", repo_url) - repo = git.Repo.clone_from(repo_url, destination_dir, multi_options = [f"--separate-git-dir={real_git_dir}"], progress=self.progress_callback) - if repo_is_efi: - self._efi_install(root_directory=destination_dir) + + + if branch: + # We've got a nearby branch, start from that + self.logger.debug("Cloning repo %s, branch %s", repo_url, branch) + repo = git.Repo.clone_from(repo_url, destination_dir, branch = branch, multi_options = [f"--separate-git-dir={real_git_dir}"], progress=self.progress_callback) else: - self._grub_install(root_directory=destination_dir, boot_device=boot_device) + # Start from main instead + self.logger.debug("Cloning repo %s", repo_url) + repo = git.Repo.clone_from(repo_url, destination_dir, multi_options = [f"--separate-git-dir={real_git_dir}"], progress=self.progress_callback) + + + self.logger.debug("Checking out indicated branch %s", branch) + remote_branch_ref = repo.heads[branch] + + if branch in repo.heads: + self.logger.debug("Local branch %s exists, checking it out first", branch) + repo.git.checkout(branch) + + self.logger.debug("Resetting branch %s to ref %s", branch, ref) + repo.git.reset("--hard", ref) + else: + if ref: + self.logger.debug("Local branch adjusted to ref %s", ref) + local_ref = repo.create_head(branch, ref) + else: + self.logger.debug("Local branch is set to remote branch %s", remote_branch_ref) + local_ref = repo.create_head(branch, remote_branch_ref) + local_ref.set_tracking_branch(remote_branch_ref) + + self.logger.debug("Checking out local branch %s", branch) + local_ref.checkout() + self.fs.mklostandfound(destination_dir) self._restore_metadata(destination_dir, set_device_uuids=True) @@ -1555,6 +1528,12 @@ class OpengnsysGitLibrary: if self.fs.filesystem_type(mountpoint = destination_dir) == "ntfs": self._ntfs_restore_secaudit(destination_dir) + if repo_is_efi: + self._efi_install(root_directory=destination_dir) + else: + self._grub_install(root_directory=destination_dir, boot_device=boot_device) + + self.logger.info("Clone completed.") @@ -1614,7 +1593,23 @@ class OpengnsysGitLibrary: # Restaurar cosas modificadas para git self._restore_metadata(path, destructive_only=True) - def push(self, path = None, device = None): + def create_branch(self, path = None, device = None, name = ""): + if path is None: + path = self.fs.ensure_mounted(device) + + repo = git.Repo(path) + if name in repo.branches: + self.logger.error("Can't create branch, it already exists") + return False + + self.logger.info("Creating branch %s", name) + + new_branch = repo.create_head(name) + repo.head.reference = new_branch + repo.head.reset(index=True, working_tree=True) + return True + + def push(self, path = None, device = None, force = False): """ Push local changes to ogrepository @@ -1633,9 +1628,10 @@ class OpengnsysGitLibrary: return origin = repo.remotes["origin"] - repo.heads.master.set_tracking_branch(origin.refs.master) + repo.heads.main.set_tracking_branch(origin.refs.main) - origin.push(progress=self.progress_callback) + ret = origin.push(progress=self.progress_callback, force = force) + ret.raise_if_error() #repo.git.push("--set-upstream", "origin", repo.head.ref, "--force") # force = True) @@ -1791,7 +1787,6 @@ if __name__ == '__main__': og_git = OpengnsysGitLibrary(ntfs_implementation = ntfs_impl) - # og_git._runBashFunction("ogMountCache", []) # if args.init_repo: diff --git a/ogclient/lib/python3/GitLib/filesystem.py b/ogclient/lib/python3/GitLib/filesystem.py index fb23619..b724f46 100644 --- a/ogclient/lib/python3/GitLib/filesystem.py +++ b/ogclient/lib/python3/GitLib/filesystem.py @@ -41,7 +41,7 @@ class FilesystemLibrary: try: with open(file, "r", encoding='utf-8') as f: data = f.read() - except IOError as io_err: + except Exception as io_err: self.logger.debug("IO Error reading file %s: %s", file, io_err) return data @@ -51,7 +51,7 @@ class FilesystemLibrary: try: data = os.readlink(file) - except IOError as io_err: + except Exception as io_err: self.logger.debug("IO Error reading link %s: %s", file, io_err) return data @@ -106,13 +106,16 @@ class FilesystemLibrary: self.logger.debug("PID %s (%s) is has a working directory within %s: %s", pid_dir, command_name, path, working_dir) pids_using_path[pid_dir] = { "cmd" : command_name, "args" : cmdline} else: - for fd_file in os.listdir(fd_path): - fd_file_full_path = os.path.join(fd_path, fd_file) - target = self._read_link(fd_file_full_path) + try: + for fd_file in os.listdir(fd_path): + fd_file_full_path = os.path.join(fd_path, fd_file) + target = self._read_link(fd_file_full_path) - if target.startswith(path): - self.logger.debug("PID %s (%s) is has an open file within %s: %s", pid_dir, command_name, path, target) - pids_using_path[pid_dir] = { "cmd" : command_name, "args" : cmdline} + if target.startswith(path): + self.logger.debug("PID %s (%s) is has an open file within %s: %s", pid_dir, command_name, path, target) + pids_using_path[pid_dir] = { "cmd" : command_name, "args" : cmdline} + except IOError as ioerr: + self.logger.warning("Exception during listdir: %s", ioerr) return pids_using_path