This will avoid duplicities in the resulting kernel boot argument line. The patch is merged from the rhel6-branch and since bootloader.py has been overhauled the changes there are different. --- pyanaconda/bootloader.py | 130 +++++++++--------------------- pyanaconda/installclass.py | 2 +- pyanaconda/iw/zipl_gui.py | 2 +- pyanaconda/kickstart.py | 2 +- pyanaconda/language.py | 6 +- pyanaconda/network.py | 34 +++----- pyanaconda/packages.py | 2 +- pyanaconda/storage/devices.py | 45 +++++------ pyanaconda/textw/zipl_text.py | 2 +- tests/pyanaconda_test/bootloader_test.py | 26 ++++++ tests/pyanaconda_test/language_test.py | 12 ++-- 11 files changed, 112 insertions(+), 151 deletions(-) create mode 100644 tests/pyanaconda_test/bootloader_test.py diff --git a/pyanaconda/bootloader.py b/pyanaconda/bootloader.py index 7fc5f61..f054ba9 100644 --- a/pyanaconda/bootloader.py +++ b/pyanaconda/bootloader.py @@ -78,69 +78,9 @@ class BootLoaderError(Exception): pass -class ArgumentList(list): - def _is_match(self, item, value): - try: - _item = item.split("=")[0] - except (ValueError, AttributeError): - _item = item - - try: - _value = value.split("=")[0] - except (ValueError, AttributeError): - _value = value - - return _item == _value - - def __contains__(self, value): - for arg in self: - if self._is_match(arg, value): - return True - - return False - - def count(self, value): - return len([x for x in self if self._is_match(x, value)]) - - def index(self, value): - for i in range(len(self)): - if self._is_match(self[i], value): - return i - - raise ValueError("'%s' is not in list" % value) - - def rindex(self, value): - for i in reversed(range(len(self))): - if self._is_match(self[i], value): - return i - - raise ValueError("'%s' is not in list" % value) - - def append(self, value): - """ Append a new argument to the list. - - If the value is an exact match to an existing argument it will - not be added again. Also, empty strings will not be added. - """ - if value == "": - return - - try: - idx = self.index(value) - except ValueError: - pass - else: - if self[idx] == value: - return - - super(ArgumentList, self).append(value) - - def extend(self, values): - map(self.append, values) - +class Arguments(set): def __str__(self): - return " ".join(map(str, self)) - + return " ".join(self) class BootLoaderImage(object): """ Base class for bootloader images. Suitable for non-linux OS images. """ @@ -184,8 +124,6 @@ class BootLoader(object): - improve password encryption for grub - fix handling of kickstart-provided already-encrypted password - - consider re-implementing ArgumentList as something else, like - perhaps collections.OrderedDict """ name = "Generic Bootloader" packages = [] @@ -236,8 +174,8 @@ class BootLoader(object): # pyanaconda.storage.Storage instance self.storage = storage - self.boot_args = ArgumentList() - self.dracut_args = [] + self.boot_args = Arguments() + self.dracut_args = Arguments() self._drives = [] self._drive_order = [] @@ -815,7 +753,7 @@ class BootLoader(object): # FIPS # if flags.cmdline.get("fips") == "1": - self.boot_args.append("boot=%s" % self.stage2_device.fstabSpec) + self.boot_args.add("boot=%s" % self.stage2_device.fstabSpec) # # dracut @@ -845,14 +783,15 @@ class BootLoader(object): if device != dep and not device.dependsOn(dep): continue - setup_string = dep.dracutSetupString().strip() - if not setup_string: + setup_args = dep.dracutSetupArgs() + if not setup_args: continue - self.boot_args.append(setup_string) - self.dracut_args.append(setup_string) + self.boot_args.update(setup_args) + self.dracut_args.update(setup_args) done.append(dep) - dracut_storage.pop(setup_string.split("=")[0], None) + for setup_arg in setup_args: + dracut_storage.pop(setup_arg.split("=")[0], None) # network storage # XXX this is nothing to be proud of @@ -865,18 +804,23 @@ class BootLoader(object): "setting boot args for network " "storage device") - setup_string = network.dracutSetupString(dep).strip() - self.boot_args.append(setup_string) - self.dracut_args.append(setup_string) + setup_args = network.dracutSetupArgs(dep) + self.boot_args.update(setup_args) + self.dracut_args.update(setup_args) - self.boot_args.extend(dracut_storage.values()) - self.dracut_args.extend(dracut_storage.values()) + self.boot_args.update(dracut_storage.values()) + self.dracut_args.update(dracut_storage.values()) # passed-in objects for cfg_obj in list(args) + kwargs.values(): - setup_string = cfg_obj.dracutSetupString().strip() - self.boot_args.append(setup_string) - self.dracut_args.append(setup_string) + if hasattr(cfg_obj, "dracutSetupArgs"): + setup_args = cfg_obj.dracutSetupArgs() + self.boot_args.update(setup_args) + self.dracut_args.update(setup_args) + else: + setup_string = cfg_obj.dracutSetupString() + self.boot_args.add(setup_string) + self.dracut_args.add(setup_string) # # preservation of some of our boot args @@ -891,7 +835,7 @@ class BootLoader(object): if arg: new_arg += "=%s" % arg - self.boot_args.append(new_arg) + self.boot_args.add(new_arg) # # configuration @@ -973,9 +917,9 @@ class BootLoader(object): if self.drive_order: f.write(" --driveorder=%s" % ",".join(self.drive_order)) - append = [a for a in self.boot_args if a not in self.dracut_args] + append = self.boot_args - self.dracut_args if append: - f.write(" --append=\"%s\"" % " ".join(append)) + f.write(" --append=\"%s\"" % append) f.write("\n") @@ -1106,7 +1050,7 @@ class GRUB(BootLoader): console_arg = "console=%s" % self.console if self.console_options: console_arg += ",%s" % self.console_options - self.boot_args.append(console_arg) + self.boot_args.add(console_arg) @property def encrypted_password(self): @@ -1185,10 +1129,10 @@ class GRUB(BootLoader): """ Write image entries into configuration file. """ for image in self.images: if isinstance(image, LinuxBootLoaderImage): - args = ArgumentList() + args = Arguments() grub_root = self.grub_device_name(self.stage2_device) - args.extend(["ro", "root=%s" % image.device.fstabSpec]) - args.extend(self.boot_args) + args.update(["ro", "root=%s" % image.device.fstabSpec]) + args.update(self.boot_args) stanza = ("title %(label)s (%(version)s)\n" "\troot %(grub_root)s\n" "\tkernel %(prefix)s/%(kernel)s %(args)s\n" @@ -1635,7 +1579,7 @@ class YabootSILOBase(BootLoader): # mac os images are handled specially in the header on mac continue - args = ArgumentList() + args = Arguments() if image.initrd: initrd_line = "\tinitrd=%s/%s\n" % (self.boot_prefix, image.initrd) @@ -1646,10 +1590,10 @@ class YabootSILOBase(BootLoader): if root_device_spec.startswith("/"): root_line = "\troot=%s\n" % root_device_spec else: - args.append("root=%s" % root_device_spec) + args.add("root=%s" % root_device_spec) root_line = "" - args.extend(self.boot_args) + args.update(self.boot_args) stanza = ("image=%(boot_prefix)s%(kernel)s\n" "\tlabel=%(label)s\n" @@ -1833,14 +1777,14 @@ class ZIPL(BootLoader): def write_config_images(self, config): for image in self.images: - args = ArgumentList() + args = Arguments() if image.initrd: initrd_line = "\tramdisk=%s/%s\n" % (self.boot_dir, image.initrd) else: initrd_line = "" - args.append("root=%s/%s" % (self.boot_dir, image.kernel)) - args.extend(self.boot_args) + args.add("root=%s/%s" % (self.boot_dir, image.kernel)) + args.update(self.boot_args) stanza = ("[%(label)s]\n" "\timage=%(boot_dir)s/%(kernel)s\n" "%(initrd_line)s" diff --git a/pyanaconda/installclass.py b/pyanaconda/installclass.py index 440b97c..630ef74 100644 --- a/pyanaconda/installclass.py +++ b/pyanaconda/installclass.py @@ -192,7 +192,7 @@ class BaseInstallClass(object): def configure(self, anaconda): anaconda.bootloader.timeout = self.bootloaderTimeoutDefault - anaconda.bootloader.boot_args.extend(self.bootloaderExtraArgs) + anaconda.bootloader.boot_args.update(self.bootloaderExtraArgs) def versionMatches(self, oldver): pass diff --git a/pyanaconda/iw/zipl_gui.py b/pyanaconda/iw/zipl_gui.py index 81cd0a2..047902c 100644 --- a/pyanaconda/iw/zipl_gui.py +++ b/pyanaconda/iw/zipl_gui.py @@ -42,7 +42,7 @@ class ZiplWindow (InstallWindow): pass def getNext (self): - self.bl.boot_args.extend(self.kernelEntry.get_text().split()) + self.bl.boot_args.update(self.kernelEntry.get_text().split()) # ZiplWindow tag="zipl" def getScreen(self, anaconda): diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index 65b7fce..58afe46 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -274,7 +274,7 @@ class Bootloader(commands.bootloader.F15_Bootloader): else: if self.appendLine: args = self.appendLine.split() - self.anaconda.bootloader.boot_args.extend(args) + self.anaconda.bootloader.boot_args.update(args) if self.password: self.anaconda.bootloader.password = self.password diff --git a/pyanaconda/language.py b/pyanaconda/language.py index 2db6e4f..6147b65 100644 --- a/pyanaconda/language.py +++ b/pyanaconda/language.py @@ -218,12 +218,12 @@ class Language(object): iutil.execWithRedirect("localedef", ["-i", locale_p, "-f", c["codeset"] or "UTF-8", self._instLang]) - def dracutSetupString(self): - args="" + def dracutSetupArgs(self): + args=set() for (key, val) in self.info.iteritems(): if val != None: - args += " %s=%s" % (key, val) + args.add("%s=%s" % (key, val)) return args diff --git a/pyanaconda/network.py b/pyanaconda/network.py index 0d10d82..4f9ab0f 100644 --- a/pyanaconda/network.py +++ b/pyanaconda/network.py @@ -815,8 +815,8 @@ class Network: return False # get a kernel cmdline string for dracut needed for access to host host - def dracutSetupString(self, networkStorageDevice): - netargs="" + def dracutSetupArgs(self, networkStorageDevice): + netargs=set() if networkStorageDevice.nic: # Storage bound to a specific nic (ie FCoE) @@ -834,7 +834,7 @@ class Network: dev = self.netdevices[nic] if dev.get('BOOTPROTO') == 'ibft': - netargs += "ip=ibft" + netargs.add("ip=ibft") elif networkStorageDevice.host_address: if self.hostname: hostname = self.hostname @@ -846,20 +846,20 @@ class Network: if dev.get('DHCPV6C') == "yes": # XXX combination with autoconf not yet clear, # support for dhcpv6 is not yet implemented in NM/ifcfg-rh - netargs += "ip=%s:dhcp6" % nic + netargs.add("ip=%s:dhcp6" % nic) elif dev.get('IPV6_AUTOCONF') == "yes": - netargs += "ip=%s:auto6" % nic + netargs.add("ip=%s:auto6" % nic) elif dev.get('IPV6ADDR'): ipaddr = "[%s]" % dev.get('IPV6ADDR') if dev.get('IPV6_DEFAULTGW'): gateway = "[%s]" % dev.get('IPV6_DEFAULTGW') else: gateway = "" - netargs += "ip=%s::%s:%s:%s:%s:none" % (ipaddr, gateway, - dev.get('PREFIX'), hostname, nic) + netargs.add("ip=%s::%s:%s:%s:%s:none" % (ipaddr, gateway, + dev.get('PREFIX'), hostname, nic)) else: if dev.get('bootproto').lower() == 'dhcp': - netargs += "ip=%s:dhcp" % nic + netargs.add("ip=%s:dhcp" % nic) else: if dev.get('GATEWAY'): gateway = dev.get('GATEWAY') @@ -871,28 +871,22 @@ class Network: if not netmask and prefix: netmask = isys.prefix2netmask(int(prefix)) - netargs += "ip=%s::%s:%s:%s:%s:none" % (dev.get('ipaddr'), - gateway, netmask, hostname, nic) + netargs.add("ip=%s::%s:%s:%s:%s:none" % (dev.get('ipaddr'), + gateway, netmask, hostname, nic)) hwaddr = dev.get("HWADDR") if hwaddr: - if netargs != "": - netargs += " " - - netargs += "ifname=%s:%s" % (nic, hwaddr.lower()) + netargs.add("ifname=%s:%s" % (nic, hwaddr.lower())) nettype = dev.get("NETTYPE") subchannels = dev.get("SUBCHANNELS") if iutil.isS390() and nettype and subchannels: - if netargs != "": - netargs += " " - - netargs += "rd.znet=%s,%s" % (nettype, subchannels) - + znet = "rd.znet=%s,%s" % (nettype, subchannels) options = dev.get("OPTIONS").strip("'\"") if options: options = filter(lambda x: x != '', options.split(' ')) - netargs += ",%s" % (','.join(options)) + znet += ",%s" % (','.join(options)) + netargs.add(znet) return netargs diff --git a/pyanaconda/packages.py b/pyanaconda/packages.py index cec96b8..a2c061c 100644 --- a/pyanaconda/packages.py +++ b/pyanaconda/packages.py @@ -289,7 +289,7 @@ def rpmSetupGraphicalSystem(anaconda): if iutil.isConsoleOnVirtualTerminal() and \ (ts.dbMatch('provides', 'rhgb').count() or \ ts.dbMatch('provides', 'plymouth').count()): - anaconda.bootloader.boot_args.extend(["rhgb", "quiet"]) + anaconda.bootloader.boot_args.update(["rhgb", "quiet"]) if ts.dbMatch('provides', 'service(graphical-login)').count() and \ ts.dbMatch('provides', 'xorg-x11-server-Xorg').count() and \ diff --git a/pyanaconda/storage/devices.py b/pyanaconda/storage/devices.py index 0d7e39c..9bd6c6e 100644 --- a/pyanaconda/storage/devices.py +++ b/pyanaconda/storage/devices.py @@ -328,8 +328,8 @@ class Device(object): return False - def dracutSetupString(self): - return "" + def dracutSetupArgs(self): + return set() @property def status(self): @@ -1940,9 +1940,8 @@ class LUKSDevice(DMCryptDevice): StorageDevice._postTeardown(self, recursive=recursive) - def dracutSetupString(self): - return "rd.luks.uuid=luks-%s" % self.slave.format.uuid - + def dracutSetupArgs(self): + return set(["rd.luks.uuid=luks-%s" % self.slave.format.uuid]) class LVMVolumeGroupDevice(DMDevice): """ An LVM Volume Group @@ -2620,10 +2619,10 @@ class LVMLogicalVolumeDevice(DMDevice): udev_settle() lvm.lvresize(self.vg.name, self._name, self.size) - def dracutSetupString(self): + def dracutSetupArgs(self): # Note no mapName usage here, this is a lvm cmdline name, which # is different (ofcourse) - return "rd.lvm.lv=%s/%s" % (self.vg.name, self._name) + return set(["rd.lvm.lv=%s/%s" % (self.vg.name, self._name)]) def checkSize(self): """ Check to make sure the size of the device is allowed by the @@ -3103,9 +3102,8 @@ class MDRaidArrayDevice(StorageDevice): def isDisk(self): return self.type == "mdbiosraidarray" - def dracutSetupString(self): - return "rd.md.uuid=%s" % self.uuid - + def dracutSetupArgs(self): + return set(["rd.md.uuid=%s" % self.uuid]) class DMRaidArrayDevice(DMDevice): """ A dmraid (device-mapper RAID) device """ @@ -3213,8 +3211,8 @@ class DMRaidArrayDevice(DMDevice): def model(self): return self.description - def dracutSetupString(self): - return "rd.dm.uuid=%s" % self.name + def dracutSetupArgs(self): + return set(["rd.dm.uuid=%s" % self.name]) class MultipathDevice(DMDevice): """ A multipath device """ @@ -3603,9 +3601,9 @@ class iScsiDiskDevice(DiskDevice, NetworkStorageDevice): NetworkStorageDevice.__init__(self, host_address=self.node.address) log.debug("created new iscsi disk %s %s:%d" % (self.node.name, self.node.address, self.node.port)) - def dracutSetupString(self): + def dracutSetupArgs(self): if self.ibft: - return "iscsi_firmware" + return set(["iscsi_firmware"]) address = self.node.address # surround ipv6 addresses with [] @@ -3622,9 +3620,9 @@ class iScsiDiskDevice(DiskDevice, NetworkStorageDevice): netroot += "@%s::%d::%s" % (address, self.node.port, self.node.name) - netroot += " iscsi_initiator=%s" % self.initiator + initiator = "iscsi_initiator=%s" % self.initiator - return netroot + return set([netroot, initiator]) class FcoeDiskDevice(DiskDevice, NetworkStorageDevice): """ An FCoE disk. """ @@ -3638,7 +3636,7 @@ class FcoeDiskDevice(DiskDevice, NetworkStorageDevice): NetworkStorageDevice.__init__(self, nic=self.nic) log.debug("created new fcoe disk %s @ %s" % (device, self.nic)) - def dracutSetupString(self): + def dracutSetupArgs(self): dcb = True from .fcoe import fcoe @@ -3651,7 +3649,7 @@ class FcoeDiskDevice(DiskDevice, NetworkStorageDevice): else: dcbOpt = "nodcb" - return "fcoe=edd:%s" % dcbOpt + return set(["fcoe=edd:%s" % dcbOpt]) class OpticalDevice(StorageDevice): @@ -3742,9 +3740,8 @@ class ZFCPDiskDevice(DiskDevice): 'wwpn': self.wwpn, 'lun': self.fcp_lun} - def dracutSetupString(self): - return "rd.zfcp=%s,%s,%s" % (self.hba_id, self.wwpn, self.fcp_lun,) - + def dracutSetupArgs(self): + return set(["rd.zfcp=%s,%s,%s" % (self.hba_id, self.wwpn, self.fcp_lun,)]) class DASDDevice(DiskDevice): """ A mainframe DASD. """ @@ -3766,7 +3763,7 @@ class DASDDevice(DiskDevice): def getOpts(self): return map(lambda (k, v): "%s=%s" % (k, v,), self.opts.items()) - def dracutSetupString(self): + def dracutSetupArgs(self): conf = "/etc/dasd.conf" opts = {} @@ -3782,9 +3779,9 @@ class DASDDevice(DiskDevice): opts[parts[0]] = parts if self.busid in opts.keys(): - return "rd.dasd=%s" % ",".join(opts[self.busid]) + return set(["rd.dasd=%s" % ",".join(opts[self.busid])]) else: - return "rd.dasd=%s" % ",".join([self.busid] + self.getOpts()) + return set(["rd.dasd=%s" % ",".join([self.busid] + self.getOpts())]) class NFSDevice(StorageDevice, NetworkStorageDevice): """ An NFS device """ diff --git a/pyanaconda/textw/zipl_text.py b/pyanaconda/textw/zipl_text.py index b50b890..01f7e77 100644 --- a/pyanaconda/textw/zipl_text.py +++ b/pyanaconda/textw/zipl_text.py @@ -62,5 +62,5 @@ class ZiplWindow: if button == TEXT_BACK_CHECK: return INSTALL_BACK - self.bl.kernel_args.extend(kernelentry.value().split()) + self.bl.boot_args.update(kernelentry.value().split()) return INSTALL_OK diff --git a/tests/pyanaconda_test/bootloader_test.py b/tests/pyanaconda_test/bootloader_test.py new file mode 100644 index 0000000..4173db3 --- /dev/null +++ b/tests/pyanaconda_test/bootloader_test.py @@ -0,0 +1,26 @@ +import mock + +class BootloaderTest(mock.TestCase): + def setUp(self): + self.setupModules( + ['_isys', 'logging', 'pyanaconda.anaconda_log', 'block', + 'pyanaconda.storage', + 'pyanaconda.storage.devicelibs', + 'pyanaconda.storage.errors']) + + import pyanaconda + pyanaconda.anaconda_log = mock.Mock() + + def tearDown(self): + self.tearDownModules() + + def test_argument(self): + from pyanaconda.bootloader import Arguments + a = Arguments() + a.update(set(["a", "b", "c"])) + b = Arguments() + b.add("b") + diff = a - b + self.assertEqual(diff, set(["a", "c"])) + self.assertIsInstance(diff, Arguments) + assert(str(diff) in ["a c", "c a"]) diff --git a/tests/pyanaconda_test/language_test.py b/tests/pyanaconda_test/language_test.py index 22cb57d..b5156d6 100644 --- a/tests/pyanaconda_test/language_test.py +++ b/tests/pyanaconda_test/language_test.py @@ -116,18 +116,18 @@ class LanguageTest(mock.TestCase): lang = pyanaconda.language.Language() self.assertEqual(set(lang.available()), set(['Czech', 'English', 'Hebrew'])) - def dracut_setup_string_default_test(self): + def dracut_setup_args_default_test(self): import pyanaconda.language lang = pyanaconda.language.Language() - ret = lang.dracutSetupString() - self.assertEqual(ret, ' LANG=%s' % ENVIRON_LANG) + ret = lang.dracutSetupArgs() + self.assertEqual(ret, set(['LANG=%s' % ENVIRON_LANG])) - def dracut_setup_string_after_set_test(self): + def dracut_setup_args_after_set_test(self): import pyanaconda.language lang = pyanaconda.language.Language() lang.systemLang = 'cs' - ret = lang.dracutSetupString() - self.assertEqual(ret, ' LANG=cs_CZ.UTF-8 SYSFONT=latarcyrheb-sun16') + ret = lang.dracutSetupArgs() + self.assertEqual(ret, set(['LANG=cs_CZ.UTF-8', 'SYSFONT=latarcyrheb-sun16'])) def get_current_lang_search_list_default_test(self): import pyanaconda.language -- 1.7.5.4 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list