diff --git a/src/jlmkr/actions/list.py b/src/jlmkr/actions/list.py new file mode 100644 index 0000000..04df74c --- /dev/null +++ b/src/jlmkr/actions/list.py @@ -0,0 +1,119 @@ +# SPDX-FileCopyrightText: © 2024 Jip-Hop and the Jailmakers +# +# SPDX-License-Identifier: LGPL-3.0-only + +import json +import subprocess + +from collections import defaultdict +from utils.config_parser import parse_config_file +from utils.jail_dataset import get_jail_config_path, get_jail_rootfs_path, parse_os_release +from utils.parent_dataset import get_all_jail_names + + +def list_jails(): + """ + List all available and running jails. + """ + + jails = {} + empty_value_indicator = "-" + + jail_names = get_all_jail_names() + + if not jail_names: + print("No jails.") + return 0 + + # Get running jails from machinectl + running_machines = run_command_and_parse_json(["machinectl", "list", "-o", "json"]) + # Index running_machines by machine name + # We're only interested in systemd-nspawn machines + running_machines = { + item["machine"]: item + for item in running_machines + if item["service"] == "systemd-nspawn" + } + + for jail_name in jail_names: + jail_rootfs_path = get_jail_rootfs_path(jail_name) + jails[jail_name] = {"name": jail_name, "running": False} + jail = jails[jail_name] + + config = parse_config_file(get_jail_config_path(jail_name)) + if config: + jail["startup"] = config.my_getboolean("startup") + jail["gpu_intel"] = config.my_getboolean("gpu_passthrough_intel") + jail["gpu_nvidia"] = config.my_getboolean("gpu_passthrough_nvidia") + + if jail_name in running_machines: + machine = running_machines[jail_name] + # Augment the jails dict with output from machinectl + jail["running"] = True + jail["os"] = machine["os"] or None + jail["version"] = machine["version"] or None + + addresses = machine.get("addresses") + if not addresses: + jail["addresses"] = empty_value_indicator + else: + addresses = addresses.split("\n") + jail["addresses"] = addresses[0] + if len(addresses) > 1: + jail["addresses"] += "…" + else: + # Parse os-release info ourselves + jail_platform = parse_os_release(jail_rootfs_path) + jail["os"] = jail_platform.get("ID") + jail["version"] = jail_platform.get("VERSION_ID") or jail_platform.get( + "VERSION_CODENAME" + ) + + print_table( + [ + "name", + "running", + "startup", + "gpu_intel", + "gpu_nvidia", + "os", + "version", + "addresses", + ], + sorted(jails.values(), key=lambda x: x["name"]), + empty_value_indicator, + ) + + return 0 + + +def run_command_and_parse_json(command): + result = subprocess.run(command, capture_output=True, text=True) + output = result.stdout.strip() + + try: + parsed_output = json.loads(output) + return parsed_output + except json.JSONDecodeError as e: + eprint(f"Error parsing JSON: {e}") + return None + + +def print_table(header, list_of_objects, empty_value_indicator): + # Find max width for each column + widths = defaultdict(int) + for obj in list_of_objects: + for hdr in header: + value = obj.get(hdr) + if value is None: + obj[hdr] = value = empty_value_indicator + widths[hdr] = max(widths[hdr], len(str(value)), len(str(hdr))) + + # Print header + print( + UNDERLINE + " ".join(hdr.upper().ljust(widths[hdr]) for hdr in header) + NORMAL + ) + + # Print rows + for obj in list_of_objects: + print(" ".join(str(obj.get(hdr)).ljust(widths[hdr]) for hdr in header)) diff --git a/src/jlmkr/donor/jlmkr.py b/src/jlmkr/donor/jlmkr.py index 62543ec..b8c510a 100755 --- a/src/jlmkr/donor/jlmkr.py +++ b/src/jlmkr/donor/jlmkr.py @@ -127,15 +127,7 @@ JAIL_CONFIG_NAME = "config" JAIL_ROOTFS_NAME = "rootfs" SHORTNAME = "jlmkr" -# Only set a color if we have an interactive tty -if sys.stdout.isatty(): - BOLD = "\033[1m" - RED = "\033[91m" - YELLOW = "\033[93m" - UNDERLINE = "\033[4m" - NORMAL = "\033[0m" -else: - BOLD = RED = YELLOW = UNDERLINE = NORMAL = "" +from utils.console import BOLD, RED, YELLOW, UNDERLINE, NORMAL DISCLAIMER = f"""{YELLOW}{BOLD}{__disclaimer__}{NORMAL}""" @@ -173,21 +165,7 @@ class Chroot: os.chdir(self.initial_cwd) -def eprint(*args, **kwargs): - """ - Print to stderr. - """ - print(*args, file=sys.stderr, **kwargs) - - -def fail(*args, **kwargs): - """ - Print to stderr and exit. - """ - eprint(*args, **kwargs) - sys.exit(1) - - +from utils.console import eprint, fail from utils.jail_dataset import get_jail_path, get_jail_config_path, get_jail_rootfs_path diff --git a/src/jlmkr/utils/console.py b/src/jlmkr/utils/console.py new file mode 100644 index 0000000..20018f7 --- /dev/null +++ b/src/jlmkr/utils/console.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: © 2024 Jip-Hop and the Jailmakers +# +# SPDX-License-Identifier: LGPL-3.0-only + +import sys + + +# Only set a color if we have an interactive tty +if sys.stdout.isatty(): + BOLD = "\033[1m" + RED = "\033[91m" + YELLOW = "\033[93m" + UNDERLINE = "\033[4m" + NORMAL = "\033[0m" +else: + BOLD = RED = YELLOW = UNDERLINE = NORMAL = "" + + +def eprint(*args, **kwargs): + """ + Print to stderr. + """ + print(*args, file=sys.stderr, **kwargs) + + +def fail(*args, **kwargs): + """ + Print to stderr and exit. + """ + eprint(*args, **kwargs) + sys.exit(1)