diff options
-rw-r--r-- | iscsi.py | 113 | ||||
-rw-r--r-- | iw/autopart_type.py | 45 | ||||
-rw-r--r-- | textw/partition_text.py | 37 | ||||
-rw-r--r-- | ui/iscsi-config.glade | 135 |
4 files changed, 302 insertions, 28 deletions
@@ -20,6 +20,7 @@ from constants import * import os +import errno import string import signal import iutil @@ -40,6 +41,7 @@ ISCSID="" global ISCSIADM ISCSIADM = "" INITIATOR_FILE="/etc/iscsi/initiatorname.iscsi" +ISCSID_CONF="/etc/iscsi/iscsid.conf" def find_iscsi_files(): global ISCSID @@ -69,7 +71,8 @@ def has_iscsi(): return True class iscsiTarget: - def __init__(self, ipaddr, port = None, user = None, pw = None): + def __init__(self, ipaddr, port=None, user=None, pw=None, + user_in=None, pw_in=None): # FIXME: validate ipaddr self.ipaddr = ipaddr if not port: # FIXME: hack hack hack @@ -77,6 +80,8 @@ class iscsiTarget: self.port = str(port) self.user = user self.password = pw + self.user_in = user_in + self.password_in = pw_in self._portal = None self._nodes = [] @@ -238,6 +243,24 @@ class iscsi(object): log.debug("queryFirmware: ISCSIADM is %s" % (ISCSIADM,)) result = iutil.execWithCapture(ISCSIADM, argv) result = result.strip() + + if len(result) == 0 \ + or result[0].find("iscsiadm -") != -1 \ + or result[0].find("iscsiadm: ") != -1: + log.debug("queryFirmware: iscsiadm %s returns bad output: %s" % + (argv,result)) + + # Try querying the node records instead + argv = [ "-m", "node", "-o", "show", "-S" ] + result = iutil.execWithCapture(ISCSIADM, argv) + + if len(result) == 0 \ + or result[0].find("iscsiadm -") != -1 \ + or result[0].find("iscsiadm: ") != -1: + log.debug("queryFirmware: iscsiadm %s returns bad output: %s" % + (argv,result)) + return retval + for line in result.split("\n"): SPLIT = " = " idx = line.find(SPLIT) @@ -292,12 +315,12 @@ class iscsi(object): def loginToDefaultDrive(self): # Example: - # [root@elm3b87 ~]# iscsiadm -m fw -l + # [root@elm3b87 ~]# iscsiadm -m discovery -t fw -l # Logging in to [iface: default, target: iqn.1992-08.com.netapp:sn.84183797, portal: 9.47.67.152,3260] find_iscsi_files() - argv = [ "-m", "fw", "-l" ] + argv = [ "-m", "discovery", "-t", "fw", "-l" ] result = iutil.execWithCapture(ISCSIADM, argv) TARGET = "target: " @@ -308,6 +331,8 @@ class iscsi(object): idxEnd = result.find(END) if idxTarget == -1 or idxPortal == -1 or idxEnd == -1: + log.warn("could not find markers. iscsiadm returned %s" % + (result,)) return None target = result[idxTarget + len(TARGET) : idxPortal] @@ -379,15 +404,87 @@ class iscsi(object): if intf: w.pop() - def addTarget(self, ipaddr, port = "3260", user = None, pw = None, - intf = None): + def addTarget(self, ipaddr, port="3260", user=None, pw=None, + user_in=None, pw_in=None, intf=None): if not self.iscsidStarted: self.startup(intf) if not self.iscsidStarted: # can't start for some reason.... just fallback I guess return - t = iscsiTarget(ipaddr, port, user, pw) + commentUser = '#' + commentUser_in = '#' + + if user is not None or password is not None: + commentUser = '' + if user is None: + raise ValueError, "user is required" + if pw is None: + raise ValueError, "pw is required" + + if user_in is not None or pw_in is not None: + commentUser_in = '' + if user_in is None: + raise ValueError, "user_in is required" + if pw_in is None: + raise ValueError, "pw_in is required" + + # If either a user/pw pair was specified or a user_in/pw_in was + # specified, then CHAP is specified. + if commentUser == '' or commentUser_in == '': + commentChap = '' + else: + commentChap = '#' + + + oldIscsidFile = [] + try: + f = open(ISCSID_CONF, "r") + oldIscsidFile = f.readlines() + f.close() + except IOError, x: + if x.errno != errno.ENOENT: + raise RuntimeError, "Cannot open %s for read." % (ISCSID_CONF,) + + try: + f = open(ISCSID_CONF, "w") + except: + raise RuntimeError, "Cannot open %s for write." % (ISCSID_CONF,) + + vals = { + "node.session.auth.authmethod = ": [commentChap, "CHAP"], + "node.session.auth.username = ": [commentUser, user], + "node.session.auth.password = ": [commentUser, pw], + "node.session.auth.username_in = ": [commentUser_in, user_in], + "node.session.auth.password_in = ": [commentUser_in, pw_in], + "discovery.sendtargets.auth.authmethod = ": [commentChap, "CHAP"], + "discovery.sendtargets.auth.username = ": [commentUser, user], + "discovery.sendtargets.auth.password = ": [commentUser, pw], + "discovery.sendtargets.auth.username_in = ": + [commentUser_in, user_in], + "discovery.sendtargets.auth.password_in = ": + [commentUser_in, pw_in], + } + + for line in oldIscsidFile: + s = line.strip() + # grab the cr/lf/cr+lf + nl = line[line.find(s)+len(s):] + found = False + for (k, (c, v)) in vals.items(): + if line.find(k) != -1: + f.write("%s%s%s%s" % (c, k, v, nl)) + found=True + del vals[k] + break + if not found: + f.write(line) + + for (k, (c, v)) in vals.items(): + f.write("%s%s%s\n" % (c, k, v)) + f.close () + + t = iscsiTarget(ipaddr, port, user, pw, user_in, pw_in) if not t.discover(): return if not t.login(): @@ -405,6 +502,10 @@ class iscsi(object): f.write(" --user %s" %(t.user,)) if t.password: f.write(" --password %s" %(t.password,)) + if t.user_in: + f.write(" --reverse-user %s" % (t.user_in,)) + if t.password_in: + f.write(" --reverse-password %s" % (t.password_in,)) f.write("\n") def write(self, instPath): diff --git a/iw/autopart_type.py b/iw/autopart_type.py index 902497fc3..6fa900d64 100644 --- a/iw/autopart_type.py +++ b/iw/autopart_type.py @@ -236,11 +236,8 @@ class PartitionTypeWindow(InstallWindow): dialog.show_all() sg = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) map(lambda x: sg.add_widget(dxml.get_widget(x)), - ("iscsiAddrEntry", "iscsiInitiatorEntry")) - - # we don't currently support username or password... - map(lambda x: dxml.get_widget(x).hide(), - ("userLabel", "passLabel", "userEntry", "passEntry")) + ("iscsiAddrEntry", "iscsiInitiatorEntry", "userEntry", "passEntry", + "userinEntry", "passinEntry")) # get the initiator name if it exists and don't allow changing # once set @@ -261,11 +258,44 @@ class PartitionTypeWindow(InstallWindow): self.intf.messageWindow(_("Invalid Initiator Name"), _("You must provide an initiator name.")) continue + self.anaconda.id.iscsi.initiator = initiator target = dxml.get_widget("iscsiAddrEntry").get_text().strip() user = dxml.get_widget("userEntry").get_text().strip() pw = dxml.get_widget("passEntry").get_text().strip() + user_in = dxml.get_widget("userinEntry").get_text().strip() + pw_in = dxml.get_widget("passinEntry").get_text().strip() + + if len(user) == 0: + user = None + if len(pw) == 0: + pw = None + if len(user_in) == 0: + user_in = None + if len(pw_in) == 0: + pw_in = None + + if user is not None or pw is not None: + if user is None: + self.intf.messageWindow(_("Missing value"), + _("CHAP username is required if CHAP password is defined.")) + continue + if pw is None: + self.intf.messageWindow(_("Missing value"), + _("CHAP password is required if CHAP username is defined.")) + continue + + if user_in is not None or pw_in is not None: + if user_in is None: + self.intf.messageWindow(_("Missing value"), + _("Reverse CHAP username is required if reverse CHAP password is defined.")) + continue + if pw_in is None: + self.intf.messageWindow(_("Missing value"), + _("Reverse CHAP password is required if reverse CHAP username is defined.")) + continue + err = None try: idx = target.rfind(":") @@ -281,10 +311,11 @@ class PartitionTypeWindow(InstallWindow): except network.IPError, msg: err = msg if err: - self.intf.messageWindow(_("Error with Data"), "%s" %(msg,)) + self.intf.messageWindow(_("Error with Data"), "%s" %(err,)) continue - self.anaconda.id.iscsi.addTarget(ip, port, user, pw, self.intf) + self.anaconda.id.iscsi.addTarget(ip, port, user, pw, user_in, pw_in, + self.intf) break dialog.destroy() diff --git a/textw/partition_text.py b/textw/partition_text.py index b09213f02..1f87d1477 100644 --- a/textw/partition_text.py +++ b/textw/partition_text.py @@ -1687,10 +1687,43 @@ class PartitionTypeWindow: _("Configure iSCSI Parameters"), _("To use iSCSI disks, you must provide the address of your iSCSI target and the iSCSI initiator name you've configured for your host."), prompts = [ _("Target IP Address"), - _("iSCSI Initiator Name") ]) + _("iSCSI Initiator Name"), + _("CHAP username"), + _("CHAP password"), + _("Reverse CHAP username"), + _("Reverse CHAP password") ]) if button == TEXT_CANCEL_CHECK: return INSTALL_BACK + (user, pw, user_in, pw_in) = entries[2:5] + + if len(user) == 0: + user = None + if len(pw) == 0: + pw = None + if len(user_in) == 0: + user_in = None + if len(pw_in) == 0: + pw_in = None + + if user is not None or pw is not None: + if user is None: + ButtonChoiceWindow(screen, _("Missing value"), + _("Username is required when password is present.")) + if pw is None: + ButtonChoiceWindow(screen, _("Missing value"), + _("Password is required when username is present.")) + + if user_in is not None or pw_in is not None: + if user_in is None: + ButtonChoiceWindow(screen, _("Missing value"), + _("Reverse username is required when" + "reverse password is present.")) + if pw_in is None: + ButtonChoiceWindow(screen, _("Missing value"), + _("Reverse password is required when" + "reverse username is present.")) + target = entries[0].strip() try: idx = target.rfind(":") @@ -1709,6 +1742,6 @@ class PartitionTypeWindow: iname = entries[1].strip() if not self.anaconda.id.iscsi.initiatorSet: self.anaconda.id.iscsi.initiator = iname - self.anaconda.id.iscsi.addTarget(ip, port) + self.anaconda.id.iscsi.addTarget(ip, port, user, pw, user_in, pw_in) return INSTALL_OK diff --git a/ui/iscsi-config.glade b/ui/iscsi-config.glade index 6b6fa8c4a..f1c80f892 100644 --- a/ui/iscsi-config.glade +++ b/ui/iscsi-config.glade @@ -162,7 +162,7 @@ <widget class="GtkTable" id="iscsiTable"> <property name="border_width">12</property> <property name="visible">True</property> - <property name="n_rows">4</property> + <property name="n_rows">6</property> <property name="n_columns">2</property> <property name="homogeneous">False</property> <property name="row_spacing">6</property> @@ -215,7 +215,7 @@ <property name="right_attach">2</property> <property name="top_attach">0</property> <property name="bottom_attach">1</property> - <property name="x_options"></property> + <property name="x_options">fill</property> <property name="y_options"></property> </packing> </child> @@ -258,16 +258,16 @@ <property name="max_length">0</property> <property name="text" translatable="yes"></property> <property name="has_frame">True</property> - <property name="invisible_char">*</property> + <property name="invisible_char">â¢</property> <property name="activates_default">False</property> - <property name="width_chars">32</property> + <property name="width_chars">45</property> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> <property name="top_attach">1</property> <property name="bottom_attach">2</property> - <property name="x_options"></property> + <property name="x_options">fill</property> <property name="y_options"></property> </packing> </child> @@ -275,7 +275,7 @@ <child> <widget class="GtkLabel" id="userLabel"> <property name="visible">True</property> - <property name="label" translatable="yes"><b>_Username:</b></property> + <property name="label" translatable="yes"><b>CHAP _Username:</b></property> <property name="use_underline">True</property> <property name="use_markup">True</property> <property name="justify">GTK_JUSTIFY_LEFT</property> @@ -302,9 +302,32 @@ </child> <child> + <widget class="GtkEntry" id="userEntry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">â¢</property> + <property name="activates_default">False</property> + <property name="width_chars">45</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> <widget class="GtkLabel" id="passLabel"> <property name="visible">True</property> - <property name="label" translatable="yes"><b>_Password:</b></property> + <property name="label" translatable="yes"><b>CHAP _Password:</b></property> <property name="use_underline">True</property> <property name="use_markup">True</property> <property name="justify">GTK_JUSTIFY_LEFT</property> @@ -331,7 +354,59 @@ </child> <child> - <widget class="GtkEntry" id="userEntry"> + <widget class="GtkEntry" id="passEntry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">False</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">â¢</property> + <property name="activates_default">False</property> + <property name="width_chars">45</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="userinLabel"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Reverse CHAP U_sername:</b></property> + <property name="use_underline">True</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">userinEntry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="userinEntry"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="editable">True</property> @@ -341,18 +416,49 @@ <property name="has_frame">True</property> <property name="invisible_char">•</property> <property name="activates_default">False</property> + <property name="width_chars">45</property> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="x_options">fill</property> <property name="y_options"></property> </packing> </child> <child> - <widget class="GtkEntry" id="passEntry"> + <widget class="GtkLabel" id="passinLabel"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Reverse CHAP P_assword:</b></property> + <property name="use_underline">True</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">passinEntry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="passinEntry"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="editable">True</property> @@ -362,15 +468,18 @@ <property name="has_frame">True</property> <property name="invisible_char">•</property> <property name="activates_default">False</property> + <property name="width_chars">45</property> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> + <property name="x_options">fill</property> <property name="y_options"></property> </packing> </child> + </widget> <packing> <property name="padding">0</property> |