Adds a new option dialog to the filtering screen, with a checkbox. If user unchecks this, /etc/multipath.conf is generated with user_friendly_names set to "no". This will give all mpath devices names in the /dev/mapper/<wwid> format, partitions are /dev/mapper<wwid>p1. The setting defaults to using the friendly names. Resolves: rhbz#709653 --- iw/filter_gui.py | 39 ++++++++++++---- kickstart.py | 2 +- po/POTFILES.in | 1 + storage/__init__.py | 9 +++- storage/devicelibs/mpath.py | 8 ++-- storage/devicetree.py | 20 +++++---- tests/storage/devicelibs/mpath.py | 91 ++++++++++++++++++++++++++++++------- ui/device-options.glade | 91 +++++++++++++++++++++++++++++++++++++ ui/filter.glade | 12 +++++ 9 files changed, 229 insertions(+), 44 deletions(-) mode change 100644 => 100755 tests/storage/devicelibs/mpath.py create mode 100644 ui/device-options.glade diff --git a/iw/filter_gui.py b/iw/filter_gui.py index 63655a0..8717d6e 100644 --- a/iw/filter_gui.py +++ b/iw/filter_gui.py @@ -418,10 +418,20 @@ class FilterWindow(InstallWindow): # are in the list. selected = set() for dev in self.pages[0].ds.getSelected(): - selected.add(udev_device_get_name(dev[OBJECT_COL])) - if isMultipath(dev[OBJECT_COL]) or isRAID(dev[OBJECT_COL]): + info = dev[OBJECT_COL] + if isRAID(info): + selected.add(udev_device_get_name(info)) members = dev[MEMBERS_COL].split("\n") selected.update(set(members)) + if isMultipath(info): + if self.anaconda.id.storage.mpathFriendlyNames: + selected.add(udev_device_get_name(info)) + else: + selected.add(dev[SERIAL_COL]) + members = dev[MEMBERS_COL].split("\n") + selected.update(set(members)) + else: + selected.add(udev_device_get_name(info)) if len(selected) == 0: self.anaconda.intf.messageWindow(_("Error"), @@ -446,10 +456,9 @@ class FilterWindow(InstallWindow): udev_get_block_devices()) mcw = MultipathConfigWriter() - cfg = mcw.write() - open("/etc/multipath.conf", "w+").write(cfg) - del cfg - del mcw + cfg = mcw.write(friendly_names=True) + with open("/etc/multipath.conf", "w+") as mpath_cfg: + mpath_cfg.write(cfg) (new_singlepaths, new_mpaths, new_partitions) = identifyMultipaths(new_disks) (new_raids, new_nonraids) = self.split_list(lambda d: isRAID(d) and not isCCISS(d), @@ -540,6 +549,15 @@ class FilterWindow(InstallWindow): np.ds.addColumn(_("Device"), DEVICE_COL, displayed=False) return np + def _options_clicked(self, button): + (xml, dialog) = gui.getGladeWidget("device-options.glade", + "options_dialog") + friendly_cb = xml.get_widget("mpath_friendly_names") + friendly_cb.set_active(self.anaconda.id.storage.mpathFriendlyNames) + if dialog.run() == gtk.RESPONSE_OK: + self.anaconda.id.storage.mpathFriendlyNames = friendly_cb.get_active() + dialog.destroy() + def _page_switched(self, notebook, useless, page_num): # When the page is switched, we need to change what is visible so the # Select All button only selects/deselected things on the current page. @@ -558,9 +576,11 @@ class FilterWindow(InstallWindow): self.buttonBox = self.xml.get_widget("buttonBox") self.notebook = self.xml.get_widget("notebook") self.addAdvanced = self.xml.get_widget("addAdvancedButton") + self.options = self.xml.get_widget("optionsButton") self.notebook.connect("switch-page", self._page_switched) self.addAdvanced.connect("clicked", self._add_advanced_clicked) + self.options.connect("clicked", self._options_clicked) self.pages = [] @@ -600,10 +620,9 @@ class FilterWindow(InstallWindow): udev_get_block_devices()) mcw = MultipathConfigWriter() - cfg = mcw.write() - open("/etc/multipath.conf", "w+").write(cfg) - del cfg - del mcw + cfg = mcw.write(friendly_names=True) + with open("/etc/multipath.conf", "w+") as mpath_cfg: + mpath_cfg.write(cfg) if anaconda.isKickstart: # identifyMultipaths() uses 'multipath -d' to find mpath devices. In diff --git a/kickstart.py b/kickstart.py index a4e25ac..61d00d3 100644 --- a/kickstart.py +++ b/kickstart.py @@ -184,7 +184,7 @@ def getEscrowCertificate(anaconda, url): def detect_multipaths(): global multipaths mcw = MultipathConfigWriter() - cfg = mcw.write() + cfg = mcw.write(friendly_names=True) with open("/etc/multipath.conf", "w+") as mpath_cfg: mpath_cfg.write(cfg) devices = udev_get_block_devices() diff --git a/po/POTFILES.in b/po/POTFILES.in index 39a446b..6b8101d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -158,6 +158,7 @@ ui/blwhere.glade.h ui/cleardisks.glade.h ui/create-storage.glade.h ui/detailed-dialog.glade.h +ui/device-options.glade.h ui/fcoe-config.glade.h ui/filter.glade.h ui/iscsi-dialogs.glade.h diff --git a/storage/__init__.py b/storage/__init__.py index a7ff29b..007f029 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -309,6 +309,7 @@ class Storage(object): self.protectedDevSpecs = [] self.autoPartitionRequests = [] self.eddDict = {} + self.mpathFriendlyNames = True self.__luksDevs = {} @@ -334,7 +335,8 @@ class Storage(object): passphrase=self.encryptionPassphrase, luksDict=self.__luksDevs, iscsi=self.iscsi, - dasd=self.dasd) + dasd=self.dasd, + mpathFriendlyNames=self.mpathFriendlyNames) self.fsset = FSSet(self.devicetree, self.anaconda.rootPath) self.services = set() @@ -423,7 +425,8 @@ class Storage(object): passphrase=self.encryptionPassphrase, luksDict=self.__luksDevs, iscsi=self.iscsi, - dasd=self.dasd) + dasd=self.dasd, + mpathFriendlyNames=self.mpathFriendlyNames) self.devicetree.populate() self.fsset = FSSet(self.devicetree, self.anaconda.rootPath) self.eddDict = get_edd_dict(self.partitioned) @@ -2174,7 +2177,7 @@ class FSSet(object): if multipath_conf: multipath_path = os.path.normpath("%s/etc/multipath.conf" % instPath) - conf_contents = multipath_conf.write() + conf_contents = multipath_conf.write(self.devicetree.mpathFriendlyNames) f = open(multipath_path, "w") f.write(conf_contents) f.close() diff --git a/storage/devicelibs/mpath.py b/storage/devicelibs/mpath.py index fb7e1aa..eb6e999 100644 --- a/storage/devicelibs/mpath.py +++ b/storage/devicelibs/mpath.py @@ -80,7 +80,7 @@ def parseMultipathOutput(output): policy = re.compile('^[|+` -]+policy') device = re.compile('^[|+` -]+[0-9]+:[0-9]+:[0-9]+:[0-9]+ +([a-zA-Z0-9!/]+)') - create = re.compile('^(create: )?(mpath\w+)') + create = re.compile('^(create: )?(mpath\w+|[a-f0-9]+)') lines = output.split('\n') for line in lines: @@ -236,7 +236,7 @@ class MultipathConfigWriter: def addMultipathDevice(self, mpath): self.mpaths.append(mpath) - def write(self): + def write(self, friendly_names): # if you add anything here, be sure and also add it to anaconda's # multipath.conf ret = '' @@ -244,7 +244,7 @@ class MultipathConfigWriter: # multipath.conf written by anaconda defaults { - user_friendly_names yes + user_friendly_names %(friendly_names)s } blacklist { devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" @@ -273,7 +273,7 @@ blacklist { device { vendor "HPT" } -""" +""" % {'friendly_names' : "yes" if friendly_names else "no"} for device in self.blacklist_devices: if device.serial: ret += '\twwid "%s"\n' % device.serial diff --git a/storage/devicetree.py b/storage/devicetree.py index 0d48dda..b419026 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -153,7 +153,8 @@ class DeviceTree(object): def __init__(self, intf=None, ignored=[], exclusive=[], type=CLEARPART_TYPE_NONE, clear=[], zeroMbr=None, reinitializeDisks=None, protected=[], - passphrase=None, luksDict=None, iscsi=None, dasd=None): + passphrase=None, luksDict=None, iscsi=None, dasd=None, + mpathFriendlyNames=True): # internal data members self._devices = [] self._actions = [] @@ -169,6 +170,7 @@ class DeviceTree(object): self.reinitializeDisks = reinitializeDisks self.iscsi = iscsi self.dasd = dasd + self.mpathFriendlyNames = mpathFriendlyNames # protected device specs as provided by the user self.protectedDevSpecs = protected @@ -1295,9 +1297,9 @@ class DeviceTree(object): # if udev_device_is_multipath_member(info) and device is None: device = self.addUdevDiskDevice(info) - elif udev_device_is_dm(info) and udev_device_is_dm_mpath(info): + elif udev_device_is_dm(info) and udev_device_is_dm_mpath(info) and device is None: log.debug("%s is a multipath device" % name) - self.addUdevDMDevice(info) + device = self.addUdevDMDevice(info) elif udev_device_is_dm(info): log.debug("%s is a device-mapper device" % name) # try to look up the device @@ -2057,9 +2059,9 @@ class DeviceTree(object): % (livetarget,)) self.protectedDevNames.append(livetarget) - cfg = self.__multipathConfigWriter.write() - open("/etc/multipath.conf", "w+").write(cfg) - del cfg + cfg = self.__multipathConfigWriter.write(self.mpathFriendlyNames) + with open("/etc/multipath.conf", "w+") as mpath_cfg: + mpath_cfg.write(cfg) devices = udev_get_block_devices() (singles, mpaths, partitions) = devicelibs.mpath.identifyMultipaths(devices) @@ -2096,9 +2098,9 @@ class DeviceTree(object): for d in self.devices: if not d.name in whitelist: self.__multipathConfigWriter.addBlacklistDevice(d) - cfg = self.__multipathConfigWriter.write() - open("/etc/multipath.conf", "w+").write(cfg) - del cfg + cfg = self.__multipathConfigWriter.write(self.mpathFriendlyNames) + with open("/etc/multipath.conf", "w+") as mpath_cfg: + mpath_cfg.write(cfg) # Now, loop and scan for devices that have appeared since the two above # blocks or since previous iterations. diff --git a/tests/storage/devicelibs/mpath.py b/tests/storage/devicelibs/mpath.py old mode 100644 new mode 100755 index 8210b0b..f83ae3e --- a/tests/storage/devicelibs/mpath.py +++ b/tests/storage/devicelibs/mpath.py @@ -1,13 +1,10 @@ -import baseclass -import unittest -import storage.devicelibs.mpath as mpath - -class MPathTestCase(baseclass.DevicelibsTestCase): - def testMPath(self): - ## - ## parseMultipathOutput - ## - output="""\ +#!/usr/bin/python +import mock + +class MPathTestCase(mock.TestCase): + + # creating devices, user_friendly_names set to yes + output1 = """\ create: mpathb (1ATA ST3120026AS 5M) undef ATA,ST3120026AS size=112G features='0' hwhandler='0' wp=undef `-+- policy='round-robin 0' prio=1 status=undef @@ -16,14 +13,74 @@ create: mpatha (36006016092d21800703762872c60db11) undef DGC,RAID 5 size=10G features='1 queue_if_no_path' hwhandler='1 emc' wp=undef `-+- policy='round-robin 0' prio=2 status=undef |- 6:0:0:0 sdb 8:16 undef ready running - `- 7:0:0:0 sdc 8:32 undef ready running + `- 7:0:0:0 sdc 8:32 undef ready running\ +""" + + # listing existing devices, user_friendly_names set to yes + output2 = """\ +mpathb (3600a0b800067fcc9000001f34d23ff88) dm-1 IBM,1726-4xx FAStT +size=100G features='0' hwhandler='1 rdac' wp=rw +`-+- policy='round-robin 0' prio=-1 status=active + |- 1:0:0:0 sda 8:0 active undef running + `- 2:0:0:0 sdc 8:32 active undef running +mpatha (3600a0b800067fabc000067694d23fe6e) dm-0 IBM,1726-4xx FAStT +size=100G features='0' hwhandler='1 rdac' wp=rw +`-+- policy='round-robin 0' prio=-1 status=active + |- 1:0:0:1 sdb 8:16 active undef running + `- 2:0:0:1 sdd 8:48 active undef running +""" + + # creating devices, user_friendly_names set to no + output3 = """\ +create: 3600a0b800067fabc000067694d23fe6e undef IBM,1726-4xx FAStT +size=100G features='1 queue_if_no_path' hwhandler='1 rdac' wp=undef +`-+- policy='round-robin 0' prio=6 status=undef + |- 1:0:0:1 sdb 8:16 undef ready running + `- 2:0:0:1 sdd 8:48 undef ready running +create: 3600a0b800067fcc9000001f34d23ff88 undef IBM,1726-4xx FAStT +size=100G features='1 queue_if_no_path' hwhandler='1 rdac' wp=undef +`-+- policy='round-robin 0' prio=3 status=undef + |- 1:0:0:0 sda 8:0 undef ready running + `- 2:0:0:0 sdc 8:32 undef ready running\ """ - topology = mpath.parseMultipathOutput(output) - expected = {'mpatha':['sdb','sdc'], 'mpathb':['sda']} - self.assertEqual(topology, expected) + + # listing existing devices, user_friendly_names set to no + output4 = """\ +3600a0b800067fcc9000001f34d23ff88 dm-1 IBM,1726-4xx FAStT +size=100G features='0' hwhandler='1 rdac' wp=rw +`-+- policy='round-robin 0' prio=-1 status=active + |- 1:0:0:0 sda 8:0 active undef running + `- 2:0:0:0 sdc 8:32 active undef running +3600a0b800067fabc000067694d23fe6e dm-0 IBM,1726-4xx FAStT +size=100G features='0' hwhandler='1 rdac' wp=rw +`-+- policy='round-robin 0' prio=-1 status=active + |- 1:0:0:1 sdb 8:16 active undef running + `- 2:0:0:1 sdd 8:48 active undef running +""" + + def setUp(self): + self.setupModules( + ['_isys', 'logging', 'anaconda_log', 'block']) + + def tearDown(self): + self.tearDownModules() + + def testParse(self): + from storage.devicelibs import mpath + topology = mpath.parseMultipathOutput(self.output1) + self.assertEqual(topology, + {'mpatha':['sdb','sdc'], 'mpathb':['sda']}) + topology = mpath.parseMultipathOutput(self.output2) + self.assertEqual(topology, + {'mpathb':['sda','sdc'], 'mpatha':['sdb', 'sdd']}) + topology = mpath.parseMultipathOutput(self.output3) + self.assertEqual(topology, + {'3600a0b800067fabc000067694d23fe6e' : ['sdb','sdd'], + '3600a0b800067fcc9000001f34d23ff88' : ['sda', 'sdc']}) + topology = mpath.parseMultipathOutput(self.output4) + self.assertEqual(topology, + {'3600a0b800067fabc000067694d23fe6e' : ['sdb','sdd'], + '3600a0b800067fcc9000001f34d23ff88' : ['sda', 'sdc']}) def suite(): return unittest.TestLoader().loadTestsFromTestCase(MPathTestCase) - -if __name__ == '__main__': - unittest.main() diff --git a/ui/device-options.glade b/ui/device-options.glade new file mode 100644 index 0000000..7672f6e --- /dev/null +++ b/ui/device-options.glade @@ -0,0 +1,91 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="options_dialog"> + <property name="visible">True</property> + <property name="title" translatable="yes">Device Options</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="cancelbutton1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="okbutton1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="mpath_friendly_names"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">If enabled the multipath devices are named following the /dev/mapper/mpath<letter> format instead of /dev/mapper/<wwid>.</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Use _friendly names for multipath devices</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/ui/filter.glade b/ui/filter.glade index a2da183..54f141e 100644 --- a/ui/filter.glade +++ b/ui/filter.glade @@ -1264,6 +1264,18 @@ Target Identifier</property> <property name="spacing">0</property> <child> + <widget class="GtkButton" id="optionsButton"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Device Options</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + + <child> <widget class="GtkButton" id="addAdvancedButton"> <property name="visible">True</property> <property name="can_default">True</property> -- 1.7.5.2 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list