[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[rhel6-branch] Implement an option that lets anaconda name mpath devices by the wwid.



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&lt;letter&gt; format instead of /dev/mapper/&lt;wwid&gt;.</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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]