[PATCH 1/3] Add support for adding zFCP devices in the GUI. (#994423)

Samantha N. Bueno sbueno+anaconda at redhat.com
Tue Nov 26 19:41:39 UTC 2013


This gives s390x users the ability to add a zFCP device for use during
installation. Even if a device is "offline", if the correct credentials
are entered, the device will be detected and turned "online" for use.

Resolves: rhbz#994423
---
 pyanaconda/constants.py                        |   1 +
 pyanaconda/ui/gui/spokes/advstorage/zfcp.glade | 447 +++++++++++++++++++++++++
 pyanaconda/ui/gui/spokes/advstorage/zfcp.py    | 189 +++++++++++
 3 files changed, 637 insertions(+)
 create mode 100644 pyanaconda/ui/gui/spokes/advstorage/zfcp.glade
 create mode 100644 pyanaconda/ui/gui/spokes/advstorage/zfcp.py

diff --git a/pyanaconda/constants.py b/pyanaconda/constants.py
index cf8a8ea..0abb9dc 100644
--- a/pyanaconda/constants.py
+++ b/pyanaconda/constants.py
@@ -128,6 +128,7 @@ THREAD_CONFIGURATION = "AnaConfigurationThread"
 THREAD_FCOE = "AnaFCOEThread"
 THREAD_ISCSI_DISCOVER = "AnaIscsiDiscoverThread"
 THREAD_ISCSI_LOGIN = "AnaIscsiLoginThread"
+THREAD_ZFCP_DISCOVER = "AnaZfcpDiscoverThread"
 THREAD_GEOLOCATION_REFRESH = "AnaGeolocationRefreshThread"
 THREAD_DATE_TIME = "AnaDateTimeThread"
 THREAD_TIME_INIT = "AnaTimeInitThread"
diff --git a/pyanaconda/ui/gui/spokes/advstorage/zfcp.glade b/pyanaconda/ui/gui/spokes/advstorage/zfcp.glade
new file mode 100644
index 0000000..03b5588
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/advstorage/zfcp.glade
@@ -0,0 +1,447 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkListStore" id="nodeStore">
+    <columns>
+      <!-- column-name nodeSelected -->
+      <column type="gboolean"/>
+      <!-- column-name nodeNotLoggedIn -->
+      <column type="gboolean"/>
+      <!-- column-name nodeName -->
+      <column type="gchararray"/>
+      <!-- column-name nodeIface -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkTreeModelFilter" id="nodeStoreFiltered">
+    <property name="child_model">nodeStore</property>
+  </object>
+  <object class="GtkDialog" id="zfcpDialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="modal">True</property>
+    <property name="type_hint">dialog</property>
+    <property name="decorated">False</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancelButton">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="okButton">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="box1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">ADD zFCP STORAGE TARGET</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkNotebook" id="zfcpNotebook">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="show_tabs">False</property>
+                <property name="show_border">False</property>
+                <child>
+                  <object class="GtkGrid" id="configureGrid">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="row_spacing">6</property>
+                    <property name="column_spacing">6</property>
+                    <child>
+                      <object class="GtkLabel" id="label5">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="margin_bottom">6</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">To use zFCP disks, you must provide the device number, WWPN, and LUN configured for the device.</property>
+                        <property name="wrap">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                        <property name="width">3</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="deviceEntry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hexpand">True</property>
+                        <property name="invisible_char">●</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">1</property>
+                        <property name="width">2</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label6">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Device number:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">deviceEntry</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">1</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="wwpnEntry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hexpand">True</property>
+                        <property name="invisible_char">●</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">2</property>
+                        <property name="width">2</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label7">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">WWPN:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">wwpnEntry</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">2</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkNotebook" id="conditionNotebook">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="valign">center</property>
+                        <property name="show_tabs">False</property>
+                        <property name="show_border">False</property>
+                        <child>
+                          <object class="GtkButton" id="startButton">
+                            <property name="label" translatable="yes">_Start Discovery</property>
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="halign">center</property>
+                            <property name="valign">center</property>
+                            <property name="vexpand">True</property>
+                            <property name="use_underline">True</property>
+                            <signal name="clicked" handler="on_start_clicked" swapped="no"/>
+                          </object>
+                        </child>
+                        <child type="tab">
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <object class="GtkBox" id="waitBox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="halign">center</property>
+                            <property name="spacing">5</property>
+                            <child>
+                              <object class="GtkSpinner" id="waitSpinner">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="active">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="label13">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">Discovering zFCP devices.  This may take a moment...</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child type="tab">
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <object class="GtkGrid" id="errorBox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="halign">center</property>
+                            <property name="row_spacing">6</property>
+                            <property name="column_spacing">6</property>
+                            <child>
+                              <object class="GtkImage" id="image3">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="xalign">1</property>
+                                <property name="stock">gtk-dialog-error</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">0</property>
+                                <property name="top_attach">0</property>
+                                <property name="width">1</property>
+                                <property name="height">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="label14">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">&lt;b&gt;Device discovery failed.&lt;/b&gt;</property>
+                                <property name="use_markup">True</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="top_attach">0</property>
+                                <property name="width">1</property>
+                                <property name="height">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="label22">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">The following error occurred discovering zFCP devices.  Please double check your configuration information and try again.</property>
+                                <property name="wrap">True</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="top_attach">1</property>
+                                <property name="width">1</property>
+                                <property name="height">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="deviceErrorLabel">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="xalign">0</property>
+                                <property name="label">Error message goes here.</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="top_attach">2</property>
+                                <property name="width">1</property>
+                                <property name="height">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkButton" id="retryButton">
+                                <property name="label" translatable="yes">_Retry Discovery</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">True</property>
+                                <property name="halign">center</property>
+                                <property name="valign">center</property>
+                                <property name="use_underline">True</property>
+                                <signal name="clicked" handler="on_start_clicked" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="top_attach">3</property>
+                                <property name="width">1</property>
+                                <property name="height">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <placeholder/>
+                            </child>
+                            <child>
+                              <placeholder/>
+                            </child>
+                            <child>
+                              <placeholder/>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                        <child type="tab">
+                          <placeholder/>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">7</property>
+                        <property name="width">3</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">LUN:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">lunEntry</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">3</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="lunEntry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hexpand">True</property>
+                        <property name="invisible_char">●</property>
+                        <property name="invisible_char_set">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">3</property>
+                        <property name="width">2</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                </child>
+                <child type="tab">
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child type="tab">
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child type="tab">
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">cancelButton</action-widget>
+      <action-widget response="1">okButton</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/pyanaconda/ui/gui/spokes/advstorage/zfcp.py b/pyanaconda/ui/gui/spokes/advstorage/zfcp.py
new file mode 100644
index 0000000..5b55ec7
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/advstorage/zfcp.py
@@ -0,0 +1,189 @@
+# zFCP configuration dialog
+#
+# Copyright (C) 2013  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Samantha N. Bueno <sbueno at redhat.com>
+#
+
+#from collections import namedtuple
+# pylint: disable-msg=E0611
+from gi.repository import GLib
+
+from pyanaconda import constants
+from pyanaconda.threads import threadMgr, AnacondaThread
+from pyanaconda.ui.gui import GUIObject
+
+from blivet.zfcp import ZFCPDevice
+
+__all__ = ["ZFCPDialog"]
+
+class ZFCPDialog(GUIObject):
+    """ Gtk dialog which allows users to manually add zFCP devices without
+        having previously specified them in a parm file.
+    """
+    builderObjects = ["zfcpDialog"]
+    mainWidgetName = "zfcpDialog"
+    uiFile = "spokes/advstorage/zfcp.glade"
+
+    def __init__(self, data, storage):
+        GUIObject.__init__(self, data)
+        self.storage = storage
+        self.zfcp = self.storage.zfcp()
+
+        self._discoveryError = None
+
+        self._discoveredNodes = []
+        self._update_devicetree = False
+
+    def refresh(self):
+        self._zfcpNotebook = self.builder.get_object("zfcpNotebook")
+
+        self._configureGrid = self.builder.get_object("configureGrid")
+        self._conditionNotebook = self.builder.get_object("conditionNotebook")
+
+        self._startButton = self.builder.get_object("startButton")
+        self._okButton = self.builder.get_object("okButton")
+        self._cancelButton = self.builder.get_object("cancelButton")
+
+        self._deviceEntry = self.builder.get_object("deviceEntry")
+        self._deviceEntry.set_text("")
+        self._deviceEntry.set_sensitive(True)
+        self._startButton.set_sensitive(True)
+
+    def run(self):
+        rc = self.window.run()
+        self.window.destroy()
+        # We need to call this to get the device nodes to show up
+        # in our devicetree.
+        if self._update_devicetree:
+            self.storage.devicetree.populate()
+        return rc
+
+    def _set_configure_sensitive(self, sensitivity):
+        for child in self._configureGrid.get_children():
+            if child == self._deviceEntry:
+                self._deviceEntry.set_sensitive(True)
+            elif child != self._conditionNotebook:
+                child.set_sensitive(sensitivity)
+
+    def _device_valid(self, dev):
+        """ Check whether the entered CCW entry is valid. """
+        widget = self.builder.get_object("deviceEntry")
+        text = widget.get_text()
+
+        try:
+            device = dev.sanitizeDeviceInput(text)
+            return device
+        except ValueError as e:
+            raise ValueError(e)
+
+    def _wwpn_valid(self, dev):
+        """ Check whether the entered WWPN entry is valid. """
+        widget = self.builder.get_object("wwpnEntry")
+        text = widget.get_text()
+
+        try:
+            wwpn = dev.sanitizeWWPNInput(text)
+            return wwpn
+        except ValueError as e:
+            raise ValueError(e)
+
+    def _lun_valid(self, dev):
+        """ Check whether the entered LUN entry is valid. """
+        widget = self.builder.get_object("lunEntry")
+        text = widget.get_text()
+
+        try:
+            lun = dev.sanitizeFCPLInput(text)
+            return lun
+        except ValueError as e:
+            raise ValueError(e)
+
+    def on_start_clicked(self, *args):
+        """ Go through the process of validating entry contents and then
+            attempt to add the device.
+        """
+        # First update widgets
+        self._startButton.hide()
+        self._cancelButton.set_sensitive(False)
+        self._okButton.set_sensitive(False)
+
+        self._conditionNotebook.set_current_page(1)
+        self._set_configure_sensitive(False)
+        self._deviceEntry.set_sensitive(False)
+
+        # Make a zFCP object with some dummy credentials so we can validate our
+        # actual input
+        self._conditionNotebook.set_current_page(1)
+        dev = ZFCPDevice("0.0.0000", "0x0000000000000000", "0x0000000000000000")
+        # below really, really is ugly and needs to be re-factored, but this
+        # should give a good base idea as far as expected behavior should go
+        try:
+            device = self._device_valid(dev)
+            wwpn = self._wwpn_valid(dev)
+            lun = self._lun_valid(dev)
+        except ValueError as e:
+            _config_error = str(e)
+            self.builder.get_object("deviceErrorLabel").set_text(_config_error)
+            self._conditionNotebook.set_current_page(2)
+
+        spinner = self.builder.get_object("waitSpinner")
+        spinner.start()
+
+        threadMgr.add(AnacondaThread(name=constants.THREAD_ZFCP_DISCOVER, target=self._discover, args=(device, wwpn, lun)))
+        GLib.timeout_add(30, self._check_discover)
+
+    def _check_discover(self, *args):
+        """ After the zFCP discover thread runs, check to see whether a valid
+            device was discovered. Display an error message if not.
+        """
+        if threadMgr.get(constants.THREAD_ZFCP_DISCOVER):
+            return True
+
+        spinner = self.builder.get_object("waitSpinner")
+        spinner.stop()
+
+        if self._discoveryError:
+            # Failure, display a message and leave the user on the dialog so
+            # they can try again (or cancel)
+            self.builder.get_object("deviceErrorLabel").set_text(self._discoveryError)
+            self._discoveryError = None
+            self._conditionNotebook.set_current_page(2)
+            self._set_configure_sensitive(True)
+        else:
+            # Great success. Just return to the advanced storage window and let the
+            # UI update with the newly-added device
+            self.window.response(1)
+            return True
+
+        self._cancelButton.set_sensitive(True)
+        return False
+
+    def _discover(self, *args):
+        """ Given the configuration options from a user, attempt to discover
+            a zFCP device. This includes searching black-listed devices.
+        """
+        # attempt to add the device
+        try:
+            self.zfcp.addFCP(args[0], args[1], args[2])
+            self._update_devicetree = True
+        except ValueError as e:
+            self._discoveryError = str(e)
+            return
+
+        if len(self._discoveredNodes) == 0:
+            self._discoveryErorr = "No devices discovered."
-- 
1.8.3.1



More information about the anaconda-patches mailing list