From 9bcdb9175fe1202fbec1a0064cba86be840adb3a Mon Sep 17 00:00:00 2001 From: Packit Service Date: Feb 04 2021 16:12:34 +0000 Subject: boom-boot-1.3 base --- diff --git a/bin/boom b/bin/boom index f5c8a89..1b8d9d9 100755 --- a/bin/boom +++ b/bin/boom @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python import sys from boom.command import main diff --git a/boom.spec b/boom.spec index a5797ed..8b297e9 100644 --- a/boom.spec +++ b/boom.spec @@ -2,7 +2,7 @@ %global sphinx_docs 1 Name: boom -Version: 1.1 +Version: 1.3 Release: 1%{?dist} Summary: %{summary} diff --git a/boom/__init__.py b/boom/__init__.py index fa17610..b5ef463 100644 --- a/boom/__init__.py +++ b/boom/__init__.py @@ -35,6 +35,6 @@ from __future__ import print_function from ._boom import * from ._boom import __all__ -__version__ = "1.1" +__version__ = "1.3" # vim: set et ts=4 sw=4 : diff --git a/boom/_boom.py b/boom/_boom.py index 690386e..db2b9d3 100644 --- a/boom/_boom.py +++ b/boom/_boom.py @@ -607,6 +607,9 @@ class Selection(object): os_root_opts_btrfs = None os_options = None + # Should results include the null profile? + allow_null_profile = False + # HostProfile fields host_id = None host_name = None @@ -684,7 +687,7 @@ class Selection(object): os_id=None, os_name=None, os_short_name=None, os_version=None, os_version_id=None, os_options=None, os_uname_pattern=None, os_kernel_pattern=None, - os_initramfs_pattern=None, host_id=None, + os_initramfs_pattern=None, allow_null=False, host_id=None, host_name=None, host_label=None, host_short_name=None, host_add_opts=None, host_del_opts=None, path=None, timestamp=None, img_id=None): @@ -713,6 +716,7 @@ class Selection(object): :param os_uname_pattern: The os_uname_pattern to match :param os_kernel_pattern: The kernel_pattern to match :param os_initramfs_pattern: The initramfs_pattern to match + :param allow_null: Allow selecting the null profile :param host_id: The host identifier to match :param host_name: The host name to match :param host_label: The host label to match @@ -745,6 +749,7 @@ class Selection(object): self.os_uname_pattern = os_uname_pattern self.os_kernel_pattern = os_kernel_pattern self.os_initramfs_pattern = os_initramfs_pattern + self.allow_null_profile = allow_null self.host_id = host_id self.host_name = host_name self.host_label = host_label @@ -886,7 +891,7 @@ def blank_or_comment(line): return not line.strip() or line.lstrip().startswith('#') -def parse_name_value(nvp, separator="="): +def parse_name_value(nvp, separator="=", allow_empty=False): """Parse a name value pair string. Parse a ``name='value'`` style string into its component parts, @@ -907,16 +912,19 @@ def parse_name_value(nvp, separator="="): # whitespace anywhere within the string. name, value = nvp.rstrip('\n').split(separator, 1) except ValueError: - raise val_err + if not allow_empty or not nvp: + raise val_err + name = nvp.strip(separator) + value = None # Value cannot start with '=' - if value.startswith('='): + if value and value.startswith('='): raise val_err name = name.strip() - value = value.lstrip() + value = value.lstrip() if value else None - if "#" in value: + if value and "#" in value: value, comment = value.split("#", 1) valid_name_chars = string.ascii_letters + string.digits + "_-,.'\"" @@ -925,8 +933,12 @@ def parse_name_value(nvp, separator="="): raise ValueError("Invalid characters in name: %s (%s)" % (name, bad_chars)) - if value.startswith('"') or value.startswith("'"): - value = value[1:-1] + if value: + if value.startswith('"') or value.startswith("'"): + quotes = "\"'" + value = value.rstrip(quotes) + value = value.lstrip(quotes) + return (name, value) diff --git a/boom/bootloader.py b/boom/bootloader.py index 1ed7157..b01b7cd 100644 --- a/boom/bootloader.py +++ b/boom/bootloader.py @@ -124,8 +124,8 @@ ENTRY_KEYS = [ BOOM_ENTRY_INITRD, BOOM_ENTRY_OPTIONS, BOOM_ENTRY_DEVICETREE, BOOM_ENTRY_ARCHITECTURE, # Optional implementation defined BLS keys - BOOM_ENTRY_GRUB_USERS, BOOM_ENTRY_GRUB_ARG, BOOM_ENTRY_GRUB_CLASS, - BOOM_ENTRY_GRUB_ID + BOOM_ENTRY_GRUB_ID, + BOOM_ENTRY_GRUB_USERS, BOOM_ENTRY_GRUB_ARG, BOOM_ENTRY_GRUB_CLASS ] #: Map Boom entry names to BLS keys @@ -657,7 +657,7 @@ class BootParams(object): return self.lvm_root_lv is not None and len(self.lvm_root_lv) @classmethod - def from_entry(cls, be): + def from_entry(cls, be, expand=False): """Recover BootParams from BootEntry. Recover BootParams values from a templated BootEntry: each @@ -672,6 +672,7 @@ class BootParams(object): is possible unless a new, valid, OsProfile is attached. :param be: The BootEntry to recover BootParams from. + :param expand: Expand bootloader environment variables. :returns: A newly initialised BootParams object. :rtype: ``BootParams`` :raises: ValueError if expected values cannot be matched. @@ -682,9 +683,6 @@ class BootParams(object): bp = BootParams(version) matches = {} - _log_debug_entry("Initialising BootParams() from " - "BootEntry(boot_id='%s')" % be.boot_id) - opts_regexes = osp.make_format_regexes(osp.options) if not opts_regexes: return None @@ -699,16 +697,19 @@ class BootParams(object): for word in be.expand_options.split(): match = re.search(exp, word) if name else re.match(exp, word) if match: - matches[word] = True if len(match.groups()): value = match.group(1) - _log_debug_entry("Matched: '%s' (%s)" % + _log_debug_entry("Matching: '%s' (%s)" % (value, name)) if name == "lvm_root_lv": if not _match_root_lv(bp.root_device, value): continue - setattr(bp, name, value) - continue + _log_debug_entry("Matched root_device=%s to %s=%s" + % (bp.root_device, name, value)) + matches[word] = True + if name: + _log_debug_entry("Matched %s=%s" % (name, value)) + setattr(bp, name, value) # The root_device key is handled specially since it is required # for a valid BootEntry. @@ -735,10 +736,10 @@ class BootParams(object): if GRUB2_EXPAND_ENV not in be.options: return False return opt not in _expand_vars(be.options) - if opt not in matches.keys(): if opt not in be._osp.options: if not opt_in_expansion(opt): + _log_debug_entry("Found add_opt: %s" % opt) return True return False @@ -760,12 +761,14 @@ class BootParams(object): opt_name = opt.split('=')[0] matched_opts = [k.split('=')[0] for k in matches.keys()] if opt_name not in matched_opts and opt_name not in ignore_bp: + _log_debug_entry("Found del_opt: %s" % opt) return True return False + options = be.expand_options.split() if expand else be.options.split() + # Compile list of unique non-template options - bp.add_opts = [opt for opt in be.options.split() if is_add(opt)] - bp.add_opts = list(set(bp.add_opts)) + bp.add_opts = [opt for opt in options if is_add(opt)] # Compile list of deleted template options bp.del_opts = [o for o in [r[1] for r in opts_regexes] if is_del(o)] @@ -970,6 +973,7 @@ def find_entries(selection=None): for be in _entries: if select_entry(selection, be): matches.append(be) + _log_debug_entry("Found %d entries" % len(matches)) return matches @@ -1032,6 +1036,7 @@ class BootEntry(object): _osp = None _bp = None _bp_generation = None + _suppress_machine_id = False # Read only state for foreign BLS entries read_only = False @@ -1075,6 +1080,8 @@ class BootEntry(object): be_str = prefix for key in [k for k in ENTRY_KEYS if getattr(self, KEY_MAP[k])]: + if key == BOOM_ENTRY_MACHINE_ID and self._suppress_machine_id: + continue attr = KEY_MAP[key] key_fmt = '%s%s"%s"' if quote else '%s%s%s' key_fmt += tail @@ -1163,22 +1170,8 @@ class BootEntry(object): if not isinstance(key, str): raise TypeError("BootEntry key must be a string.") - if key in self._entry_data: - return self._entry_data[key] - if key == BOOM_ENTRY_LINUX: - return self.linux - if key == BOOM_ENTRY_INITRD: - return self.initrd - if key == BOOM_ENTRY_OPTIONS: - return self.options - if key == BOOM_ENTRY_DEVICETREE: - return self.devicetree - if key == BOOM_ENTRY_EFI: - return self.efi - if key == BOOM_ENTRY_BOOT_ID: - return self.boot_id - if self.bp and key == BOOM_ENTRY_VERSION: - return self.bp.version + if key in KEY_MAP and hasattr(self, KEY_MAP[key]): + return getattr(self, KEY_MAP[key]) raise KeyError("BootEntry key %s not present." % key) @@ -1191,24 +1184,10 @@ class BootEntry(object): if not isinstance(key, str): raise TypeError("BootEntry key must be a string.") - if key == BOOM_ENTRY_VERSION and self.bp: - self.bp.version = value - elif key == BOOM_ENTRY_LINUX and self.bp: - self.linux = value - elif key == BOOM_ENTRY_INITRD and self.bp: - self.initrd = value - elif key == BOOM_ENTRY_OPTIONS and self.bp: - self.options = value - elif key == BOOM_ENTRY_DEVICETREE and self.bp: - self.devicetree = value - elif key == BOOM_ENTRY_EFI and self.bp: - self.efi = value - elif key == BOOM_ENTRY_BOOT_ID: - raise TypeError("'boot_id' property does not support assignment") - elif key in self._entry_data: - self._entry_data[key] = value - else: - raise KeyError("BootEntry key %s not present." % key) + if key in KEY_MAP and hasattr(self, KEY_MAP[key]): + return setattr(self, KEY_MAP[key], value) + + raise KeyError("BootEntry key %s not present." % key) def keys(self): """Return the list of keys for this ``BootEntry``. @@ -1415,19 +1394,39 @@ class BootEntry(object): self.machine_id = self.machine_id or "" self.architecture = self.architecture or "" + boot_id = self.boot_id if boot_params: self.bp = boot_params # boot_params is always authoritative self._entry_data[BOOM_ENTRY_VERSION] = self.bp.version else: + _log_debug_entry("Initialising BootParams() from " + "BootEntry(boot_id='%s')" % boot_id) # Attempt to recover BootParams from entry data - self.bp = BootParams.from_entry(self) + self._bp = BootParams.from_entry(self) + self._bp_generation = self._bp.generation + + if BOOM_ENTRY_OPTIONS in self._entry_data: + orig_options = self._entry_data[BOOM_ENTRY_OPTIONS] + option_words = self.options.split() + + # Remove add_opts options from BootEntry stored options + opts = [opt for opt in option_words if opt not in self.bp.add_opts] + self._entry_data[BOOM_ENTRY_OPTIONS] = " ".join(opts) + + # Test whether the re-generated options match the stored values. + if boot_id != self.__generate_boot_id(): + self._entry_data[BOOM_ENTRY_OPTIONS] = orig_options + self.read_only = True + _log_warn("Options for BootEntry(boot_id=%s) do not match " + "OsProfile: marking read-only" % + boot_id[:min_boot_id_width()]) if self.machine_id: # Wrap OsProfile in HostProfile if available self.__match_host_profile() - if self.bp: + if not self.read_only: def _pop_if_set(key): if key in _entry_data: if _entry_data[key] == getattr(self, KEY_MAP[key]): @@ -1470,6 +1469,26 @@ class BootEntry(object): :rtype: None :raises: ValueError """ + def machine_id_from_filename(filename): + """Try to obtain a machine-id value from a BLS entry file name. + + :param filename: The file name of the BLS snippet. + :param returns: The machine-id or the empty string if it + could not be read. + """ + machine_id_len = 32 + machine_id_chars = "0123456789abcdef" + try: + maybe_machine_id = filename.split("-")[0] + except ValueError: + return "" + if len(maybe_machine_id) != machine_id_len: + return "" + for c in maybe_machine_id: + if c not in machine_id_chars: + return "" + return maybe_machine_id + entry_data = {} comments = {} comment = "" @@ -1482,7 +1501,8 @@ class BootEntry(object): if blank_or_comment(line): comment += line if line else "" else: - bls_key, value = parse_name_value(line, separator=None) + bls_key, value = parse_name_value(line, separator=None, + allow_empty=True) # Convert BLS key name to Boom notation key = _transform_key(bls_key) if key not in MAP_KEY: @@ -1497,6 +1517,23 @@ class BootEntry(object): comment = "" self._comments = comments + # Red Hat native BLS entries do not set the machine-id BLS key: + # this does not matter when we are reading and displaying the + # entry, but it becomes important when cloning since we want to + # use the BOOM_ENTRY_MACHINE_ID value as the first component of + # the file name. Handle these entries by reading the machine-id + # value from the file name and setting the _suppress_machine_id + # attribute on the BootEntry. This prevents the machine-id from + # appearing in string representations of the BootEntry or being + # part of the boot_id calculation, but is not copied across a + # clone operation (meaning that the cloned entry has normal boom + # machine-id handling). + if BOOM_ENTRY_MACHINE_ID not in entry_data: + machine_id = machine_id_from_filename(entry_basename) + if machine_id: + entry_data[BOOM_ENTRY_MACHINE_ID] = machine_id + self._suppress_machine_id = True + self.__from_data(entry_data, boot_params) match = re.match(BOOT_ENTRIES_PATTERN, entry_basename) @@ -1806,7 +1843,6 @@ class BootEntry(object): # # Other callers should always rely on the standard methods. boot_id = sha1(self.__str(no_boot_id=True).encode('utf-8')).hexdigest() - _log_debug_entry("Generated new boot_id='%s'" % boot_id) return boot_id def _entry_data_property(self, name): @@ -1879,6 +1915,7 @@ class BootEntry(object): self._dirty() if not self.__boot_id or self._unwritten: self.__boot_id = self.__generate_boot_id() + _log_debug_entry("Generated new boot_id='%s'" % self.__boot_id) return self.__boot_id @property @@ -2042,7 +2079,7 @@ class BootEntry(object): if BOOM_ENTRY_OPTIONS in self._entry_data: opts = self._entry_data_property(BOOM_ENTRY_OPTIONS) - if self.bp: + if self.bp and not self.read_only: opts = add_opts(opts, self.bp.add_opts) return do_exp(del_opts(opts, self.bp.del_opts)) return do_exp(opts) diff --git a/boom/cache.py b/boom/cache.py index 302b669..f4a5391 100644 --- a/boom/cache.py +++ b/boom/cache.py @@ -624,9 +624,10 @@ class CacheEntry(object): dot_path = _RESTORED_DOT_PATTERN % basename(boot_path) boot_dir = dirname(boot_path) - if self.state not in (CACHE_MISSING, CACHE_RESTORED): + restore_states = (CACHE_MISSING, CACHE_RESTORED) + if self.state not in restore_states: raise ValueError("Restore failed: CacheEntry state is not " - "%s or %s" % (CACHE_MISSING, CACHE_RESTORED)) + "%s or %s" % restore_states) shutil.copy2(cache_path, boot_path) try: diff --git a/boom/command.py b/boom/command.py index dc9abae..be638df 100644 --- a/boom/command.py +++ b/boom/command.py @@ -482,24 +482,75 @@ def _do_print_type(report_fields, selected, output_fields=None, return br.report_output() -def _merge_add_del_opts(orig_opts, opts): +def _merge_add_del_opts(bp, add_opts, del_opts): """Merge a set of existing bootparams option alterations with a set of command-line provided values to produce a single set of options to add or remove from a cloned or edited ``BootEntry``. - :param orig_opts: A list of original option modifications - :param opts: A space-separated string containing a list of - command line option modifications - :returns: A single list containing the merged options - """ - # Merge new and cloned kernel options - all_opts = set() - if opts: - all_opts.update(opts.split()) - if orig_opts: - all_opts.update(orig_opts) - return list(all_opts) + The sets are merged giving precedence to alterations on the + current command line: i.e. if an option is present in both + ``bp.del_opts`` and ``add_opts`` (or vice versa) then the + option taken from the current command line will be effective. + + :param bp: A ``BootParams`` object with the original ``add_opts`` + and ``del_opts`` values. + :param add_opts: A space-separated string containing a list of + additional options taken from the current + command line. + :param del_opts: A space-separated string containing a list of + options to delete taken from the current + command line. + :returns: A tuple ``(effective_add_opts, effective_del_opts)`` + giving the final effective values as a list of + strings, one per option word. + """ + def _merge_opts(orig_opts, opts, r_opts): + # Merge new and cloned kernel options + all_opts = [] + if orig_opts: + for opt in orig_opts: + if opt not in all_opts: + all_opts.append(opt) + if opts: + for opt in opts: + if opt not in all_opts: + all_opts.append(opt) + return [o for o in all_opts if o not in r_opts] + + _log_debug_cmd("Add opts: %s" % add_opts) + _log_debug_cmd("Del opts: %s" % del_opts) + _log_debug_cmd("Original add_opts: %s" % bp.add_opts) + _log_debug_cmd("Original del_opts: %s" % bp.del_opts) + + r_del_opts = [] + r_add_opts = [] + + add_opts = add_opts.split() if add_opts else [] + del_opts = del_opts.split() if del_opts else [] + + for add_opt in list(add_opts): + # Do not allow conflicting command line add/del opts + if add_opt in del_opts: + raise ValueError("Conflicting --add-opts %s and --del-opts %s" % + (add_opt, add_opt)) + + if add_opt in bp.del_opts: + r_del_opts.append(add_opt) + add_opts.remove(add_opt) + + for del_opt in list(del_opts): + if del_opt in bp.add_opts: + r_add_opts.append(del_opt) + del_opts.remove(del_opt) + + add_opts = _merge_opts(bp.add_opts, add_opts, r_add_opts) + del_opts = _merge_opts(bp.del_opts, del_opts, r_del_opts) + + _log_debug_cmd("Effective add options: %s" % add_opts) + _log_debug_cmd("Effective del options: %s" % del_opts) + + return (add_opts, del_opts) # @@ -751,45 +802,45 @@ def clone_entry(selection=None, title=None, version=None, machine_id=None, title = title if title else be.title version = version if version else be.version machine_id = machine_id if machine_id else be.machine_id - root_device = root_device if root_device else be.bp.root_device - lvm_root_lv = lvm_root_lv if lvm_root_lv else be.bp.lvm_root_lv - btrfs_subvol_path = (btrfs_subvol_path if btrfs_subvol_path - else be.bp.btrfs_subvol_path) - btrfs_subvol_id = (btrfs_subvol_id if btrfs_subvol_id - else be.bp.btrfs_subvol_id) profile = profile if profile else be._osp - add_opts = _merge_add_del_opts(be.bp.add_opts, add_opts) - del_opts = _merge_add_del_opts(be.bp.del_opts, del_opts) - _log_debug_cmd("Effective add options: %s" % add_opts) - _log_debug_cmd("Effective del options: %s" % del_opts) + bp = BootParams.from_entry(be, expand=expand) + (add_opts, del_opts) = _merge_add_del_opts(bp, add_opts, del_opts) - bp = BootParams(version, root_device, lvm_root_lv=lvm_root_lv, - btrfs_subvol_path=btrfs_subvol_path, - btrfs_subvol_id=btrfs_subvol_id, - add_opts=add_opts, del_opts=del_opts) + bp.root_device = root_device if root_device else bp.root_device + bp.lvm_root_lv = lvm_root_lv if lvm_root_lv else bp.lvm_root_lv + + if btrfs_subvol_path and btrfs_subvol_id: + raise ValueError("cannot set btrfs_subvol_path and btrfs_subvol_id") + + if btrfs_subvol_path: + bp.btrfs_subvol_path = btrfs_subvol_path + elif btrfs_subvol_id: + bp.btrfs_subvol_id = btrfs_subvol_id clone_be = BootEntry(title=title, machine_id=machine_id, osprofile=profile, boot_params=bp, architecture=architecture, allow_no_dev=allow_no_dev) - orig_be = find_entries(selection)[0] - if orig_be.options != orig_be.expand_options: - clone_be.options = orig_be.options + if be.options != be.expand_options and not expand: + clone_be.options = be.options + else: + clone_be.bp.add_opts = add_opts + clone_be.bp.del_opts = del_opts # Clone optional keys allowed by profile - for optional_key in orig_be._osp.optional_keys.split(): + for optional_key in be._osp.optional_keys.split(): if optional_key in clone_be._osp.optional_keys: - if hasattr(orig_be, optional_key): + if hasattr(be, optional_key): setattr(clone_be, optional_key, - getattr(orig_be, optional_key)) + getattr(be, optional_key)) # Boot image overrides? - if orig_be.initrd != clone_be.initrd: - clone_be.initrd = orig_be.initrd - if orig_be.linux != clone_be.linux: - clone_be.linux = orig_be.linux + if be.initrd != clone_be.initrd: + clone_be.initrd = be.initrd + if be.linux != clone_be.linux: + clone_be.linux = be.linux if images in (I_BACKUP, I_CACHE): clone_be.initrd = _cache_image(clone_be.initrd, images == I_BACKUP) @@ -800,7 +851,7 @@ def clone_entry(selection=None, title=None, version=None, machine_id=None, clone_be.disp_boot_id) if write: - clone_be.write_entry(expand=expand) + clone_be.write_entry() __write_legacy() return clone_be @@ -864,10 +915,7 @@ def edit_entry(selection=None, title=None, version=None, machine_id=None, machine_id = machine_id or be.machine_id version = version or be.version - add_opts = _merge_add_del_opts(be.bp.add_opts, add_opts) - del_opts = _merge_add_del_opts(be.bp.del_opts, del_opts) - _log_debug_cmd("Effective add options: %s" % add_opts) - _log_debug_cmd("Effective del options: %s" % del_opts) + (add_opts, del_opts) = _merge_add_del_opts(be.bp, add_opts, del_opts) be._osp = profile or be._osp be.title = title or be.title @@ -885,6 +933,11 @@ def edit_entry(selection=None, title=None, version=None, machine_id=None, be.initrd = _cache_image(be.initrd, images == I_BACKUP) be.linux = _cache_image(be.linux, images == I_BACKUP) + # Is the entry now identical to an existing entry? + if len(find_entries(Selection(boot_id=be.boot_id))) > 1: + raise ValueError("Entry already exists (boot_id=%s)." % + be.disp_boot_id) + be.update_entry(expand=expand) __write_legacy() @@ -2109,6 +2162,12 @@ def _show_cmd(cmd_args, select, opts, identifier): if identifier is not None: select = Selection(boot_id=identifier) + if identifier or cmd_args.all: + if select: + select.allow_null_profile = True + else: + select = Selection(allow_null=True) + try: bes = find_entries(selection=select) except ValueError as e: @@ -2171,6 +2230,12 @@ def _list_cmd(cmd_args, select, opts, identifier): if identifier is not None: select = Selection(boot_id=identifier) + if cmd_args.all: + if select: + select.allow_null_profile = True + else: + select = Selection(allow_null=True) + return _generic_list_cmd(cmd_args, select, opts, _verbose_entry_fields, print_entries) @@ -2214,6 +2279,9 @@ def _edit_cmd(cmd_args, select, opts, identifier): profile = _find_profile(cmd_args, version, machine_id, "edit") + add_opts = cmd_args.add_opts + del_opts = cmd_args.del_opts + arch = cmd_args.architecture try: @@ -2222,6 +2290,7 @@ def _edit_cmd(cmd_args, select, opts, identifier): lvm_root_lv=lvm_root_lv, btrfs_subvol_path=btrfs_subvol_path, btrfs_subvol_id=btrfs_subvol_id, profile=profile, + add_opts=add_opts, del_opts=del_opts, architecture=arch, expand=cmd_args.expand_variables) except ValueError as e: print(e) @@ -3033,6 +3102,8 @@ def main(args): "operate on", nargs="?", default=None) parser.add_argument("-a", "--add-opts", "--addopts", metavar="OPTIONS", help="Additional kernel options to append", type=str) + parser.add_argument("--all", action="store_true", + help="Include entries with no valid profile") parser.add_argument("--architecture", metavar="ARCH", default=None, help="An optional BLS architecture string", type=str) parser.add_argument("--backup", action="store_true", diff --git a/boom/legacy.py b/boom/legacy.py index 7e67f2a..14abde3 100644 --- a/boom/legacy.py +++ b/boom/legacy.py @@ -172,7 +172,13 @@ def write_legacy_loader(selection=None, loader=BOOM_LOADER_GRUB1, tmp_f.write(line) tmp_f.write(begin_tag + "\n") bes = find_entries(selection=selection) - for be in bes: + # Entries are naturally in the order returned by the file system: + # this may lead to confusing re-ordering of entries in the legacy + # boot loader configuration file, as boom entries are modified or + # re-written (causing a change to the entry's inode number). + # + # Prevent this by sorting entries lexically by version. + for be in sorted(bes, key=lambda b: b.version): dbe = decorator(be) tmp_f.write(str(dbe) + "\n") tmp_f.write(end_tag + "\n") diff --git a/boom/osprofile.py b/boom/osprofile.py index 1893676..52dea07 100644 --- a/boom/osprofile.py +++ b/boom/osprofile.py @@ -224,8 +224,10 @@ def drop_profiles(): _profiles = [] _profiles_by_id = {} + optional_keys = "id grub_users grub_arg grub_class" _null_profile = OsProfile(name="", short_name="", - version="", version_id="") + version="", version_id="", + optional_keys=optional_keys) _profiles_by_id[_null_profile.os_id] = _null_profile if nr_profiles: _log_info("Dropped %d profiles" % nr_profiles) @@ -298,7 +300,7 @@ def select_profile(s, osp): :returns: True if ``osp`` passes selection or ``False`` otherwise. """ - if _is_null_profile(osp): + if not s.allow_null_profile and _is_null_profile(osp): return False if s.os_id and not osp.os_id.startswith(s.os_id): return False @@ -1506,6 +1508,9 @@ class OsProfile(BoomProfile): if all([not val for val in required_args]): # NULL profile for key in OS_PROFILE_KEYS: + # Allow optional_keys for the NULL profile + if key == BOOM_OS_OPTIONAL_KEYS: + continue self._profile_data[key] = "" elif any([not val for val in required_args]): raise ValueError("Invalid profile arguments: name, " diff --git a/doc/conf.py b/doc/conf.py index cccdcfa..6221791 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -64,9 +64,9 @@ author = u'Bryn M. Reeves' # built documents. # # The short X.Y version. -version = u'1.1' +version = u'1.3' # The full version, including alpha/beta/rc tags. -release = u'1.1' +release = u'1.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/tests/__init__.py b/tests/__init__.py index 2f9114b..d45da1f 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -71,6 +71,7 @@ class MockArgs(object): """Mock arguments class for testing boom command line infrastructure. """ add_opts = "" + all = False architecture = None backup = False boot_id = None diff --git a/tests/boom/profiles/db5e6d25c066844eaeeaa931d30176e644e8fd15-fedora32.profile b/tests/boom/profiles/db5e6d25c066844eaeeaa931d30176e644e8fd15-fedora32.profile new file mode 100644 index 0000000..ec2d5cb --- /dev/null +++ b/tests/boom/profiles/db5e6d25c066844eaeeaa931d30176e644e8fd15-fedora32.profile @@ -0,0 +1,13 @@ +BOOM_OS_ID="db5e6d25c066844eaeeaa931d30176e644e8fd15" +BOOM_OS_NAME="Fedora" +BOOM_OS_SHORT_NAME="fedora" +BOOM_OS_VERSION="32 (Workstation Edition)" +BOOM_OS_VERSION_ID="32" +BOOM_OS_KERNEL_PATTERN="/vmlinuz-%{version}" +BOOM_OS_INITRAMFS_PATTERN="/initramfs-%{version}.img" +BOOM_OS_ROOT_OPTS_LVM2="rd.lvm.lv=%{lvm_root_lv}" +BOOM_OS_ROOT_OPTS_BTRFS="rootflags=%{btrfs_subvolume}" +BOOM_OS_OPTIONS="root=%{root_device} ro %{root_opts} rhgb quiet" +BOOM_OS_TITLE="%{os_name} %{os_version_id} (%{version})" +BOOM_OS_OPTIONAL_KEYS="grub_users grub_arg grub_class id" +BOOM_OS_UNAME_PATTERN="fc32" diff --git a/tests/cache_tests.py b/tests/cache_tests.py index 6b15eb2..b0377f1 100644 --- a/tests/cache_tests.py +++ b/tests/cache_tests.py @@ -59,6 +59,21 @@ class CacheHelperTests(unittest.TestCase): not modify on-disk state and do not use a unique test fixture. """ + + # Test fixture init/cleanup + def setUp(self): + """Set up a test fixture for the CacheHelperTests class. + """ + set_boom_config(config) + set_boot_path(BOOT_ROOT_TEST) + + def tearDown(self): + # Drop any in-memory entries and profiles modified by tests + drop_entries() + drop_profiles() + drop_host_profiles() + drop_cache() + def test__make_relative_with_non_abs_path(self): path = "not/an/absolute/path" self.assertEqual(path, boom.cache._make_relative(path)) diff --git a/tests/command_tests.py b/tests/command_tests.py index 6d0c79b..00cda3b 100644 --- a/tests/command_tests.py +++ b/tests/command_tests.py @@ -192,6 +192,58 @@ class CommandHelperTests(unittest.TestCase): machine_id = boom.command._get_machine_id() self.assertTrue(machine_id) + def test__merge_add_del_opts_no_op(self): + bp = BootParams(version="1.1.1", root_device="/dev/vg00/lvol0") + _merge_add_del_opts = boom.command._merge_add_del_opts + + # Empty bp and empty add/del lead to empty results + to_add = "" + to_del = "" + (add_opts, del_opts) = _merge_add_del_opts(bp, to_add, to_del) + self.assertEqual([], add_opts) + self.assertEqual([], del_opts) + + # Existing add/del with no modifications + bp.add_opts = ["debug"] + bp.del_opts = ["rhgb quiet"] + (add_opts, del_opts) = _merge_add_del_opts(bp, to_add, to_del) + self.assertEqual(bp.add_opts, add_opts) + self.assertEqual(bp.del_opts, del_opts) + + def test__merge_add_del_opts_with_add(self): + bp = BootParams(version="1.1.1", root_device="/dev/vg00/lvol0") + _merge_add_del_opts = boom.command._merge_add_del_opts + + # Empty bp and add places add in final add_opts + to_add = "debug" + to_del = "" + (add_opts, del_opts) = _merge_add_del_opts(bp, to_add, to_del) + self.assertEqual([to_add], add_opts) + self.assertEqual([], del_opts) + + def test__merge_add_del_opts_with_del(self): + bp = BootParams(version="1.1.1", root_device="/dev/vg00/lvol0") + _merge_add_del_opts = boom.command._merge_add_del_opts + + # Empty bp and del places del in final del_opts + to_add = "" + to_del = "rhgb quiet" + (add_opts, del_opts) = _merge_add_del_opts(bp, to_add, to_del) + self.assertEqual([], add_opts) + self.assertEqual(to_del.split(), del_opts) + + def test__merge_add_del_opts_with_add_appends(self): + bp = BootParams(version="1.1.1", root_device="/dev/vg00/lvol0", + add_opts=["log_buf_len=16M"]) + _merge_add_del_opts = boom.command._merge_add_del_opts + + # bp with add_opts and and command line add gives correct order + # of final add_opts list (command line additions appended). + to_add = "debug" + to_del = "" + (add_opts, del_opts) = _merge_add_del_opts(bp, to_add, to_del) + self.assertEqual(["log_buf_len=16M", "debug"], add_opts) + # Default test OsProfile identifiers test_os_id = "9cb53ddda889d6285fd9ab985a4c47025884999f" @@ -520,6 +572,44 @@ class CommandTests(unittest.TestCase): self.assertFalse(exists(be._entry_path)) @unittest.skipIf(not have_root_lv(), "requires root LV") + def test_clone_entry_del_opts_and_re_add(self): + # Fedora 24 (Workstation Edition) + + # Delete rhgb quiet + osp = get_os_profile_by_id(test_os_id) + be = create_entry("delopts", "2.6.0", "ffffffff", test_lv, + lvm_root_lv=test_root_lv, profile=osp, + del_opts="rhgb quiet") + + # Assert it's gone + self.assertFalse("rhgb quiet" in be.options) + + be2 = clone_entry(Selection(boot_id=be.boot_id), title="addoptsclone", + add_opts="rhgb quiet") + + # Assert it's back + self.assertTrue("rhgb quiet" in be2.options) + + @unittest.skipIf(not have_root_lv(), "requires root LV") + def test_clone_entry_add_opts_and_re_del(self): + # Fedora 24 (Workstation Edition) + + # Add debug + osp = get_os_profile_by_id(test_os_id) + be = create_entry("addopts", "2.6.0", "ffffffff", test_lv, + lvm_root_lv=test_root_lv, profile=osp, + add_opts="debug") + + # Assert it's there + self.assertTrue("debug" in be.options) + + be2 = clone_entry(Selection(boot_id=be.boot_id), title="deloptsclone", + del_opts="debug") + + # Assert it's gone + self.assertFalse("debug" in be2.options) + + @unittest.skipIf(not have_root_lv(), "requires root LV") def test_clone_delete_entry(self): # Fedora 24 (Workstation Edition) osp = get_os_profile_by_id(test_os_id) @@ -1320,7 +1410,7 @@ class CommandTests(unittest.TestCase): args = get_create_cmd_args() args.machine_id = None args.profile = None - args.version = None + args.version = "5.8.16-200.fc32.x86_64" opts = boom.command._report_opts_from_args(args) r = boom.command._create_cmd(args, None, opts, None) self.assertNotEqual(r, 1) diff --git a/tests/grub/grub.conf b/tests/grub/grub.conf index 79eb5dd..ec4104e 100644 --- a/tests/grub/grub.conf +++ b/tests/grub/grub.conf @@ -24,140 +24,140 @@ title Red Hat Enterprise Linux Server (2.6.32-504.16.2.el6.x86_64) kernel /vmlinuz-2.6.32-504.16.2.el6.x86_64 ro root=/dev/vg_mother/lv_root rd_NO_LUKS KEYBOARDTYPE=pc KEYTABLE=uk LANG=en_US.UTF-8 rd_NO_MD quiet SYSFONT=latarcyrheb-sun16 rhgb crashkernel=auto rd_LVM_LV=vg_mother/lv_root rd_LVM_LV=vg_mother/lv_swap0 rd_NO_DM initrd /initramfs-2.6.32-504.16.2.el6.x86_64.img #--- BOOM_Grub1_BEGIN --- -title ATITLE +title title root (hd0,0) - kernel /vmlinuz-3.3.10 root=/dev/vg00/lvol0-snap9 ro rd.lvm.lv=vg00/lvol0-snap9 rootflags=subvolid=23 rhgb quiet - initrd /initramfs-3.3.10.img -title ANEWTITLE + kernel /vmlinuz-1.1.1-1.fc24.x86_64 root=/dev/vg_root/root ro rd.lvm.lv=vg_root/root rhgb quiet + initrd /initramfs-1.1.1-1.fc24.x86_64.img +title title root (hd0,0) - kernel /vmlinuz-3.3.30 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 - initrd /initrd.img-3.3.30 + kernel vmlinuz-1.1.1-1.fc24.x86_64 root=/dev/vg_root/root ro rd.lvm.lv=vg_root/root rhgb quiet + initrd initramfs-1.1.1-1.fc24.x86_64.img +title title + root (hd0,0) + kernel /vmlinuz-2.2.2-2.fc24.x86_64 root=/dev/vg_root/root ro rootflags=subvol=/snapshot/today rhgb quiet + initrd /initramfs-2.2.2-2.fc24.x86_64.img +title title + root (hd0,0) + kernel vmlinuz-2.2.2-2.fc24.x86_64 root=/dev/vg_root/root ro rootflags=subvol=/snapshot/today rhgb quiet + initrd initramfs-2.2.2-2.fc24.x86_64.img title add_del_opts root (hd0,0) kernel /vmlinuz-3.10-1.el7.fc24.x86_64 root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root debug initrd /initramfs-3.10-1.el7.fc24.x86_64.img +title clone with addopts + root (hd0,0) + kernel /vmlinuz-3.10-1.el7.fc24.x86_64 root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root rhgb quiet debug + initrd /initramfs-3.10-1.el7.fc24.x86_64.img +title Red Hat Enterprise Linux Server (3.10-1.el7.fc24.x86_64) 7.2 (Maipo) + root (hd0,0) + kernel /vmlinuz-3.10-1.el7.fc24.x86_64 root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root rhgb quiet + initrd /initramfs-3.10-1.el7.fc24.x86_64.img title ANOTHERTITLE2 root (hd0,0) kernel /vmlinuz-3.10-23.el7 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 rhgb quiet initrd /initramfs-3.10-23.el7.img -title ANEWTITLE - root (hd0,0) - kernel /vmlinuz-3.3.40 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 - initrd /initrd.img-3.3.40 -title ANOTHERTITLE - root (hd0,0) - kernel /vmlinuz-3.3.60-12.fc24.x86_64 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 rhgb quiet - initrd /initramfs-3.3.60-12.fc24.x86_64.img title Red Hat Enterprise Linux 7.2 (Maipo) 3.10-23.el7 root (hd0,0) kernel /vmlinuz-3.10-23.el7 root=/dev/sda5 ro rhgb quiet initrd /initramfs-3.10-23.el7.img -title Some other snapshot - root (hd0,0) - kernel /vmlinuz-4.11.12-100.fc24.x86_64 root=/dev/vg00/lvol0-snapshot2 ro rd.lvm.lv=vg00/lvol0-snapshot2 rhgb quiet - initrd /initramfs-4.11.12-100.fc24.x86_64.img -title qux - root (hd0,0) - kernel /vmlinuz-3.3.4 root=/dev/vg00/lvol0-snap2 ro rd.lvm.lv=vg00/lvol0-snap2 rhgb quiet - initrd /initramfs-3.3.4.img -title Some snapshot - root (hd0,0) - kernel /vmlinuz-4.11.12-100.fc24.x86_64 root=/dev/vg00/lvol0-snapshot ro rd.lvm.lv=vg00/lvol0-snapshot rhgb quiet - initrd /initramfs-4.11.12-100.fc24.x86_64.img -title ATITLE - root (hd0,0) - kernel /vmlinuz-3.3.4 root=/dev/vg00/lvol0-snap2 ro rd.lvm.lv=vg00/lvol0-snap2 rhgb quiet - initrd /initramfs-3.3.4.img -title title +title RHEL7 snapshot root (hd0,0) - kernel /vmlinuz-1.1.1-1.fc24.x86_64 root=/dev/vg_root/root ro rd.lvm.lv=vg_root/root rhgb quiet - initrd /initramfs-1.1.1-1.fc24.x86_64.img + kernel /vmlinuz-3.10-272.el7 root=/dev/vg00/lvol0-snap ro rd.lvm.lv=vg00/lvol0-snap rhgb quiet + initrd /initramfs-3.10-272.el7.img title ANEWTITLE root (hd0,0) kernel /vmlinuz-3.10.1-1.el7 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 rhgb quiet initrd /initramfs-3.10.1-1.el7.img -title A NEWER TITLE - root (hd0,0) - kernel /vmlinuz-7.7.7 root=/dev/vg_qux/lv_qux ro rd.lvm.lv=vg_qux/lv_qux rhgb quiet - initrd /initramfs-7.7.7.img -title Fedora (4.1.1-100.fc24.x86_64) 24 (Workstation Edition) - root (hd0,0) - kernel /vmlinuz-4.1.1-100.fc24 root=/dev/sda5 ro rootflags=subvolid=23 rhgb quiet - initrd /initramfs-4.1.1-100.fc24.img -title ANEWTITLE - root (hd0,0) - kernel /vmlinuz-3.3.50 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 - initrd /initrd.img-3.3.50 -title title - root (hd0,0) - kernel /vmlinuz-2.2.2-2.fc24.x86_64 root=/dev/vg_root/root ro rootflags=subvol=/snapshot/today rhgb quiet - initrd /initramfs-2.2.2-2.fc24.x86_64.img -title Clone test1 +title ANOTHERTITLE3 root (hd0,0) - kernel /vmlinuz-4.16.11-100.fc26.x86_64 BOOT_IMAGE=/vmlinuz-4.16.11-100.fc26.x86_64 root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root rhgb quiet debug - initrd /initramfs-4.16.11-100.fc26.x86_64.img -title ANEWTITLE + kernel /vmlinuz-3.3.10 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 + initrd /initrd.img-3.3.10 +title ATITLE root (hd0,0) - kernel /vmlinuz-3.3.30 root=/dev/vg00/lvol0-snap ro rd.lvm.lv=vg00/lvol0-snap - initrd /initrd.img-3.3.30 -title RHEL7 snapshot + kernel /vmlinuz-3.3.10 root=/dev/vg00/lvol0-snap2 ro rd.lvm.lv=vg00/lvol0-snap2 rootflags=subvolid=23 rhgb quiet + initrd /initramfs-3.3.10.img +title ATITLE root (hd0,0) - kernel /vmlinuz-3.10-272.el7 root=/dev/vg00/lvol0-snap ro rd.lvm.lv=vg00/lvol0-snap rhgb quiet - initrd /initramfs-3.10-272.el7.img + kernel /vmlinuz-3.3.10 root=/dev/vg00/lvol0-snap9 ro rd.lvm.lv=vg00/lvol0-snap9 rootflags=subvolid=23 rhgb quiet + initrd /initramfs-3.3.10.img title ATITLE root (hd0,0) - kernel /vmlinuz-3.3.5 root=/dev/vg00/lvol0-snap ro rd.lvm.lv=vg00/lvol0-snap rhgb quiet - initrd /initramfs-3.3.5.img + kernel /vmlinuz-3.3.10 root=/dev/vg00/lvol0-snap2 ro rootflags=subvolid=23 rhgb quiet + initrd /initramfs-3.3.10.img title ANEWERTITLE3 root (hd0,0) kernel /vmlinuz-3.3.30 root=/dev/vg00/lvol0-snap2 ro rd.lvm.lv=vg00/lvol0-snap2 initrd /initrd.img-3.3.30 -title A NEW TEST TITLE - root (hd0,0) - kernel /vmlinuz-4.14.14-200.fc26.x86_64 root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root qux debug - initrd /initramfs-4.14.14-200.fc26.x86_64.img -title ANOTHERTITLE3 - root (hd0,0) - kernel /vmlinuz-3.3.10 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 - initrd /initrd.img-3.3.10 -title ATITLE - root (hd0,0) - kernel /vmlinuz-3.3.10 root=/dev/vg00/lvol0-snap2 ro rootflags=subvolid=23 rhgb quiet - initrd /initramfs-3.3.10.img title ANEWERTITLE2 root (hd0,0) kernel /vmlinuz-3.3.30 root=/dev/vg00/lvol0-snap ro rd.lvm.lv=vg00/lvol0-snap initrd /initrd.img-3.3.30 -title title +title ANEWTITLE root (hd0,0) - kernel vmlinuz-2.2.2-2.fc24.x86_64 root=/dev/vg_root/root ro rootflags=subvol=/snapshot/today rhgb quiet - initrd initramfs-2.2.2-2.fc24.x86_64.img -title grub args + kernel /vmlinuz-3.3.30 root=/dev/vg00/lvol0-snap ro rd.lvm.lv=vg00/lvol0-snap + initrd /initrd.img-3.3.30 +title ANEWTITLE root (hd0,0) - kernel /vmlinuz-5.4.7-100.fc30.x86_64 root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root - initrd /initramfs-5.4.7-100.fc30.x86_64.img -title title + kernel /vmlinuz-3.3.30 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 + initrd /initrd.img-3.3.30 +title qux root (hd0,0) - kernel vmlinuz-1.1.1-1.fc24.x86_64 root=/dev/vg_root/root ro rd.lvm.lv=vg_root/root rhgb quiet - initrd initramfs-1.1.1-1.fc24.x86_64.img -title Red Hat Enterprise Linux Server (3.10-1.el7.fc24.x86_64) 7.2 (Maipo) + kernel /vmlinuz-3.3.4 root=/dev/vg00/lvol0-snap2 ro rd.lvm.lv=vg00/lvol0-snap2 rhgb quiet + initrd /initramfs-3.3.4.img +title ATITLE root (hd0,0) - kernel /vmlinuz-3.10-1.el7.fc24.x86_64 root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root rhgb quiet - initrd /initramfs-3.10-1.el7.fc24.x86_64.img + kernel /vmlinuz-3.3.4 root=/dev/vg00/lvol0-snap2 ro rd.lvm.lv=vg00/lvol0-snap2 rhgb quiet + initrd /initramfs-3.3.4.img +title ANEWTITLE + root (hd0,0) + kernel /vmlinuz-3.3.40 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 + initrd /initrd.img-3.3.40 title ATITLE root (hd0,0) - kernel /vmlinuz-3.3.10 root=/dev/vg00/lvol0-snap2 ro rd.lvm.lv=vg00/lvol0-snap2 rootflags=subvolid=23 rhgb quiet - initrd /initramfs-3.3.10.img + kernel /vmlinuz-3.3.5 root=/dev/vg00/lvol0-snap ro rd.lvm.lv=vg00/lvol0-snap rhgb quiet + initrd /initramfs-3.3.5.img +title ANEWTITLE + root (hd0,0) + kernel /vmlinuz-3.3.50 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 + initrd /initrd.img-3.3.50 title ANOTHERTITLE root (hd0,0) kernel /vmlinuz-3.3.60 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 initrd /initrd.img-3.3.60 -title clone with addopts +title ANOTHERTITLE root (hd0,0) - kernel /vmlinuz-3.10-1.el7.fc24.x86_64 root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root rhgb quiet debug - initrd /initramfs-3.10-1.el7.fc24.x86_64.img + kernel /vmlinuz-3.3.60-12.fc24.x86_64 root=/dev/vg00/lvol0 ro rd.lvm.lv=vg00/lvol0 rhgb quiet + initrd /initramfs-3.3.60-12.fc24.x86_64.img title ATITLE root (hd0,0) kernel /vmlinuz-3.3.9 root=/dev/vg00/lvol0-snap ro rd.lvm.lv=vg00/lvol0-snap rhgb quiet initrd /initramfs-3.3.9.img +title Fedora (4.1.1-100.fc24.x86_64) 24 (Workstation Edition) + root (hd0,0) + kernel /vmlinuz-4.1.1-100.fc24 root=/dev/sda5 ro rootflags=subvolid=23 rhgb quiet + initrd /initramfs-4.1.1-100.fc24.img +title Some other snapshot + root (hd0,0) + kernel /vmlinuz-4.11.12-100.fc24.x86_64 root=/dev/vg00/lvol0-snapshot2 ro rd.lvm.lv=vg00/lvol0-snapshot2 rhgb quiet + initrd /initramfs-4.11.12-100.fc24.x86_64.img +title Some snapshot + root (hd0,0) + kernel /vmlinuz-4.11.12-100.fc24.x86_64 root=/dev/vg00/lvol0-snapshot ro rd.lvm.lv=vg00/lvol0-snapshot rhgb quiet + initrd /initramfs-4.11.12-100.fc24.x86_64.img +title A NEW TEST TITLE + root (hd0,0) + kernel /vmlinuz-4.14.14-200.fc26.x86_64 root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root debug + initrd /initramfs-4.14.14-200.fc26.x86_64.img +title Clone test1 + root (hd0,0) + kernel /vmlinuz-4.16.11-100.fc26.x86_64 BOOT_IMAGE=/vmlinuz-4.16.11-100.fc26.x86_64 root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root rhgb quiet debug + initrd /initramfs-4.16.11-100.fc26.x86_64.img +title grub args + root (hd0,0) + kernel /vmlinuz-5.4.7-100.fc30.x86_64 root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root + initrd /initramfs-5.4.7-100.fc30.x86_64.img +title A NEWER TITLE + root (hd0,0) + kernel /vmlinuz-7.7.7 root=/dev/vg_qux/lv_qux ro rd.lvm.lv=vg_qux/lv_qux rhgb quiet + initrd /initramfs-7.7.7.img #--- BOOM_Grub1_END --- diff --git a/tests/hostprofile_tests.py b/tests/hostprofile_tests.py index 596a804..ca9d909 100644 --- a/tests/hostprofile_tests.py +++ b/tests/hostprofile_tests.py @@ -561,7 +561,7 @@ class HostProfileTests(unittest.TestCase): self.assertFalse(hp) def test_match_host_profile(self): - bes = find_entries(Selection(boot_id="dc5f44d")) + bes = find_entries(Selection(boot_id="242d946")) self.assertTrue(bes) be = bes[0] self.assertTrue(be) diff --git a/tests/loader/entries/ffffffffffffc-242d946-4.14.14-200.fc26.x86_64.conf b/tests/loader/entries/ffffffffffffc-242d946-4.14.14-200.fc26.x86_64.conf new file mode 100644 index 0000000..5f4fe2d --- /dev/null +++ b/tests/loader/entries/ffffffffffffc-242d946-4.14.14-200.fc26.x86_64.conf @@ -0,0 +1,7 @@ +#OsIdentifier: d4439b7 +title A NEW TEST TITLE +machine-id ffffffffffffc +version 4.14.14-200.fc26.x86_64 +linux /vmlinuz-4.14.14-200.fc26.x86_64 +initrd /initramfs-4.14.14-200.fc26.x86_64.img +options root=/dev/vg_hex/root ro rd.lvm.lv=vg_hex/root qux debug