diff options
Diffstat (limited to 'python/vyos/system')
-rw-r--r-- | python/vyos/system/compat.py | 25 | ||||
-rw-r--r-- | python/vyos/system/disk.py | 2 | ||||
-rw-r--r-- | python/vyos/system/grub.py | 78 | ||||
-rw-r--r-- | python/vyos/system/grub_util.py | 70 | ||||
-rw-r--r-- | python/vyos/system/image.py | 13 |
5 files changed, 173 insertions, 15 deletions
diff --git a/python/vyos/system/compat.py b/python/vyos/system/compat.py index 319c3dabf..37b834ad6 100644 --- a/python/vyos/system/compat.py +++ b/python/vyos/system/compat.py @@ -1,4 +1,4 @@ -# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -27,7 +27,7 @@ TMPL_GRUB_COMPAT: str = 'grub/grub_compat.j2' # define regexes and variables REGEX_VERSION = r'^menuentry "[^\n]*{\n[^}]*\s+linux /boot/(?P<version>\S+)/[^}]*}' REGEX_MENUENTRY = r'^menuentry "[^\n]*{\n[^}]*\s+linux /boot/(?P<version>\S+)/vmlinuz (?P<options>[^\n]+)\n[^}]*}' -REGEX_CONSOLE = r'^.*console=(?P<console_type>[^\s\d]+)(?P<console_num>[\d]+).*$' +REGEX_CONSOLE = r'^.*console=(?P<console_type>[^\s\d]+)(?P<console_num>[\d]+)(,(?P<console_speed>[\d]+))?.*$' REGEX_SANIT_CONSOLE = r'\ ?console=[^\s\d]+[\d]+(,\d+)?\ ?' REGEX_SANIT_INIT = r'\ ?init=\S*\ ?' REGEX_SANIT_QUIET = r'\ ?quiet\ ?' @@ -131,6 +131,8 @@ def parse_entry(entry: tuple) -> dict: # find console type and number regex_filter = compile(REGEX_CONSOLE) entry_dict.update(regex_filter.match(entry[1]).groupdict()) + speed = entry_dict.get('console_speed', None) + entry_dict['console_speed'] = speed if speed is not None else '115200' entry_dict['boot_opts'] = sanitize_boot_opts(entry[1]) return entry_dict @@ -168,9 +170,12 @@ def prune_vyos_versions(root_dir: str = '') -> None: if not root_dir: root_dir = disk.find_persistence() - for version in grub.version_list(): + version_files = Path(f'{root_dir}/{grub.GRUB_DIR_VYOS_VERS}').glob('*.cfg') + + for file in version_files: + version = Path(file).stem if not Path(f'{root_dir}/boot/{version}').is_dir(): - grub.version_del(version) + grub.version_del(version, root_dir) def update_cfg_ver(root_dir:str = '') -> int: @@ -244,13 +249,17 @@ def update_version_list(root_dir: str = '') -> list[dict]: menu_entries = list(filter(lambda x: x.get('version') != ver, menu_entries)) + # reset boot_opts in case of config update + for entry in menu_entries: + entry['boot_opts'] = grub.get_boot_opts(entry['version']) + add = list(set(current_versions) - set(menu_versions)) for ver in add: last = menu_entries[0].get('version') new = deepcopy(list(filter(lambda x: x.get('version') == last, menu_entries))) for e in new: - boot_opts = e.get('boot_opts').replace(last, ver) + boot_opts = grub.get_boot_opts(ver) e.update({'version': ver, 'boot_opts': boot_opts}) menu_entries = new + menu_entries @@ -271,9 +280,11 @@ def grub_cfg_fields(root_dir: str = '') -> dict: root_dir = disk.find_persistence() grub_cfg_main = f'{root_dir}/{grub.GRUB_CFG_MAIN}' + grub_vars = f'{root_dir}/{grub.CFG_VYOS_VARS}' - fields = {'default': 0, 'timeout': 5} - # 'default' and 'timeout' from legacy grub.cfg + fields = grub.vars_read(grub_vars) + # 'default' and 'timeout' from legacy grub.cfg resets 'default' to + # index, rather than uuid fields |= grub.vars_read(grub_cfg_main) fields['tools_version'] = SYSTEM_CFG_VER diff --git a/python/vyos/system/disk.py b/python/vyos/system/disk.py index b8a2c0f35..7860d719f 100644 --- a/python/vyos/system/disk.py +++ b/python/vyos/system/disk.py @@ -78,7 +78,7 @@ def parttable_create(drive_path: str, root_size: int) -> None: run(command) # update partitons in kernel sync() - run(f'partprobe {drive_path}') + run(f'partx -u {drive_path}') partitions: list[str] = partition_list(drive_path) diff --git a/python/vyos/system/grub.py b/python/vyos/system/grub.py index a94729964..2e8b20972 100644 --- a/python/vyos/system/grub.py +++ b/python/vyos/system/grub.py @@ -1,4 +1,4 @@ -# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -45,10 +45,14 @@ TMPL_GRUB_MODULES: str = 'grub/grub_modules.j2' TMPL_GRUB_OPTS: str = 'grub/grub_options.j2' TMPL_GRUB_COMMON: str = 'grub/grub_common.j2' +# default boot options +BOOT_OPTS_STEM: str = 'boot=live rootdelay=5 noautologin net.ifnames=0 biosdevname=0 vyos-union=/boot/' + # prepare regexes REGEX_GRUB_VARS: str = r'^set (?P<variable_name>.+)=[\'"]?(?P<variable_value>.*)(?<![\'"])[\'"]?$' REGEX_GRUB_MODULES: str = r'^insmod (?P<module_name>.+)$' REGEX_KERNEL_CMDLINE: str = r'^BOOT_IMAGE=/(?P<boot_type>boot|live)/((?P<image_version>.+)/)?vmlinuz.*$' +REGEX_GRUB_BOOT_OPTS: str = r'^\s*set boot_opts="(?P<boot_opts>[^$]+)"$' def install(drive_path: str, boot_dir: str, efi_dir: str, id: str = 'VyOS') -> None: @@ -95,7 +99,8 @@ def gen_version_uuid(version_name: str) -> str: def version_add(version_name: str, root_dir: str = '', - boot_opts: str = '') -> None: + boot_opts: str = '', + boot_opts_config = None) -> None: """Add a new VyOS version to GRUB loader configuration Args: @@ -112,7 +117,9 @@ def version_add(version_name: str, version_config, TMPL_VYOS_VERSION, { 'version_name': version_name, 'version_uuid': gen_version_uuid(version_name), - 'boot_opts': boot_opts + 'boot_opts_default': BOOT_OPTS_STEM + version_name, + 'boot_opts': boot_opts, + 'boot_opts_config': boot_opts_config }) @@ -294,12 +301,43 @@ def vars_write(grub_cfg: str, grub_vars: dict[str, str]) -> None: """ render(grub_cfg, TMPL_GRUB_VARS, {'vars': grub_vars}) +def get_boot_opts(version_name: str, root_dir: str = '') -> str: + """Read boot_opts setting from version file; return default setting on + any failure. + + Args: + version_name (str): version name + root_dir (str, optional): an optional path to the root directory. + Defaults to empty. + """ + if not root_dir: + root_dir = disk.find_persistence() + + boot_opts_default: str = BOOT_OPTS_STEM + version_name + boot_opts: str = '' + regex_filter = re_compile(REGEX_GRUB_BOOT_OPTS) + version_config: str = f'{root_dir}/{GRUB_DIR_VYOS_VERS}/{version_name}.cfg' + try: + config_text: list[str] = Path(version_config).read_text().splitlines() + except FileNotFoundError: + return boot_opts_default + for line in config_text: + search_result = regex_filter.fullmatch(line) + if search_result: + search_dict = search_result.groupdict() + boot_opts = search_dict.get('boot_opts', '') + break + + if not boot_opts: + boot_opts = boot_opts_default + + return boot_opts def set_default(version_name: str, root_dir: str = '') -> None: """Set version as default boot entry Args: - version_name (str): versio name + version_name (str): version name root_dir (str, optional): an optional path to the root directory. Defaults to empty. """ @@ -354,5 +392,33 @@ def set_console_type(console_type: str, root_dir: str = '') -> None: vars_current['console_type'] = str(console_type) vars_write(vars_file, vars_current) -def set_raid(root_dir: str = '') -> None: - pass +def set_console_speed(console_speed: str, root_dir: str = '') -> None: + """Write default console speed to GRUB configuration + + Args: + console_speed (str): default console speed + root_dir (str, optional): an optional path to the root directory. + Defaults to empty. + """ + if not root_dir: + root_dir = disk.find_persistence() + + vars_file: str = f'{root_dir}/{CFG_VYOS_VARS}' + vars_current: dict[str, str] = vars_read(vars_file) + vars_current['console_speed'] = str(console_speed) + vars_write(vars_file, vars_current) + +def set_kernel_cmdline_options(cmdline_options: str, version_name: str, + root_dir: str = '') -> None: + """Write additional cmdline options to GRUB configuration + + Args: + cmdline_options (str): cmdline options to add to default boot line + version_name (str): image version name + root_dir (str, optional): an optional path to the root directory. + """ + if not root_dir: + root_dir = disk.find_persistence() + + version_add(version_name=version_name, root_dir=root_dir, + boot_opts_config=cmdline_options) diff --git a/python/vyos/system/grub_util.py b/python/vyos/system/grub_util.py new file mode 100644 index 000000000..4a3d8795e --- /dev/null +++ b/python/vyos/system/grub_util.py @@ -0,0 +1,70 @@ +# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +from vyos.system import disk, grub, image, compat + +@compat.grub_cfg_update +def set_console_speed(console_speed: str, root_dir: str = '') -> None: + """Write default console speed to GRUB configuration + + Args: + console_speed (str): default console speed + root_dir (str, optional): an optional path to the root directory. + Defaults to empty. + """ + if not root_dir: + root_dir = disk.find_persistence() + + grub.set_console_speed(console_speed, root_dir) + +@image.if_not_live_boot +def update_console_speed(console_speed: str, root_dir: str = '') -> None: + """Update console_speed if different from current value""" + + if not root_dir: + root_dir = disk.find_persistence() + + vars_file: str = f'{root_dir}/{grub.CFG_VYOS_VARS}' + vars_current: dict[str, str] = grub.vars_read(vars_file) + console_speed_current = vars_current.get('console_speed', None) + if console_speed != console_speed_current: + set_console_speed(console_speed, root_dir) + +@compat.grub_cfg_update +def set_kernel_cmdline_options(cmdline_options: str, version: str = '', + root_dir: str = '') -> None: + """Write Kernel CLI cmdline options to GRUB configuration""" + if not root_dir: + root_dir = disk.find_persistence() + + if not version: + version = image.get_running_image() + + grub.set_kernel_cmdline_options(cmdline_options, version, root_dir) + +@image.if_not_live_boot +def update_kernel_cmdline_options(cmdline_options: str, + root_dir: str = '') -> None: + """Update Kernel custom cmdline options""" + if not root_dir: + root_dir = disk.find_persistence() + + version = image.get_running_image() + + boot_opts_current = grub.get_boot_opts(version, root_dir) + boot_opts_proposed = grub.BOOT_OPTS_STEM + f'{version} {cmdline_options}' + + if boot_opts_proposed != boot_opts_current: + set_kernel_cmdline_options(cmdline_options, version, root_dir) diff --git a/python/vyos/system/image.py b/python/vyos/system/image.py index 514275654..5460e6a36 100644 --- a/python/vyos/system/image.py +++ b/python/vyos/system/image.py @@ -1,4 +1,4 @@ -# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -15,6 +15,7 @@ from pathlib import Path from re import compile as re_compile +from functools import wraps from tempfile import TemporaryDirectory from typing import TypedDict @@ -262,6 +263,16 @@ def is_live_boot() -> bool: return True return False +def if_not_live_boot(func): + """Decorator to call function only if not live boot""" + @wraps(func) + def wrapper(*args, **kwargs): + if not is_live_boot(): + ret = func(*args, **kwargs) + return ret + return None + return wrapper + def is_running_as_container() -> bool: if Path('/.dockerenv').exists(): return True |