import os import json import sys import argparse from gentooimgr import LOG import gentooimgr.configs import multiprocessing # A day in seconds: DAY_IN_SECONDS = 60*60*24 # days until the iso is old DAYS = 1 # Define threads to compile packages with THREADS = multiprocessing.cpu_count() # URL to latest image text file, defaults to amd64. This is parsed to find latest iso to download ARCHITECTURE = "amd64" GENTOO_BASE_ISO_URL = f"https://distfiles.gentoo.org/releases/{ARCHITECTURE}/autobuilds/current-install-{ARCHITECTURE}-minimal/" GENTOO_BASE_STAGE_OPENRC_URL = f"https://distfiles.gentoo.org/releases/{ARCHITECTURE}/autobuilds/current-stage3-{ARCHITECTURE}-openrc/" GENTOO_BASE_STAGE_SYSTEMD_URL = f"https://distfiles.gentoo.org/releases/{ARCHITECTURE}/autobuilds/current-stage3-{ARCHITECTURE}-systemd/" GENTOO_LATEST_ISO_FILE = f"latest-install-{ARCHITECTURE}-minimal.txt" GENTOO_LATEST_STAGE_OPENRC_FILE = f"latest-stage3-{ARCHITECTURE}-openrc.txt" GENTOO_LATEST_STAGE_SYSTEMD_FILE = f"latest-stage3-{ARCHITECTURE}-systemd.txt" GENTOO_PORTAGE_FILE = "http://distfiles.gentoo.org/snapshots/portage-latest.tar.xz" # No architecture, no txt files to determine latest. GENTOO_MOUNT = "/mnt/gentoo" GENTOO_IMG_NAME = "gentoo.qcow2" GENTOO_FILE_HASH_RE = r"^Hash\: ([\w]*)$" GENTOO_FILE_ISO_RE = r"^(install-[\w\-_\.]*.iso) ([\d]*)" GENTOO_FILE_ISO_HASH_RE = r"^([\w]*) (install-[\w\-_\.]*.iso)$" GENTOO_FILE_STAGE3_RE = r"^(stage3-[\w\-_\.]*.tar.*) ([\d]*)" GENTOO_FILE_STAGE3_HASH_RE = r"^([\w]*) (stage3-[\w\-_\.]*.tar.*)$" # TODO: Repo regex to replace attributes, use function to do so as find key will change. def replace_repos_conf(key, value): pass CLOUD_MODULES = [ "iscsi_tcp" ] def load_config(path): assert path, "load config called with nothing" if os.path.exists(path): with open(path, 'r') as f: try: return json.loads(f.read()) except Exception as e: LOG.error(f"ERROR loading {path}") raise return {} def load_default_config(config_name): """This is called when a --config option is set. --kernel options update the resulting config, whether it be 'base' or other. If user is supplying their own configuration, this is not called. """ name, ext = os.path.splitext(config_name) if not name in gentooimgr.configs.KNOWN_CONFIGS: return {} json_file = os.path.join(gentooimgr.configs.CONFIG_DIR, config_name) ret = {} with open(json_file, 'r') as f: try: ret = json.loads(f.read()) except Exception as e: LOG.error(f"loading {json_file} {e}") return ret def inherit_config(config: dict) -> dict: """Returns the json file that the inherit key specifies; will recursively update if inherit values are set. """ configuration = load_default_config(config.get("inherit")) if not configuration: configuration = load_config(config.get("inherit")) if not configuration: sys.stderr.write(f"\tWW: Warning: Inherited configuration {config.get('inherit')} is not found.\n") return {} if configuration.get("inherit"): configuration.update(inherit_config(configuration.get("inherit"))) return configuration def determine_config(args: argparse.Namespace) -> dict: """Check argparser options and return the most valid configuration The "package" key/value object overrides everything that is set, it does not update() them. If you override "base" package set, it's exactly what you set. It makes more sense to do it this way. For example, if you have a dist kernel config, you don't want the base.json to update and include all non-dist kernel options as it would add a lot of used space for unused functionality. The package set is only overridden in the top level json configuration file though; If you have multiple inherits, those package sets will be combined before the parent package set overrides with the keys that are set. If you have base.json and base2.json that contain multiple layers of "base" packages, ie: base: ['foo'] and base2: ['bar'] then you will have in yours.json: packages { base: ['foo', 'bar'] } and unless you set "base", that is what you'll get. If you check `status` action, it will flatten all configurations into one, so the "inherit" key will always be null. :Returns: - configuration from json to dict """ # Check custom configuration configuration = load_default_config(args.config or 'base.json') if not configuration and args.config: configuration = load_config(args.config) if not configuration: LOG.error(f"\tWW: Warning: Configuration {args.config} is empty\n") else: if configuration.get("inherit"): # newpkgs = configuration.get("packages", {}) inherited = inherit_config(configuration) new_packages = configuration.get("packages", {}) old_packages = inherited.get("packages", {}) inherited.update(configuration) # Set back old package dict and then update only what is set in new: inherited['packages'] = old_packages for key, pkgs in new_packages.items(): if pkgs: inherited['packages'][key] = pkgs return inherited return configuration