This patch adds support for using FCoE during the installation. This patch merely lays the initial ground work, there is more work todo:
- The system will not boot without manual help after the install, as dracut / mkinitrd do not support FCoE yet
- If FCoE is not used for / but for example for /srv, then information about the nic used for FCoE needs to be written in a to be defined config file in the system, and rc.sysinit needs to be thought to read this file and bring up FCoE SAN's / Fabrics not used for /
- kickstart support for FCoE still needs to be done --- iw/autopart_type.py | 84 ++++++++++++++++++++++++++- storage/__init__.py | 5 ++ storage/fcoe.py | 92 +++++++++++++++++++++++++++++ textw/partition_text.py | 29 +++++++++- ui/adddrive.glade | 19 ++++++ ui/fcoe-config.glade | 149 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 storage/fcoe.py create mode 100644 ui/fcoe-config.glade
diff --git a/iw/autopart_type.py b/iw/autopart_type.py index 8a12689..9c04e52 100644 --- a/iw/autopart_type.py +++ b/iw/autopart_type.py @@ -32,6 +32,7 @@ from iw_gui import * from flags import flags import network from storage import iscsi +from storage import fcoe from storage.deviceaction import *
import gettext @@ -325,6 +326,81 @@ class PartitionTypeWindow(InstallWindow): return rc
+ def addFcoeDrive(self): + (dxml, dialog) = gui.getGladeWidget("fcoe-config.glade", + "fcoeDialog") + + # Populate the combo + combo = dxml.get_widget("fcoeNicCombo") + cell = gtk.CellRendererText() + combo.pack_start(cell, True) + combo.set_attributes(cell, text = 0) + cell.set_property("wrap-width", 525) + combo.set_size_request(480, -1) + store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING) + combo.set_model(store) + + netdevs = self.anaconda.id.network.available() + keys = netdevs.keys() + keys.sort() + selected_interface = None + for dev in keys: + # Skip NIC's which are connected (iow in use for a net install) + if dev in network.getActiveNetDevs(): + continue + + i = store.append(None) + + desc = netdevs[dev].get("DESC") + if desc: + desc = "%s - %s" %(dev, desc) + else: + desc = "%s" %(dev,) + + mac = netdevs[dev].get("HWADDR") + if mac: + desc = "%s - %s" %(desc, mac) + + if selected_interface is None: + selected_interface = i + + store[i] = (desc, dev) + + if selected_interface: + combo.set_active_iter(selected_interface) + else: + combo.set_active(0) + + # Show the dialog + gui.addFrame(dialog) + dialog.show_all() + sg = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) + sg.add_widget(dxml.get_widget("fcoeNicCombo")) + + while 1: + rc = dialog.run() + + if rc == gtk.RESPONSE_CANCEL: + break; + + iter = combo.get_active_iter() + if iter is None: + self.intf.messageWindow(_("Error"), + "Must select a NIC to use.", + type="warning", custom_icon="error") + continue; + + try: + self.storage.fcoe.addSan(store.get_value(iter, 1), self.intf) + except IOError, e: + self.intf.messageWindow(_("Error"), str(e)) + rc = gtk.RESPONSE_CANCEL + + break + + dialog.destroy() + return rc + def addZfcpDrive(self): (dxml, dialog) = gui.getGladeWidget("zfcp-config.glade", "zfcpDialog") @@ -366,6 +442,10 @@ class PartitionTypeWindow(InstallWindow): dxml.get_widget("iscsiRadio").set_sensitive(False) dxml.get_widget("iscsiRadio").set_active(False)
+ if not fcoe.has_fcoe(): + dxml.get_widget("fcoeRadio").set_sensitive(False) + dxml.get_widget("fcoeRadio").set_active(False) + #figure out what advanced devices we have available and set sensible default group = dxml.get_widget("iscsiRadio").get_group() for button in group: @@ -379,6 +459,8 @@ class PartitionTypeWindow(InstallWindow): return if dxml.get_widget("iscsiRadio").get_active() and iscsi.has_iscsi(): rc = self.addIscsiDrive() + elif dxml.get_widget("fcoeRadio").get_active() and fcoe.has_fcoe(): + rc = self.addFcoeDrive() elif dxml.get_widget("zfcpRadio") is not None and dxml.get_widget("zfcpRadio").get_active(): rc = self.addZfcpDrive() dialog.destroy() @@ -498,7 +580,7 @@ class PartitionTypeWindow(InstallWindow): self.xml.get_widget("bootDriveCombo").set_sensitive(False) self.xml.get_widget("encryptButton").set_sensitive(False)
- if not iutil.isS390() and not iscsi.has_iscsi(): + if not iutil.isS390() and not iscsi.has_iscsi() and not fcoe.has_fcoe(): self.xml.get_widget("addButton").set_sensitive(False)
sigs = { "on_partitionTypeCombo_changed": self.comboChanged, diff --git a/storage/__init__.py b/storage/__init__.py index 623eff1..6fdb086 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -46,6 +46,7 @@ from devicelibs.lvm import safeLvmName from devicelibs.dm import name_from_dm_node from udev import * import iscsi +import fcoe import zfcp
import gettext @@ -204,6 +205,7 @@ class Storage(object): self.__luksDevs = {}
self.iscsi = iscsi.iscsi() + self.fcoe = fcoe.fcoe() self.zfcp = zfcp.ZFCP()
self._nextID = 0 @@ -269,6 +271,7 @@ class Storage(object): w = self.anaconda.intf.waitWindow(_("Finding Devices"), _("Finding storage devices...")) self.iscsi.startup(self.anaconda.intf) + self.fcoe.startup(self.anaconda.intf) self.zfcp.startup() if self.anaconda.id.getUpgrade(): clearPartType = CLEARPART_TYPE_NONE @@ -911,6 +914,7 @@ class Storage(object): def write(self, instPath): self.fsset.write(instPath) self.iscsi.write(instPath, self.anaconda) + self.fcoe.write(instPath, self.anaconda) self.zfcp.write(instPath)
def writeKS(self, f): @@ -978,6 +982,7 @@ class Storage(object): f.write("\n")
self.iscsi.writeKS(f) + self.fcoe.writeKS(f) self.zfcp.writeKS(f)
diff --git a/storage/fcoe.py b/storage/fcoe.py new file mode 100644 index 0000000..46c5bcb --- /dev/null +++ b/storage/fcoe.py @@ -0,0 +1,92 @@ +# +# fcoe.py - fcoe class +# +# Copyright (C) 2009 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# + +import os +import iutil +import logging +import time +log = logging.getLogger("anaconda") + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +_fcoe_module_loaded = False + +def has_fcoe(): + global _fcoe_module_loaded + if not _fcoe_module_loaded: + iutil.execWithRedirect("modprobe", [ "fcoe" ], + stdout = "/dev/tty5", stderr="/dev/tty5", + searchPath = 1) + _fcoe_module_loaded = True + + return os.access("/sys/module/fcoe", os.X_OK) + +class fcoe(object): + def __init__(self): + self.started = False + self.nics = [] + + def _stabilize(self, intf = None): + if intf: + w = intf.waitWindow(_("Connecting to FCoE SAN"), + _("Connecting to FCoE SAN")) + + # I have no clue how long we need to wait, this ought to do the trick + time.sleep(10) + iutil.execWithRedirect("udevadm", [ "settle" ], + stdout = "/dev/tty5", stderr="/dev/tty5", + searchPath = 1) + if intf: + w.pop() + + def startup(self, intf = None): + if self.started: + return + + if not has_fcoe(): + return + + # Place holder for adding autodetection of FCoE setups based on + # firmware tables (like iBFT for iSCSI) + + self.started = True + + def addSan(self, nic, intf=None): + if not has_fcoe(): + raise IOError, _("FCoE not available") + + log.info("Activating FCoE SAN attached to %s" % nic) + + f = open("/sys/module/fcoe/parameters/create", "w") + f.write(nic) + f.close() + + self._stabilize(intf) + self.nics.append(nic) + + def writeKS(self, f): + # fixme plenty (including add ks support for fcoe in general) + return + + def write(self, instPath, anaconda): + # erm, do we need todo anything here ? + return + +# vim:tw=78:ts=4:et:sw=4 diff --git a/textw/partition_text.py b/textw/partition_text.py index fdf3496..f14a273 100644 --- a/textw/partition_text.py +++ b/textw/partition_text.py @@ -162,6 +162,9 @@ class PartitionTypeWindow: newdrv.append("Add iSCSI target") if iutil.isS390(): newdrv.append( "Add zFCP LUN" ) + from storage import fcoe + if fcoe.has_fcoe(): + newdrv.append("Add FCoE SAN")
if len(newdrv) == 0: return INSTALL_BACK @@ -176,12 +179,18 @@ class PartitionTypeWindow:
if button == TEXT_BACK_CHECK: return INSTALL_BACK - if choice == 1: + if newdrv[choice] == "Add zFCP LUN": try: return self.addZFCPDriveDialog(screen) except ValueError, e: ButtonChoiceWindow(screen, _("Error"), str(e)) return INSTALL_BACK + elif newdrv[choice] == "Add FCoE SAN": + try: + return self.addFcoeDriveDialog(screen) + except ValueError, e: + ButtonChoiceWindow(screen, _("Error"), str(e)) + return INSTALL_BACK else: try: return self.addIscsiDriveDialog(screen) @@ -206,6 +215,24 @@ class PartitionTypeWindow:
return INSTALL_OK
+ def addFcoeDriveDialog(self, screen): + (button, entries) = EntryWindow(screen, + _("Add FCoE SAN"), + _("Enter the device name for the NIC which is connected to the FCoE SAN. For example "eth0"."), + prompts = [ _("NIC device name") ] ) + if button == TEXT_CANCEL_CHECK: + return INSTALL_BACK + + nic = entries[0].strip() + if nic not in self.anaconda.id.network.available(): + ButtonChoiceWindow(screen, _("Error"), + _("%s is not a valid NIC device name.") % nic) + return INSTALL_BACK + + self.anaconda.id.storage.fcoe.addSan(nic) + + return INSTALL_OK + def addIscsiDriveDialog(self, screen): if not network.hasActiveNetDev(): ButtonChoiceWindow(screen, _("Error"), diff --git a/ui/adddrive.glade b/ui/adddrive.glade index 599ed79..2d991ef 100644 --- a/ui/adddrive.glade +++ b/ui/adddrive.glade @@ -202,6 +202,25 @@ </child>
<child> + <widget class="GtkRadioButton" id="fcoeRadio"> + <property name="visible">True</property> + <property name="label" translatable="yes" context="yes">Add _FCoE SAN</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <property name="group">iscsiRadio</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> <widget class="GtkRadioButton" id="radiobutton1"> <property name="visible">True</property> <property name="sensitive">False</property> diff --git a/ui/fcoe-config.glade b/ui/fcoe-config.glade new file mode 100644 index 0000000..7034064 --- /dev/null +++ b/ui/fcoe-config.glade @@ -0,0 +1,149 @@ +<?xml version="1.0"?> +<glade-interface> + <!-- interface-requires gtk+ 2.16 --> + <!-- interface-naming-policy toplevel-contextual --> + <widget class="GtkDialog" id="fcoeDialog"> + <property name="title" translatable="yes">Configure FCoE Parameters</property> + <property name="window_position">center</property> + <property name="type_hint">dialog</property> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="border_width">12</property> + <property name="spacing">12</property> + <child> + <widget class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="label" translatable="yes">Please select the network interface which is connected to +your FCoE switch.</property> + <property name="wrap">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <widget class="GtkTable" id="fcoeTable"> + <property name="visible">True</property> + <property name="border_width">12</property> + <property name="n_columns">2</property> + <property name="column_spacing">6</property> + <property name="row_spacing">6</property> + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes">NIC</property> + </widget> + </child> + <child> + <widget class="GtkComboBox" id="fcoeNicCombo"> + <property name="visible">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <widget class="GtkButton" id="button1"> + <property name="label">gtk-cancel</property> + <property name="response_id">-6</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button2"> + <property name="response_id">-10</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <child> + <widget class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <child> + <widget class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkImage" id="image1"> + <property name="visible">True</property> + <property name="stock">gtk-add</property> + <property name="icon-size">4</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Add FCoE Disk(s)</property> + <property name="use_underline">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface>
Write out an /etc/fcoe/cfg-eth# file for each nic used to connect to an FCoE SAN during the install. --- storage/fcoe.py | 18 +++++++++++++++++- 1 files changed, 17 insertions(+), 1 deletions(-)
diff --git a/storage/fcoe.py b/storage/fcoe.py index 46c5bcb..1ea5e9d 100644 --- a/storage/fcoe.py +++ b/storage/fcoe.py @@ -21,6 +21,7 @@ import os import iutil import logging import time +from flags import flags log = logging.getLogger("anaconda")
import gettext @@ -86,7 +87,22 @@ class fcoe(object): return
def write(self, instPath, anaconda): - # erm, do we need todo anything here ? + if flags.test or not self.nics: + return + + if not os.path.isdir(instPath + "/etc/fcoe"): + os.makedirs(instPath + "/etc/fcoe", 0755) + + for nic in self.nics: + fd = os.open(instPath + "/etc/fcoe/cfg-" + nic, + os.O_RDWR | os.O_CREAT) + os.write(fd, '# Created by anaconda\n') + os.write(fd, '# Enable/Disable FCoE service at the Ethernet port\n') + os.write(fd, 'FCOE_ENABLE="yes"\n') + os.write(fd, '# Indicate if DCB service is required at the Ethernet port\n') + os.write(fd, 'DCB_REQUIRED="no"\n') + os.close(fd) + return
# vim:tw=78:ts=4:et:sw=4
Hans de Goede (hdegoede@redhat.com) said:
def write(self, instPath, anaconda):
# erm, do we need todo anything here ?
if flags.test or not self.nics:
return
if not os.path.isdir(instPath + "/etc/fcoe"):
os.makedirs(instPath + "/etc/fcoe", 0755)
for nic in self.nics:
fd = os.open(instPath + "/etc/fcoe/cfg-" + nic,
os.O_RDWR | os.O_CREAT)
os.write(fd, '# Created by anaconda\n')
os.write(fd, '# Enable/Disable FCoE service at the Ethernet port\n')
os.write(fd, 'FCOE_ENABLE="yes"\n')
os.write(fd, '# Indicate if DCB service is required at the Ethernet port\n')
os.write(fd, 'DCB_REQUIRED="no"\n')
os.close(fd)
Is this going to be a Red Hat specific config file?
Bill
On 07/02/2009 04:00 PM, Bill Nottingham wrote:
Hans de Goede (hdegoede@redhat.com) said:
def write(self, instPath, anaconda):
# erm, do we need todo anything here ?
if flags.test or not self.nics:
return
if not os.path.isdir(instPath + "/etc/fcoe"):
os.makedirs(instPath + "/etc/fcoe", 0755)
for nic in self.nics:
fd = os.open(instPath + "/etc/fcoe/cfg-" + nic,
os.O_RDWR | os.O_CREAT)
os.write(fd, '# Created by anaconda\n')
os.write(fd, '# Enable/Disable FCoE service at the Ethernet port\n')
os.write(fd, 'FCOE_ENABLE="yes"\n')
os.write(fd, '# Indicate if DCB service is required at the Ethernet port\n')
os.write(fd, 'DCB_REQUIRED="no"\n')
os.close(fd)
Is this going to be a Red Hat specific config file?
No this file is used by the upstream fcoe-utils provided init script.
Regards,
Hans
Hans de Goede (hdegoede@redhat.com) said:
Is this going to be a Red Hat specific config file?
No this file is used by the upstream fcoe-utils provided init script.
... which requires indexing by device name? For consistency, that may not be the best.
Bill
Hi,
On 07/02/2009 07:37 PM, Bill Nottingham wrote:
Hans de Goede (hdegoede@redhat.com) said:
Is this going to be a Red Hat specific config file?
No this file is used by the upstream fcoe-utils provided init script.
... which requires indexing by device name? For consistency, that may not be the best.
Yeah well all the script basically does is:
echo eth# > /sys/modules/fcoe/parameters/create
So the kernel interface wants a device name, which I agree sucks, but such is life.
Given that when this run the udev consistent device name rules have already done their magic this should not be a problem.
Regards,
Hans
anaconda-devel@lists.fedoraproject.org