From 96b65e90fbfa1fe63d97929ac86fc910abb0caa9 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Sat, 21 Oct 2023 13:33:37 -0500 Subject: image: T4516: support for interoperability of legacy/new image tools This commit allows management of system images with either new or legacy tools: 'add/delete/rename system image' and 'set default' are translated appropriately on booting between images with the old and new tools. Consequently, the warning of the initial commit of T4516 is dropped. --- src/op_mode/image_info.py | 7 +- src/op_mode/image_installer.py | 17 +++-- src/op_mode/image_manager.py | 7 +- src/system/grub_update.py | 141 +++++------------------------------------ 4 files changed, 36 insertions(+), 136 deletions(-) (limited to 'src') diff --git a/src/op_mode/image_info.py b/src/op_mode/image_info.py index 14dca7476..791001e00 100755 --- a/src/op_mode/image_info.py +++ b/src/op_mode/image_info.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 VyOS maintainers and contributors +# Copyright 2023 VyOS maintainers and contributors # # This file is part of VyOS. # @@ -91,10 +91,7 @@ def show_images_summary(raw: bool) -> Union[image.BootDetails, str]: def show_images_details(raw: bool) -> Union[list[image.ImageDetails], str]: - images: list[str] = grub.version_list() - images_details: list[image.ImageDetails] = list() - for image_name in images: - images_details.append(image.get_details(image_name)) + images_details = image.get_images_details() if raw: return images_details diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py index 12d32968c..2d998f5e1 100755 --- a/src/op_mode/image_installer.py +++ b/src/op_mode/image_installer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 VyOS maintainers and contributors +# Copyright 2023 VyOS maintainers and contributors # # This file is part of VyOS. # @@ -28,7 +28,7 @@ from psutil import disk_partitions from vyos.configtree import ConfigTree from vyos.remote import download -from vyos.system import disk, grub, image +from vyos.system import disk, grub, image, compat, SYSTEM_CFG_VER from vyos.template import render from vyos.utils.io import ask_input, ask_yes_no from vyos.utils.file import chmod_2775 @@ -462,6 +462,7 @@ def install_image() -> None: exit(1) +@compat.grub_cfg_update def add_image(image_path: str) -> None: """Add a new image @@ -488,10 +489,16 @@ def add_image(image_path: str) -> None: Path(DIR_ROOTFS_SRC).mkdir(mode=0o755, parents=True) disk.partition_mount(f'{DIR_ISO_MOUNT}/live/filesystem.squashfs', DIR_ROOTFS_SRC, 'squashfs') - version_file: str = Path( - f'{DIR_ROOTFS_SRC}/opt/vyatta/etc/version').read_text() + + cfg_ver: str = image.get_image_tools_version(DIR_ROOTFS_SRC) + version_name: str = image.get_image_version(DIR_ROOTFS_SRC) + disk.partition_umount(f'{DIR_ISO_MOUNT}/live/filesystem.squashfs') - version_name: str = version_file.lstrip('Version: ').strip() + + if cfg_ver < SYSTEM_CFG_VER: + raise compat.DowngradingImageTools( + f'Adding image would downgrade image tools to v.{cfg_ver}; disallowed') + image_name: str = ask_input(MSG_INPUT_IMAGE_NAME, version_name) set_as_default: bool = ask_yes_no(MSG_INPUT_IMAGE_DEFAULT, default=True) diff --git a/src/op_mode/image_manager.py b/src/op_mode/image_manager.py index 76fc4367f..725c40613 100755 --- a/src/op_mode/image_manager.py +++ b/src/op_mode/image_manager.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 VyOS maintainers and contributors +# Copyright 2023 VyOS maintainers and contributors # # This file is part of VyOS. # @@ -22,10 +22,11 @@ from pathlib import Path from shutil import rmtree from sys import exit -from vyos.system import disk, grub, image +from vyos.system import disk, grub, image, compat from vyos.utils.io import ask_yes_no +@compat.grub_cfg_update def delete_image(image_name: str) -> None: """Remove installed image files and boot entry @@ -57,6 +58,7 @@ def delete_image(image_name: str) -> None: exit(f'Unable to remove the image "{image_name}": {err}') +@compat.grub_cfg_update def set_image(image_name: str) -> None: """Set default boot image @@ -86,6 +88,7 @@ def set_image(image_name: str) -> None: exit(f'Unable to set default image "{image_name}": {err}') +@compat.grub_cfg_update def rename_image(name_old: str, name_new: str) -> None: """Rename installed image diff --git a/src/system/grub_update.py b/src/system/grub_update.py index 1ae66464b..da1986e9d 100644 --- a/src/system/grub_update.py +++ b/src/system/grub_update.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2022 VyOS maintainers and contributors +# Copyright 2023 VyOS maintainers and contributors # # This file is part of VyOS. # @@ -18,23 +18,11 @@ # VyOS. If not, see . from pathlib import Path -from re import compile, MULTILINE, DOTALL from sys import exit -from vyos.system import disk, grub, image +from vyos.system import disk, grub, image, compat, SYSTEM_CFG_VER from vyos.template import render -# define configuration version -CFG_VER = 1 - -# define regexes and variables -REGEX_VERSION = r'^menuentry "[^\n]*{\n[^}]*\s+linux /boot/(?P\S+)/[^}]*}' -REGEX_MENUENTRY = r'^menuentry "[^\n]*{\n[^}]*\s+linux /boot/(?P\S+)/vmlinuz (?P[^\n]+)\n[^}]*}' -REGEX_CONSOLE = r'^.*console=(?P[^\s\d]+)(?P[\d]+).*$' -REGEX_SANIT_CONSOLE = r'\ ?console=[^\s\d]+[\d]+(,\d+)?\ ?' -REGEX_SANIT_INIT = r'\ ?init=\S*\ ?' -PW_RESET_OPTION = 'init=/opt/vyatta/sbin/standalone_root_pw_reset' - def cfg_check_update() -> bool: """Check if GRUB structure update is required @@ -43,111 +31,10 @@ def cfg_check_update() -> bool: bool: False if not required, True if required """ current_ver = grub.get_cfg_ver() - if current_ver and current_ver >= CFG_VER: + if current_ver and current_ver >= SYSTEM_CFG_VER: return False - else: - return True - - -def find_versions(menu_entries: list) -> list: - """Find unique VyOS versions from menu entries - - Args: - menu_entries (list): a list with menu entries - - Returns: - list: List of installed versions - """ - versions = [] - for vyos_ver in menu_entries: - versions.append(vyos_ver.get('version')) - # remove duplicates - versions = list(set(versions)) - return versions - -def filter_unparsed(grub_path: str) -> str: - """Find currently installed VyOS version - - Args: - grub_path (str): a path to the grub.cfg file - - Returns: - str: unparsed grub.cfg items - """ - config_text = Path(grub_path).read_text() - regex_filter = compile(REGEX_VERSION, MULTILINE | DOTALL) - filtered = regex_filter.sub('', config_text) - regex_filter = compile(grub.REGEX_GRUB_VARS, MULTILINE) - filtered = regex_filter.sub('', filtered) - regex_filter = compile(grub.REGEX_GRUB_MODULES, MULTILINE) - filtered = regex_filter.sub('', filtered) - # strip extra new lines - filtered = filtered.strip() - return filtered - - -def sanitize_boot_opts(boot_opts: str) -> str: - """Sanitize boot options from console and init - - Args: - boot_opts (str): boot options - - Returns: - str: sanitized boot options - """ - regex_filter = compile(REGEX_SANIT_CONSOLE) - boot_opts = regex_filter.sub('', boot_opts) - regex_filter = compile(REGEX_SANIT_INIT) - boot_opts = regex_filter.sub('', boot_opts) - - return boot_opts - - -def parse_entry(entry: tuple) -> dict: - """Parse GRUB menuentry - - Args: - entry (tuple): tuple of (version, options) - - Returns: - dict: dictionary with parsed options - """ - # save version to dict - entry_dict = {'version': entry[0]} - # detect boot mode type - if PW_RESET_OPTION in entry[1]: - entry_dict['bootmode'] = 'pw_reset' - else: - entry_dict['bootmode'] = 'normal' - # find console type and number - regex_filter = compile(REGEX_CONSOLE) - entry_dict.update(regex_filter.match(entry[1]).groupdict()) - entry_dict['boot_opts'] = sanitize_boot_opts(entry[1]) - - return entry_dict - - -def parse_menuntries(grub_path: str) -> list: - """Parse all GRUB menuentries - - Args: - grub_path (str): a path to GRUB config file - - Returns: - list: list with menu items (each item is a dict) - """ - menuentries = [] - # read configuration file - config_text = Path(grub_path).read_text() - # parse menuentries to tuples (version, options) - regex_filter = compile(REGEX_MENUENTRY, MULTILINE) - filter_results = regex_filter.findall(config_text) - # parse each entry - for entry in filter_results: - menuentries.append(parse_entry(entry)) - - return menuentries + return True if __name__ == '__main__': @@ -162,12 +49,12 @@ if __name__ == '__main__': root_dir = disk.find_persistence() # read current GRUB config - grub_cfg_main = f'{root_dir}/{image.GRUB_DIR_MAIN}/grub.cfg' + grub_cfg_main = f'{root_dir}/{grub.GRUB_CFG_MAIN}' vars = grub.vars_read(grub_cfg_main) modules = grub.modules_read(grub_cfg_main) - vyos_menuentries = parse_menuntries(grub_cfg_main) - vyos_versions = find_versions(vyos_menuentries) - unparsed_items = filter_unparsed(grub_cfg_main) + vyos_menuentries = compat.parse_menuentries(grub_cfg_main) + vyos_versions = compat.find_versions(vyos_menuentries) + unparsed_items = compat.filter_unparsed(grub_cfg_main) # find default values default_entry = vyos_menuentries[int(vars['default'])] @@ -185,13 +72,12 @@ if __name__ == '__main__': # print(f'unparsed_items: {unparsed_items}') # create new files - grub_cfg_vars = f'{root_dir}/{image.CFG_VYOS_VARS}' + grub_cfg_vars = f'{root_dir}/{grub.CFG_VYOS_VARS}' grub_cfg_modules = f'{root_dir}/{grub.CFG_VYOS_MODULES}' grub_cfg_platform = f'{root_dir}/{grub.CFG_VYOS_PLATFORM}' grub_cfg_menu = f'{root_dir}/{grub.CFG_VYOS_MENU}' grub_cfg_options = f'{root_dir}/{grub.CFG_VYOS_OPTIONS}' - render(grub_cfg_main, grub.TMPL_GRUB_MAIN, {}) Path(image.GRUB_DIR_VYOS).mkdir(exist_ok=True) grub.vars_write(grub_cfg_vars, vars) grub.modules_write(grub_cfg_modules, modules) @@ -210,5 +96,12 @@ if __name__ == '__main__': grub.version_add(vyos_ver, root_dir, boot_opts) # update structure version - grub.write_cfg_ver(CFG_VER, root_dir) + cfg_ver = compat.update_cfg_ver(root_dir) + grub.write_cfg_ver(cfg_ver, root_dir) + + if compat.mode(): + compat.render_grub_cfg(root_dir) + else: + render(grub_cfg_main, grub.TMPL_GRUB_MAIN, {}) + exit(0) -- cgit v1.2.3