This does not yet include support for CHAP/rCHAP (well, it might but I have
not tested it at all), complete error handling, or correct filtering.
However if you are careful and type everything in as you should, you can add
iSCSI disks to the installer.
---
configure.ac | 1 +
po/POTFILES.in | 1 +
pyanaconda/constants.py | 2 +
pyanaconda/ui/gui/spokes/Makefile.am | 2 +-
pyanaconda/ui/gui/spokes/advstorage/Makefile.am | 25 +
pyanaconda/ui/gui/spokes/advstorage/__init__.py | 0
pyanaconda/ui/gui/spokes/advstorage/iscsi.glade | 1144 +++++++++++++++++++++++
pyanaconda/ui/gui/spokes/advstorage/iscsi.py | 340 +++++++
pyanaconda/ui/gui/spokes/filter.glade | 327 ++++++-
pyanaconda/ui/gui/spokes/filter.py | 87 +-
pyanaconda/ui/lib/disks.py | 4 +-
11 files changed, 1925 insertions(+), 8 deletions(-)
create mode 100644 pyanaconda/ui/gui/spokes/advstorage/Makefile.am
create mode 100644 pyanaconda/ui/gui/spokes/advstorage/__init__.py
create mode 100644 pyanaconda/ui/gui/spokes/advstorage/iscsi.glade
create mode 100644 pyanaconda/ui/gui/spokes/advstorage/iscsi.py
diff --git a/configure.ac b/configure.ac
index a928365..d90d83f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -233,6 +233,7 @@ AC_CONFIG_FILES([Makefile
pyanaconda/ui/gui/categories/Makefile
pyanaconda/ui/gui/hubs/Makefile
pyanaconda/ui/gui/spokes/Makefile
+ pyanaconda/ui/gui/spokes/advstorage/Makefile
pyanaconda/ui/gui/spokes/lib/Makefile
pyanaconda/ui/gui/tools/Makefile
pyanaconda/ui/gui/Makefile
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f5a1d8c..695be87 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -89,6 +89,7 @@ pyanaconda/ui/gui/spokes/source.glade
pyanaconda/ui/gui/spokes/welcome.glade
pyanaconda/ui/gui/spokes/user.glade
pyanaconda/ui/gui/spokes/custom.glade
+pyanaconda/ui/gui/spokes/advstorage/iscsi.glade
pyanaconda/ui/gui/spokes/lib/cart.glade
pyanaconda/ui/gui/spokes/lib/detailederror.glade
pyanaconda/ui/gui/spokes/lib/passphrase.glade
diff --git a/pyanaconda/constants.py b/pyanaconda/constants.py
index 4310bf1..f050a8d 100644
--- a/pyanaconda/constants.py
+++ b/pyanaconda/constants.py
@@ -122,3 +122,5 @@ THREAD_CHECK_SOFTWARE = "AnaCheckSoftwareThread"
THREAD_SOURCE_WATCHER = "AnaSourceWatcher"
THREAD_INSTALL = "AnaInstallThread"
THREAD_CONFIGURATION = "AnaConfigurationThread"
+THREAD_ISCSI_DISCOVER = "AnaIscsiDiscoverThread"
+THREAD_ISCSI_LOGIN = "AnaIscsiLoginThread"
diff --git a/pyanaconda/ui/gui/spokes/Makefile.am b/pyanaconda/ui/gui/spokes/Makefile.am
index 352f4e4..a052dab 100644
--- a/pyanaconda/ui/gui/spokes/Makefile.am
+++ b/pyanaconda/ui/gui/spokes/Makefile.am
@@ -15,7 +15,7 @@
#
# Author: Chris Lumens <clumens(a)redhat.com>
-SUBDIRS = lib
+SUBDIRS = advstorage lib
MAINTAINERCLEANFILES = Makefile.in
diff --git a/pyanaconda/ui/gui/spokes/advstorage/Makefile.am
b/pyanaconda/ui/gui/spokes/advstorage/Makefile.am
new file mode 100644
index 0000000..bcad43c
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/advstorage/Makefile.am
@@ -0,0 +1,25 @@
+# Copyright (C) 2013 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+# Author: Chris Lumens <clumens(a)redhat.com>
+
+MAINTAINERCLEANFILES = Makefile.in
+
+pkgpyexecdir = $(pyexecdir)/py$(PACKAGE_NAME)
+spokesdir = $(pkgpyexecdir)/ui/gui/spokes/advstorage
+spokes_PYTHON = *.py
+
+uidir = $(datadir)/$(PACKAGE_NAME)/ui/spokes/advstorage
+dist_ui_DATA = *.glade
diff --git a/pyanaconda/ui/gui/spokes/advstorage/__init__.py
b/pyanaconda/ui/gui/spokes/advstorage/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pyanaconda/ui/gui/spokes/advstorage/iscsi.glade
b/pyanaconda/ui/gui/spokes/advstorage/iscsi.glade
new file mode 100644
index 0000000..1e5ac2c
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/advstorage/iscsi.glade
@@ -0,0 +1,1144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkDialog" id="iscsiDialog">
+ <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
iSCSI 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="iscsiNotebook">
+ <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 iSCSI disks, you must provide the address of your
iSCSI target and the iSCSI initiator name you've configured for your
host.</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="targetEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property
name="invisible_char">●</property>
+ <signal name="changed"
handler="on_login_field_changed" swapped="no"/>
+ </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">_Target IP Address:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">targetEntry</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="initiatorEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property
name="invisible_char">●</property>
+ <signal name="changed"
handler="on_login_field_changed" swapped="no"/>
+ </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">iSCSI _Initiator Name:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">initiatorEntry</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="GtkComboBoxText"
id="authTypeCombo">
+ <property name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="active">0</property>
+ <property
name="entry_text_column">0</property>
+ <property name="id_column">1</property>
+ <items>
+ <item translatable="yes">No credentials
(discovery authentication disabled)</item>
+ <item translatable="yes">CHAP
pair</item>
+ <item translatable="yes">CHAP pair and a
reverse pair</item>
+ </items>
+ <signal name="changed"
handler="on_auth_type_changed" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">4</property>
+ <property name="width">2</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label"
translatable="yes">_Discovery Authentication Type:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">authTypeCombo</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="xalign">0</property>
+ <property name="label"
translatable="yes"><span
size="small"><b>Example:</b>
iqn.2012-09.com.example:diskarrays-sn-a8675309</span></property>
+ <property
name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property name="stock">gtk-info</property>
+ </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>
+ <object class="GtkNotebook"
id="authNotebook">
+ <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="GtkLabel"
id="authFiller">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ </object>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkGrid"
id="chapGrid">
+ <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="label2">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">CHAP _Username:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">chapUsernameEntry</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="label3">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">CHAP _Password:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">chapPasswordEntry</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="chapUsernameEntry">
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="hexpand">True</property>
+ <property
name="invisible_char">●</property>
+ <signal name="changed"
handler="on_login_field_changed" swapped="no"/>
+ </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="GtkEntry"
id="chapPasswordEntry">
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="hexpand">True</property>
+ <property
name="visibility">False</property>
+ <property
name="invisible_char">●</property>
+ <signal name="changed"
handler="on_login_field_changed" swapped="no"/>
+ </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>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkGrid"
id="rchapGrid">
+ <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="GtkEntry"
id="rchapUsernameEntry">
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="hexpand">True</property>
+ <property
name="invisible_char">●</property>
+ <signal name="changed"
handler="on_login_field_changed" swapped="no"/>
+ </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="GtkEntry"
id="rchapPasswordEntry">
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="hexpand">True</property>
+ <property
name="visibility">False</property>
+ <property
name="invisible_char">●</property>
+ <signal name="changed"
handler="on_login_field_changed" swapped="no"/>
+ </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="GtkEntry"
id="rchapReverseUsername">
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="hexpand">True</property>
+ <property
name="invisible_char">●</property>
+ <signal name="changed"
handler="on_login_field_changed" swapped="no"/>
+ </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="GtkEntry"
id="rchapReversePassword">
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="hexpand">True</property>
+ <property
name="visibility">False</property>
+ <property
name="invisible_char">●</property>
+ <signal name="changed"
handler="on_login_field_changed" 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>
+ <object class="GtkLabel"
id="label4">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">CHAP _Username:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">rchapUsernameEntry</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="label10">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">CHAP _Password:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">rchapPasswordEntry</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="GtkLabel"
id="label11">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">Reverse CHAP User_name:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">rchapReverseUsername</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="GtkLabel"
id="label12">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">Reverse CHAP Pass_word:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">rchapReversePassword</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>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">5</property>
+ <property name="width">2</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 iSCSI targets. 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"><b>No iSCSI targets
discovered.</b></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">There was an error discovering iSCSI targets. Check your
target IP address and server configuration 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>
+ <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">6</property>
+ <property name="width">3</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkGrid" id="discoveredGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel"
id="discoveredLabel">
+ <property name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label"
translatable="yes">The following nodes were discovered using the iSCSI
initiator <b>%(initiatorName)s</b> using the target IP address
<b>%(targetAddress)s</b>. Please select which nodes you wish
to log into:</property>
+ <property
name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">2</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow"
id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView"
id="nodeTreeView">
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="model">nodeStoreFiltered</property>
+ <property
name="headers_clickable">False</property>
+ <property
name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"
id="treeview-selection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn"
id="nodeSelectedColumn">
+ <child>
+ <object class="GtkCellRendererToggle"
id="nodeSelectedRenderer">
+ <property
name="radio">True</property>
+ <signal name="toggled"
handler="on_row_toggled" swapped="no"/>
+ </object>
+ <attributes>
+ <attribute
name="active">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn"
id="nodeNameColumn">
+ <property name="title"
translatable="yes">Node Name</property>
+ <child>
+ <object class="GtkCellRendererText"
id="nodeNameRenderer"/>
+ <attributes>
+ <attribute
name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">2</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook"
id="loginNotebook">
+ <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="grid1">
+ <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="loginAuthTypeLabel">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">_Node login authentication type:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">loginAuthTypeCombo</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="GtkComboBoxText"
id="loginAuthTypeCombo">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="entry_text_column">0</property>
+ <property
name="id_column">1</property>
+ <items>
+ <item translatable="yes">No credentials
(discovery authentication disabled)</item>
+ <item translatable="yes">CHAP
pair</item>
+ <item translatable="yes">CHAP pair and
a reverse pair</item>
+ <item translatable="yes">Use the
credentials from discovery</item>
+ </items>
+ <signal name="changed"
handler="on_discovered_type_changed" swapped="no"/>
+ </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="GtkBox"
id="loginBox">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="spacing">6</property>
+ <child>
+ <object class="GtkButton"
id="loginButton">
+ <property name="label"
translatable="yes">_Log In</property>
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="receives_default">True</property>
+ <property
name="use_underline">True</property>
+ <signal name="clicked"
handler="on_login_clicked" swapped="no"/>
+ </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="GtkSpinner"
id="loginSpinner">
+ <property
name="can_focus">False</property>
+ <property
name="no_show_all">True</property>
+ <property
name="active">True</property>
+ </object>
+ <packing>
+ <property
name="expand">False</property>
+ <property
name="fill">True</property>
+ <property
name="pack_type">end</property>
+ <property
name="position">1</property>
+ </packing>
+ </child>
+ </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="GtkNotebook"
id="loginAuthNotebook">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="show_tabs">False</property>
+ <property
name="show_border">False</property>
+ <child>
+ <object class="GtkLabel"
id="noCredentialsFiller">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ </object>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkGrid"
id="loginChapGrid">
+ <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="label15">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">CHAP _Username:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">loginChapUsernameEntry</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="label16">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">CHAP _Password:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">loginChapPasswordEntry</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="loginChapUsernameEntry">
+ <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">0</property>
+ <property
name="width">1</property>
+ <property
name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry"
id="loginChapPasswordEntry">
+ <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">1</property>
+ <property
name="width">1</property>
+ <property
name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property
name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkGrid"
id="loginRchapGrid">
+ <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="GtkEntry"
id="loginRchapUsernameEntry">
+ <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">0</property>
+ <property
name="width">1</property>
+ <property
name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry"
id="loginRchapPasswordEntry">
+ <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">1</property>
+ <property
name="width">1</property>
+ <property
name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry"
id="loginRchapReverseUsername">
+ <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">2</property>
+ <property
name="width">1</property>
+ <property
name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry"
id="loginRchapReversePassword">
+ <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">1</property>
+ <property
name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel"
id="label17">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">CHAP _Username:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">loginRchapUsernameEntry</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="label18">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">CHAP _Password:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">loginRchapPasswordEntry</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="GtkLabel"
id="label19">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">Reverse CHAP User_name:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">loginRchapReverseUsername</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="GtkLabel"
id="label20">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="xalign">0</property>
+ <property name="label"
translatable="yes">Reverse CHAP Pass_word:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">loginRchapReversePassword</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>
+ </object>
+ <packing>
+ <property
name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkLabel"
id="sameCredentialsFiller">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ </object>
+ <packing>
+ <property
name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ </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>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkBox"
id="loginSuccessBox">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="halign">center</property>
+ <property
name="vexpand">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage"
id="image2">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="stock">gtk-apply</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="label21">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property name="label"
translatable="yes"><b>Node login successful.</b>
+Successfully logged in and attached to the selected nodes! Pressing 'OK' will
return you to the previous disk selection screen.</property>
+ <property
name="use_markup">True</property>
+ <property
name="wrap">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="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </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>
+ <object class="GtkListStore" id="nodeStore">
+ <columns>
+ <!-- column-name nodeSelected -->
+ <column type="gboolean"/>
+ <!-- column-name nodeNotLoggedIn -->
+ <column type="gboolean"/>
+ <!-- column-name nodeName -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkTreeModelFilter" id="nodeStoreFiltered">
+ <property name="child_model">nodeStore</property>
+ </object>
+</interface>
diff --git a/pyanaconda/ui/gui/spokes/advstorage/iscsi.py
b/pyanaconda/ui/gui/spokes/advstorage/iscsi.py
new file mode 100644
index 0000000..f3d469e
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/advstorage/iscsi.py
@@ -0,0 +1,340 @@
+# iSCSI 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): Chris Lumens <clumens(a)redhat.com>
+#
+
+from IPy import IP
+from collections import namedtuple
+from gi.repository import GLib, Gtk
+
+from pyanaconda import constants
+from pyanaconda.threads import threadMgr, AnacondaThread
+from pyanaconda.ui.gui import GUIObject
+
+import gettext
+_ = lambda x: gettext.ldgettext("anaconda", x)
+
+__all__ = ["ISCSIDialog"]
+
+STYLE_NONE = 0
+STYLE_CHAP = 1
+STYLE_REVERSE_CHAP = 2
+
+Credentials = namedtuple("Credentials", ["style",
+ "targetIP", "initiator",
"username",
+ "password", "rUsername",
"rPassword"])
+
+NodeStoreRow = namedtuple("NodeStoreRow", ["selected",
"notLoggedIn", "name"])
+
+def login_no_credentials(builder):
+ return Credentials._make([STYLE_NONE,
+ builder.get_object("targetEntry").get_text(),
+ builder.get_object("initiatorEntry").get_text(),
+ "", "", "", ""])
+
+def login_chap(builder):
+ return Credentials._make([STYLE_CHAP,
+ builder.get_object("targetEntry").get_text(),
+ builder.get_object("initiatorEntry").get_text(),
+
builder.get_object("chapUsernameEntry").get_text(),
+
builder.get_object("chapPasswordEntry").get_text(),
+ "", ""])
+
+def login_reverse_chap(builder):
+ return Credentials._make([STYLE_REVERSE_CHAP,
+ builder.get_object("targetEntry").get_text(),
+ builder.get_object("initiatorEntry").get_text(),
+
builder.get_object("rchapUsernameEntry").get_text(),
+
builder.get_object("rchapPasswordEntry").get_text(),
+
builder.get_object("rchapReverseUsername").get_text(),
+
builder.get_object("rchapReversePassword").get_text()])
+
+def login_credentials_valid(credentials):
+ if credentials.style == STYLE_NONE:
+ return True
+ elif credentials.style == STYLE_CHAP:
+ return credentials.username.strip() != "" and credentials.password !=
""
+ elif credentials.style == STYLE_REVERSE_CHAP:
+ return credentials.username.strip() != "" and credentials.password !=
"" and \
+ credentials.rUsername.strip() != "" and credentials.rPassword !=
""
+
+# This list maps the current page from the authNotebook to a function to grab
+# credentials out of the UI. This works as long as authNotebook keeps the
+# filler page at the front.
+loginMap = [login_no_credentials, login_chap, login_reverse_chap]
+
+class ISCSIDialog(GUIObject):
+ builderObjects = ["iscsiDialog", "nodeStore",
"nodeStoreFiltered"]
+ mainWidgetName = "iscsiDialog"
+ uiFile = "spokes/advstorage/iscsi.glade"
+
+ def __init__(self, data, storage):
+ GUIObject.__init__(self, data)
+ self.storage = storage
+ self.iscsi = self.storage.iscsi()
+
+ self._discoveryError = False
+ self._loginError = False
+
+ self._discoveredNodes = []
+
+ def refresh(self):
+ self._authTypeCombo = self.builder.get_object("authTypeCombo")
+ self._authNotebook = self.builder.get_object("authNotebook")
+ self._iscsiNotebook = self.builder.get_object("iscsiNotebook")
+
+ self._loginButton = self.builder.get_object("loginButton")
+ self._loginAuthTypeCombo =
self.builder.get_object("loginAuthTypeCombo")
+ self._loginAuthNotebook = self.builder.get_object("loginAuthNotebook")
+
+ 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._store = self.builder.get_object("nodeStore")
+
+ self._authTypeCombo.set_active(0)
+ self._startButton.set_sensitive(True)
+
+ self._loginAuthTypeCombo.set_active(0)
+
+ self.builder.get_object("nodeStoreFiltered").set_visible_column(1)
+
+ initiatorEntry = self.builder.get_object("initiatorEntry")
+ initiatorEntry.set_text(self.iscsi.initiator)
+ initiatorEntry.set_sensitive(self.iscsi.initiatorSet)
+
+ @property
+ def selectedNames(self):
+ return [itr[2] for itr in self._store if itr[0]]
+
+ def run(self):
+ rc = self.window.run()
+ self.window.destroy()
+ return rc
+
+ ##
+ ## DISCOVERY
+ ##
+
+ def on_auth_type_changed(self, widget, *args):
+ self._authNotebook.set_current_page(widget.get_active())
+
+ # When we change the notebook, we also need to reverify the credentials
+ # in order to set the Start button sensitivity.
+ self.on_login_field_changed()
+
+ def _discover(self, credentials):
+ # This needs to be in its own thread, not marked with gtk_action_* because
it's
+ # called from on_start_clicked, which is in the GTK main loop. Those decorators
+ # won't do anything special in that case.
+ if not self.iscsi.initiatorSet:
+ self.iscsi.initiator = credentials.initiator
+
+ try:
+ self._discoveredNodes = self.iscsi.discover(credentials.targetIP,
+ username=credentials.username,
+ password=credentials.password,
+
r_username=credentials.rUsername,
+
r_password=credentials.rPassword)
+ except IOError as e:
+ self._discoveryError = True
+
+ if len(self._discoveredNodes) == 0:
+ self._discoveryError = True
+
+ def _check_discover(self, *args):
+ if threadMgr.get(constants.THREAD_ISCSI_DISCOVER):
+ return True
+
+ # When iscsi discovery is done, update the UI. We don't need to worry
+ # about the user escaping from the dialog because all the buttons are
+ # marked insensitive.
+ spinner = self.builder.get_object("waitSpinner")
+ spinner.stop()
+
+ if self._discoveryError:
+ # Failure. Display some error message and leave the user on the
+ # dialog to try again.
+ self._discoveryError = False
+ self._conditionNotebook.set_current_page(2)
+ else:
+ # Success. Now populate the node store and kick the user on over to
+ # that subscreen.
+ self._add_nodes(self._discoveredNodes)
+ self._iscsiNotebook.set_current_page(1)
+ self._okButton.set_sensitive(True)
+
+ # We always want to enable this button, in case the user's had enough.
+ self._cancelButton.set_sensitive(True)
+ return False
+
+ def on_start_clicked(self, *args):
+ # First, update some widgets to not be usable while discovery happens.
+ self._startButton.hide()
+ self._cancelButton.set_sensitive(False)
+ self._okButton.set_sensitive(False)
+
+ self._conditionNotebook.set_current_page(1)
+
+ # Mark all children of the configureGrid as insensitive, except for the
+ # wait box.
+ for child in self._configureGrid.get_children():
+ if child != self._conditionNotebook:
+ child.set_sensitive(False)
+
+ # Now get the login credentials.
+ credentials = loginMap[self._authNotebook.get_current_page()](self.builder)
+
+ # FIXME
+ # Verify credentials, setting the UI if required.
+
+ discoveredLabel = self.builder.get_object("discoveredLabel")
+ discoveredLabel.set_markup(discoveredLabel.get_label() %
{"initiatorName": credentials.initiator,
+
"targetAddress": credentials.targetIP})
+
+ spinner = self.builder.get_object("waitSpinner")
+ spinner.start()
+
+ threadMgr.add(AnacondaThread(name=constants.THREAD_ISCSI_DISCOVER,
target=self._discover,
+ args=(credentials,)))
+ GLib.timeout_add(250, self._check_discover)
+
+ # When the initiator name, ip address, and any auth fields are filled in
+ # valid, only then should the Start button be made sensitive.
+ def _target_ip_valid(self):
+ widget = self.builder.get_object("targetEntry")
+ text = widget.get_text()
+
+ try:
+ ip = IP(text)
+ return True
+ except ValueError:
+ return False
+
+ def _initiator_name_valid(self):
+ widget = self.builder.get_object("initiatorEntry")
+ text = widget.get_text()
+
+ stripped = text.strip()
+ return "." in stripped and ":" in stripped
+
+ def on_login_field_changed(self, *args):
+ # Make up a credentials object so we can test if it's valid.
+ credentials = loginMap[self._authNotebook.get_current_page()](self.builder)
+ sensitive = self._target_ip_valid() and self._initiator_name_valid() and
login_credentials_valid(credentials)
+ self._startButton.set_sensitive(sensitive)
+
+ ##
+ ## LOGGING IN
+ ##
+
+ def _add_nodes(self, nodes):
+ for node in nodes:
+ self._store.append([False, True, node.name])
+
+ # We should select the first node by default.
+ self._store[0][0] = True
+
+ def on_discovered_type_changed(self, widget, *args):
+ self._loginAuthNotebook.set_current_page(widget.get_active())
+
+ def on_row_toggled(self, button, path):
+ if not path:
+ return
+
+ # First, mark all rows as unselected.
+ for row in self._store:
+ row[0] = False
+
+ # Then, go back and mark just this row as selected.
+ itr = self._store.get_iter(path)
+ self._store[itr][0] = True
+
+ def _login(self):
+ for row in self._store:
+ obj = NodeStoreRow._make(row)
+
+ if not obj.selected:
+ continue
+
+ for node in self._discoveredNodes:
+ if obj.notLoggedIn and node.name == obj.name:
+ (rc, msg) = self.iscsi.log_into_node(node)
+ if not rc:
+ print msg
+ return
+
+ row[1] = False
+
+ # We need to call this to get the device node to show up
+ # in our devicetree.
+ self.storage.devicetree.populate()
+
+ # Only logging into one at a time.
+ break
+
+ def _check_login(self, *args):
+ if threadMgr.get(constants.THREAD_ISCSI_LOGIN):
+ return True
+
+ spinner = self.builder.get_object("loginSpinner")
+ spinner.stop()
+ spinner.hide()
+
+ if self._loginError:
+ self._loginError = False
+ self._cancelButton.set_sensitive(True)
+ self._loginButton.set_sensitive(True)
+ else:
+ # Select the now-first target for the user in case they want to
+ # log into another one.
+ for row in self._store:
+ if row[1]:
+ row[0] = True
+
+ # And make the login button sensitive if there are any more
+ # nodes to login to.
+ self._loginButton.set_sensitive(True)
+ break
+
+ self._okButton.set_sensitive(True)
+
+ # Once a node has been logged into, it doesn't make much sense to let
+ # the user cancel. Cancel what, exactly?
+ self._cancelButton.set_sensitive(False)
+
+ return False
+
+ def on_login_clicked(self, *args):
+ # Make the buttons unusable while we work.
+ self._okButton.set_sensitive(False)
+ self._cancelButton.set_sensitive(False)
+ self._loginButton.set_sensitive(False)
+
+ spinner = self.builder.get_object("loginSpinner")
+ spinner.start()
+ spinner.set_visible(True)
+ spinner.show()
+
+ threadMgr.add(AnacondaThread(name=constants.THREAD_ISCSI_LOGIN,
target=self._login))
+ GLib.timeout_add(250, self._check_login)
diff --git a/pyanaconda/ui/gui/spokes/filter.glade
b/pyanaconda/ui/gui/spokes/filter.glade
index cde4c80..57a9a87 100644
--- a/pyanaconda/ui/gui/spokes/filter.glade
+++ b/pyanaconda/ui/gui/spokes/filter.glade
@@ -917,17 +917,338 @@
</packing>
</child>
<child>
- <object class="GtkLabel" id="label8">
+ <object class="GtkGrid" id="otherGrid">
<property name="visible">True</property>
<property
name="can_focus">False</property>
- <property name="label"
translatable="yes">Nothing here yet.</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="margin_top">12</property>
+ <property name="row_spacing">6</property>
+ <property
name="column_spacing">6</property>
+ <child>
+ <object class="GtkLabel"
id="label3">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label"
translatable="yes">_Filter By:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">multipathTypeCombo</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="GtkComboBoxText"
id="otherTypeCombo">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="entry_text_column">0</property>
+ <property
name="id_column">1</property>
+ <items>
+ <item translatable="yes">None</item>
+ <item
translatable="yes">Vendor</item>
+ <item
translatable="yes">Interconnect</item>
+ <item translatable="yes">ID</item>
+ </items>
+ <signal name="changed"
handler="on_other_type_combo_changed" swapped="no"/>
+ </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="GtkNotebook"
id="otherTypeNotebook">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="no_show_all">True</property>
+ <property
name="hexpand">True</property>
+ <property
name="show_tabs">False</property>
+ <child>
+ <object class="GtkLabel"
id="label8">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="hexpand">True</property>
+ </object>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkBox"
id="otherVendorBox">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="spacing">6</property>
+ <child>
+ <object class="GtkLabel"
id="label12">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property name="label"
translatable="yes">_Show Only Devices From:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">otherVendorCombo</property>
+ </object>
+ <packing>
+ <property
name="expand">False</property>
+ <property
name="fill">True</property>
+ <property
name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText"
id="otherVendorCombo">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="entry_text_column">0</property>
+ <property
name="id_column">1</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="GtkBox"
id="otherInterconnectBox">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="spacing">6</property>
+ <child>
+ <object class="GtkLabel"
id="label20">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property name="label"
translatable="yes">_Show Only Devices With:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">otherInterconnectCombo</property>
+ </object>
+ <packing>
+ <property
name="expand">False</property>
+ <property
name="fill">True</property>
+ <property
name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText"
id="otherInterconnectCombo">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="entry_text_column">0</property>
+ <property
name="id_column">1</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">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkBox"
id="otherIDBox">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property
name="spacing">6</property>
+ <child>
+ <object class="GtkLabel"
id="label21">
+ <property
name="visible">True</property>
+ <property
name="can_focus">False</property>
+ <property name="label"
translatable="yes">_Show Only Devices Containing:</property>
+ <property
name="use_underline">True</property>
+ <property
name="mnemonic_widget">otherIDEntry</property>
+ </object>
+ <packing>
+ <property
name="expand">False</property>
+ <property
name="fill">True</property>
+ <property
name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry"
id="otherIDEntry">
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="invisible_char">●</property>
+ <property
name="width_chars">30</property>
+ <property
name="invisible_char_set">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="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property
name="left_attach">2</property>
+ <property
name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTreeView"
id="otherTreeView">
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="hexpand">True</property>
+ <property
name="vexpand">True</property>
+ <property
name="model">otherModel</property>
+ <property
name="headers_clickable">False</property>
+ <property
name="rules_hint">True</property>
+ <property
name="enable_search">False</property>
+ <property
name="search_column">0</property>
+ <property
name="enable_tree_lines">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection"
id="treeview-selection5"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn"
id="otherSelectedCol">
+ <child>
+ <object class="GtkCellRendererToggle"
id="otherSelectedRenderer">
+ <signal name="toggled"
handler="on_row_toggled" swapped="no"/>
+ </object>
+ <attributes>
+ <attribute
name="sensitive">2</attribute>
+ <attribute
name="visible">0</attribute>
+ <attribute
name="active">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn"
id="otherIDCol">
+ <property name="title"
translatable="yes">Identifier</property>
+ <property
name="clickable">True</property>
+ <property
name="sort_indicator">True</property>
+ <property
name="sort_column_id">10</property>
+ <child>
+ <object class="GtkCellRendererText"
id="otherIDRenderer"/>
+ <attributes>
+ <attribute
name="sensitive">2</attribute>
+ <attribute
name="visible">0</attribute>
+ <attribute
name="text">10</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn"
id="otherCapacityCol">
+ <property name="title"
translatable="yes">Capacity</property>
+ <property
name="clickable">True</property>
+ <property
name="sort_indicator">True</property>
+ <property
name="sort_column_id">6</property>
+ <child>
+ <object class="GtkCellRendererText"
id="otherCapacityRenderer"/>
+ <attributes>
+ <attribute
name="sensitive">2</attribute>
+ <attribute
name="visible">0</attribute>
+ <attribute
name="text">6</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn"
id="otherVendorCol">
+ <property name="title"
translatable="yes">Vendor</property>
+ <property
name="clickable">True</property>
+ <property
name="sort_indicator">True</property>
+ <property
name="sort_column_id">7</property>
+ <child>
+ <object class="GtkCellRendererText"
id="otherVendorRenderer"/>
+ <attributes>
+ <attribute
name="yalign">2</attribute>
+ <attribute
name="sensitive">1</attribute>
+ <attribute
name="visible">0</attribute>
+ <attribute
name="text">7</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn"
id="otherInterconnectCol">
+ <property name="title"
translatable="yes">Interconnect</property>
+ <property
name="clickable">True</property>
+ <property
name="sort_indicator">True</property>
+ <property
name="sort_column_id">8</property>
+ <child>
+ <object class="GtkCellRendererText"
id="otherInterconnectRenderer"/>
+ <attributes>
+ <attribute
name="sensitive">2</attribute>
+ <attribute
name="visible">0</attribute>
+ <attribute
name="text">8</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property
name="left_attach">0</property>
+ <property
name="top_attach">1</property>
+ <property name="width">5</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton"
id="otherClearButton">
+ <property
name="label">gtk-clear</property>
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="receives_default">True</property>
+ <property
name="use_stock">True</property>
+ <signal name="clicked"
handler="on_clear_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property
name="left_attach">4</property>
+ <property
name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton"
id="otherFindButton">
+ <property
name="label">gtk-find</property>
+ <property
name="visible">True</property>
+ <property
name="can_focus">True</property>
+ <property
name="receives_default">True</property>
+ <property
name="use_stock">True</property>
+ <signal name="clicked"
handler="on_find_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property
name="left_attach">3</property>
+ <property
name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child type="tab">
- <object class="GtkLabel" id="label3">
+ <object class="GtkLabel"
id="otherLabel">
<property name="visible">True</property>
<property
name="can_focus">False</property>
<property name="label"
translatable="yes">_Other SAN Devices</property>
diff --git a/pyanaconda/ui/gui/spokes/filter.py b/pyanaconda/ui/gui/spokes/filter.py
index 80ea03e..a934c47 100644
--- a/pyanaconda/ui/gui/spokes/filter.py
+++ b/pyanaconda/ui/gui/spokes/filter.py
@@ -35,6 +35,7 @@ from pyanaconda.flags import flags
from pyanaconda.ui.lib.disks import getDisks, isLocalDisk, size_str
from pyanaconda.ui.gui.utils import enlightbox
from pyanaconda.ui.gui.spokes import NormalSpoke
+from pyanaconda.ui.gui.spokes.advstorage.iscsi import ISCSIDialog
from pyanaconda.ui.gui.spokes.lib.cart import SelectedDisksDialog
from pyanaconda.ui.gui.categories.storage import StorageCategory
@@ -230,6 +231,51 @@ class OtherPage(FilterPage):
def ismember(self, device):
return isinstance(device, iScsiDiskDevice) or isinstance(device, FcoeDiskDevice)
+ def _long_identifier(self, disk):
+ # For iSCSI devices, we want the long ip-address:port-iscsi-tgtname-lun-XX
+ # identifier, but blivet doesn't expose that in any useful way and I
don't
+ # want to go asking udev. Instead, we dig around in the deviceLinks and
+ # default to the name if we can't figure anything else out.
+ for link in disk.deviceLinks:
+ if "by-path" in link:
+ lastSlash = link.rindex("/")+1
+ return link[lastSlash:]
+
+ return disk.name
+
+ def setup(self, store, selectedNames, disks):
+ vendors = []
+ interconnects = []
+
+ for disk in disks:
+ selected = disk.name in selectedNames
+
+ if hasattr(disk, "node"):
+ port = str(disk.node.port)
+ lun = str(disk.node.tpgt)
+ else:
+ port = ""
+ lun = ""
+
+ store.append([True, selected, not disk.protected,
+ disk.name, "", disk.model, size_str(disk.size),
+ disk.vendor, disk.bus, disk.serial,
+ self._long_identifier(disk), "", port, getattr(disk,
"initiator", ""),
+ lun, ""])
+
+ if not disk.vendor in vendors:
+ vendors.append(disk.vendor)
+
+ if not disk.bus in interconnects:
+ interconnects.append(disk.bus)
+
+ self._combo = self.builder.get_object("otherTypeCombo")
+ self._combo.set_active(0)
+ self._combo.emit("changed")
+
+ self.setupCombo(self.builder.get_object("otherVendorCombo"), vendors)
+ self.setupCombo(self.builder.get_object("otherInterconnectCombo"),
interconnects)
+
def visible_func(self, model, itr, *args):
obj = DiskStoreRow._make(model[itr])
device = self.storage.devicetree.getDeviceByName(obj.name)
@@ -311,6 +357,8 @@ class FilterSpoke(NormalSpoke):
self._addAdditionalCombo =
self.builder.get_object("addAdditionalCombo")
self._notebook = self.builder.get_object("advancedNotebook")
+ self._addAdditionalCombo.set_active(0)
+
if not arch.isS390():
self._notebook.remove_page(-1)
self._addAdditionalCombo.remove(3)
@@ -426,8 +474,29 @@ class FilterSpoke(NormalSpoke):
self._update_summary()
- def on_add_additional_changed(self, *args):
- pass
+ def on_add_additional_changed(self, widget, *args):
+ active = widget.get_active()
+
+ if active == 0:
+ # Nothing.
+ return
+ elif active == 1:
+ # iSCSI
+ dialog = ISCSIDialog(self.data, self.storage)
+
+ with enlightbox(self.window, dialog.window):
+ dialog.refresh()
+ dialog.run()
+ elif active == 2:
+ # FCoE
+ pass
+ elif active == 3:
+ # ZFCP
+ pass
+
+ # We now need to refresh so any new disks picked up by adding advanced
+ # storage are displayed in the UI.
+ self.refresh()
##
## SEARCH TAB SIGNAL HANDLERS
@@ -456,3 +525,17 @@ class FilterSpoke(NormalSpoke):
findButton.set_sensitive(ndx != 0)
clearButton.set_sensitive(ndx != 0)
notebook.set_current_page(ndx)
+
+ ##
+ ## OTHER TAB SIGNAL HANDLERS
+ ##
+ def on_other_type_combo_changed(self, combo):
+ ndx = combo.get_active()
+
+ notebook = self.builder.get_object("otherTypeNotebook")
+ findButton = self.builder.get_object("otherFindButton")
+ clearButton = self.builder.get_object("otherClearButton")
+
+ findButton.set_sensitive(ndx != 0)
+ clearButton.set_sensitive(ndx != 0)
+ notebook.set_current_page(ndx)
diff --git a/pyanaconda/ui/lib/disks.py b/pyanaconda/ui/lib/disks.py
index ba31eac..ec352f5 100644
--- a/pyanaconda/ui/lib/disks.py
+++ b/pyanaconda/ui/lib/disks.py
@@ -20,7 +20,7 @@
# Chris Lumens <clumens(a)redhat.com>
#
-from blivet.devices import MultipathDevice
+from blivet.devices import MultipathDevice, iScsiDiskDevice
from blivet.size import Size
__all__ = ["FakeDiskLabel", "FakeDisk", "getDisks",
"isLocalDisk", "size_str"]
@@ -65,7 +65,7 @@ def getDisks(devicetree, fake=False):
return disks
def isLocalDisk(disk):
- return not isinstance(disk, MultipathDevice)
+ return not isinstance(disk, MultipathDevice) and not isinstance(disk,
iScsiDiskDevice)
def size_str(mb):
if isinstance(mb, Size):
--
1.8.1.2