summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--iscsi.py113
-rw-r--r--iw/autopart_type.py45
-rw-r--r--textw/partition_text.py37
-rw-r--r--ui/iscsi-config.glade135
4 files changed, 302 insertions, 28 deletions
diff --git a/iscsi.py b/iscsi.py
index b9f28516e..48060d7e9 100644
--- a/iscsi.py
+++ b/iscsi.py
@@ -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">&lt;b&gt;_Username:&lt;/b&gt;</property>
+ <property name="label" translatable="yes">&lt;b&gt;CHAP _Username:&lt;/b&gt;</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">&lt;b&gt;_Password:&lt;/b&gt;</property>
+ <property name="label" translatable="yes">&lt;b&gt;CHAP _Password:&lt;/b&gt;</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">&lt;b&gt;Reverse CHAP U_sername:&lt;/b&gt;</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">&lt;b&gt;Reverse CHAP P_assword:&lt;/b&gt;</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>