qubes-installer-qubes-os/anaconda/pyanaconda/textw/add_drive_text.py
2013-01-24 01:45:53 +01:00

442 lines
17 KiB
Python

#
# Copyright (C) 2010 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/>.
#
from pyanaconda import iutil
from pyanaconda import network
from pyanaconda.storage import iscsi
from pyanaconda.storage import fcoe
from pyanaconda.storage import zfcp
from snack import *
from constants_text import *
from pyanaconda.constants import *
import pyanaconda.partIntfHelpers as pih
from pyanaconda import isys
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
import logging
log = logging.getLogger("anaconda")
# iSCSI Wizard classes and helpers
class GridEntry(object):
def __init__(self, text, disabled=False, password=False, width=20):
self.text = text
self.disabled = disabled
self.password = password
self.width = width
class iSCSITextWizard(pih.iSCSIWizard):
def __init__(self, screen):
self.screen = screen
self.entry_target_ip = None
self.entry_initiator = None
self.entry_disc_username = None
self.entry_disc_password = None
self.entry_disc_r_username = None
self.entry_disc_r_password = None
self.entry_login_username = None
self.entry_login_password = None
self.entry_login_r_username = None
self.entry_login_r_password = None
self.listbox_disc = None
self.listbox_login = None
self.listbox_nodes = None
@staticmethod
def _auth_entries(cred_type):
all_entries = [
GridEntry(_("CHAP Username:")),
GridEntry(_("CHAP Password:"), password=True),
GridEntry(_("Reverse CHAP Username:")),
GridEntry(_("Reverse CHAP Password:"), password=True)
]
entries = [None for i in range(4)]
if cred_type == pih.CRED_ONE[0]:
entries = all_entries[0:2] + [None for i in range(2)]
elif cred_type == pih.CRED_BOTH[0]:
entries = all_entries
return entries
@staticmethod
def _build_grid(grid_entries):
entries = []
grid = Grid(2, len(grid_entries))
for (i, ge) in enumerate(grid_entries):
if ge:
grid.setField(Label(ge.text), 0, i)
entry = Entry(ge.width, password=ge.password)
if ge.disabled:
entry.setFlags(FLAG_DISABLED, FLAGS_SET)
grid.setField(entry, 1, i)
else:
entry = None
# we want Nones in grid_entries result in Nones in return value
entries.append(entry)
return (grid, entries)
@staticmethod
def _value_when(entry):
return entry.value() if entry else None
def _discovery_auth_dialog(self):
if self.listbox_disc.current() == pih.CRED_NONE[0]:
# we need not collect anything
return True
grid = GridForm(self.screen, _("iSCSI Discovery Credentials"), 1, 3)
grid.add(TextboxReflowed(50,
_("Please enter the iSCSI "
"discovery credentials.")),
0, 0)
auth_entries = self._auth_entries(self.listbox_disc.current())
(basic_grid, entries) = self._build_grid(auth_entries)
(self.entry_disc_username,
self.entry_disc_password,
self.entry_disc_r_username,
self.entry_disc_r_password) = entries
grid.add(basic_grid, 0, 1, padding=(0, 1, 0, 1))
grid.buttons = ButtonBar(self.screen,
[TEXT_OK_BUTTON,TEXT_CANCEL_BUTTON])
grid.add(grid.buttons, 0, 2, padding=(0, 1, 0, -1))
return self._run_grid(grid)
def _discovery_setup_dialog(self, initiator, initiator_set):
grid = GridForm(self.screen, _("iSCSI Discovery"), 1, 7)
header_text = TextboxReflowed(60,
_("To use iSCSI disks, you must provide "
"the address of your iSCSI target and "
"the iSCSI initiator name you've "
"configured for your host."))
grid.add(header_text, 0, 0)
entry_list = [
GridEntry(_("Target IP Address:"), width=40),
GridEntry(_("iSCSI Initiator Name:"),
disabled=initiator_set,
width=40)
]
(basic_grid, (self.entry_target_ip, self.entry_initiator)) = \
self._build_grid(entry_list)
self.entry_initiator.set(initiator)
grid.add(basic_grid, 0, 1)
grid.add(TextboxReflowed(60,
_("What kind of iSCSI discovery "
"authentication do you wish to perform:")),
0, 2, padding=(0, 1, 0, 0))
self.listbox_disc = Listbox(3, scroll=1)
self.listbox_disc.append(*reversed(pih.CRED_NONE))
self.listbox_disc.append(*reversed(pih.CRED_ONE))
self.listbox_disc.append(*reversed(pih.CRED_BOTH))
grid.add(self.listbox_disc, 0, 3)
grid.add(TextboxReflowed(60,
_("What kind of iSCSI login authentication "
"do you wish to perform:")),
0, 4, padding=(0, 1, 0, 0))
self.listbox_login = Listbox(3, scroll=1)
self.listbox_login.append(*reversed(pih.CRED_NONE))
self.listbox_login.append(*reversed(pih.CRED_ONE))
self.listbox_login.append(*reversed(pih.CRED_BOTH))
self.listbox_login.append(*reversed(pih.CRED_REUSE))
grid.add(self.listbox_login, 0, 5)
grid.buttons = ButtonBar(self.screen,
[TEXT_OK_BUTTON, TEXT_CANCEL_BUTTON])
grid.add(grid.buttons, 0, 6, padding=(0, 1, 0, -1))
return self._run_grid(grid)
def _run_grid(self, grid):
result = grid.run()
button = grid.buttons.buttonPressed(result)
self.screen.popWindow()
return bool(button == TEXT_OK_CHECK or result == "F12")
def destroy_dialogs(self):
pass
def display_discovery_dialog(self, initiator, initiator_set):
# this is in fact two dialogs here due to limited screen space in TUI
return self._discovery_setup_dialog(initiator, initiator_set) and \
self._discovery_auth_dialog()
def display_login_dialog(self):
# in TUI, the login credentials are asked for with nodes list, so this
# should never stop us:
return True
def display_nodes_dialog(self, found_nodes, ifaces):
grid_height = 4
basic_grid = None
if self.listbox_login.current() not in \
(pih.CRED_NONE[0], pih.CRED_REUSE[0]):
auth_entries = self._auth_entries(self.listbox_login.current())
(basic_grid, entries) = self._build_grid(auth_entries)
(self.entry_login_username,
self.entry_login_password,
self.entry_login_r_username,
self.entry_login_r_password) = entries
grid_height += 1
grid = GridForm(self.screen, _("iSCSI Discovered Nodes"), 1, 5)
grid.add(TextboxReflowed(50,
_("Check the nodes you wish to log into:")),
0, 0)
listbox = CheckboxTree(5, scroll=1)
# unfortunately, Listbox.add won't accept node directly as the second
# argument, we have to remember the list and use an index
for i, node in enumerate(found_nodes):
node_description = "%s via %s" % (node.name,
ifaces.get(node.iface,
node.iface))
listbox.append(node_description, i, selected=True)
grid.add(listbox, 0, 1, padding=(0, 1, 0, 1))
if basic_grid:
grid.add(TextboxReflowed(60,
_("Please enter iSCSI login credentials "
"for the selected nodes:")),
0, 2)
grid.add(basic_grid, 0, 3, padding=(0, 1, 0, 1))
grid.buttons = ButtonBar(self.screen,
[TEXT_OK_BUTTON, TEXT_CANCEL_BUTTON])
grid.add(grid.buttons, 0, 4, padding=(0, 0, 0, -1))
rc = self._run_grid(grid)
selected_nodes = [node for (i, node) in enumerate(found_nodes)
if i in listbox.getSelection()]
return (rc, selected_nodes)
def display_success_dialog(self, success_nodes, fail_nodes, fail_reason,
ifaces):
buttons = [TEXT_OK_BUTTON]
msg = _("Successfully logged into all the selected nodes.")
msg_reason = _("Reason:")
if fail_nodes:
buttons.append(TEXT_RETRY_BUTTON)
msg = _("Could not log into the following nodes:\n")
msg = reduce(lambda s1, s2: "%s\n%s" % (s1, s2), fail_nodes, msg)
if fail_reason:
msg = "%s\n\n%s\n%s" % (msg, msg_reason, fail_reason)
rc = ButtonChoiceWindow(self.screen,
_("iSCSI Login Results"),
msg,
buttons)
return True if rc == TEXT_OK_CHECK else False
def get_discovery_dict(self):
dct = {
'username' : self._value_when(self.entry_disc_username),
'password' : self._value_when(self.entry_disc_password),
'r_username' : self._value_when(self.entry_disc_r_username),
'r_password' : self._value_when(self.entry_disc_r_password)
}
entered_ip = self.entry_target_ip.value()
(ip, port) = pih.parse_ip(entered_ip)
dct["ipaddr"] = ip
dct["port"] = port
return dct
def get_initiator(self):
return self.entry_initiator.value()
def get_login_dict(self):
auth_kind = self.listbox_login.current()
if auth_kind == pih.CRED_REUSE[0]:
discovery_dict = self.get_discovery_dict()
dct = dict((k,discovery_dict[k]) for k in discovery_dict if k in
['username',
'password',
'r_username',
'r_password'])
else:
dct = {
'username' : self._value_when(self.entry_login_username),
'password' : self._value_when(self.entry_login_password),
'r_username' : self._value_when(self.entry_login_r_username),
'r_password' : self._value_when(self.entry_login_r_password)
}
return dct
def set_initiator(self, initiator, initiator_set):
pass
# general add drive stuff
class addDriveDialog(object):
def __init__(self, anaconda):
self.anaconda = anaconda
def addDriveDialog(self, screen):
newdrv = []
if iscsi.has_iscsi():
if iscsi.iscsi().mode == "none":
newdrv.append("Add iSCSI target")
newdrv.append("Add iSCSI target - use interface binding")
elif iscsi.iscsi().mode == "bind":
newdrv.append("Add iSCSI target - use interface binding")
elif iscsi.iscsi().mode == "default":
newdrv.append("Add iSCSI target")
if iutil.isS390():
newdrv.append( "Add zFCP LUN" )
if fcoe.has_fcoe():
newdrv.append("Add FCoE SAN")
if len(newdrv) == 0:
return INSTALL_BACK
(button, choice) = ListboxChoiceWindow(screen,
_("Advanced Storage Options"),
_("How would you like to modify "
"your drive configuration?"),
newdrv,
[ TEXT_OK_BUTTON, TEXT_BACK_BUTTON],
width=55, height=3)
if button == TEXT_BACK_CHECK:
return INSTALL_BACK
if newdrv[choice] == "Add zFCP LUN":
try:
return self.addZFCPDriveDialog(screen)
except ValueError as e:
ButtonChoiceWindow(screen, _("Error"), str(e))
return INSTALL_BACK
elif newdrv[choice] == "Add FCoE SAN":
try:
return self.addFcoeDriveDialog(screen)
except ValueError as e:
ButtonChoiceWindow(screen, _("Error"), str(e))
return INSTALL_BACK
elif newdrv[choice].startswith("Add iSCSI target"):
bind = newdrv[choice] == "Add iSCSI target - use interface binding"
try:
return self.addIscsiDriveDialog(screen, bind)
except (ValueError, IOError) as e:
ButtonChoiceWindow(screen, _("Error"), str(e))
return INSTALL_BACK
def addZFCPDriveDialog(self, screen):
(button, entries) = EntryWindow(screen,
_("Add FCP Device"),
_("zSeries machines can access industry-standard SCSI devices via Fibre Channel (FCP). You need to provide a 16 bit device number, a 64 bit World Wide Port Name (WWPN), and a 64 bit FCP LUN for each device."),
prompts = [ "Device number",
"WWPN",
"FCP LUN" ] )
if button == TEXT_CANCEL_CHECK:
return INSTALL_BACK
devnum = entries[0].strip()
wwpn = entries[1].strip()
fcplun = entries[2].strip()
# This may throw a value error, which gets handled by addDriveDialog()
zfcp.ZFCP().addFCP(devnum, wwpn, fcplun)
return INSTALL_OK
def addFcoeDriveDialog(self, screen):
devs = network.getDevices()
devs.sort()
if not devs:
ButtonChoiceWindow(screen, _("Error"),
_("No network cards present."))
return INSTALL_BACK
grid = GridFormHelp(screen, _("Add FCoE SAN"), "fcoeconfig",
1, 4)
tb = TextboxReflowed(60,
_("Select which NIC is connected to the FCoE SAN."))
grid.add(tb, 0, 0, anchorLeft = 1, padding = (0, 0, 0, 1))
interfaceList = Listbox(height=len(devs), scroll=1)
for dev in devs:
hwaddr = isys.getMacAddress(dev)
if hwaddr:
desc = "%s - %.50s" % (dev, hwaddr)
else:
desc = dev
interfaceList.append(desc, dev)
interfaceList.setCurrent(devs[0])
grid.add(interfaceList, 0, 1, padding = (0, 1, 0, 0))
dcbCheckbox = Checkbox(_("Use DCB"), 1)
grid.add(dcbCheckbox, 0, 2, anchorLeft = 1)
autovlanCheckbox = Checkbox(_("Use auto vlan"), 1)
grid.add(autovlanCheckbox, 0, 3, anchorLeft = 1)
buttons = ButtonBar(screen, [TEXT_OK_BUTTON, TEXT_BACK_BUTTON] )
grid.add(buttons, 0, 4, anchorLeft = 1, growx = 1)
result = grid.run()
if buttons.buttonPressed(result) == TEXT_BACK_CHECK:
screen.popWindow()
return INSTALL_BACK
nic = interfaceList.current()
dcb = dcbCheckbox.selected()
auto_vlan = autovlanCheckbox.selected()
fcoe.fcoe().addSan(nic=nic, dcb=dcb, auto_vlan=auto_vlan,
intf=self.anaconda.intf)
screen.popWindow()
return INSTALL_OK
def addIscsiDriveDialog(self, screen, bind=False):
if not network.hasActiveNetDev():
ButtonChoiceWindow(screen, _("Error"),
"Must have a network configuration set up "
"for iSCSI config. Please boot with "
"'linux asknetwork'")
log.info("addIscsiDriveDialog(): early exit, network disabled.")
return INSTALL_BACK
# This will modify behaviour of iscsi.discovery() function
if iscsi.iscsi().mode == "none" and not bind:
iscsi.iscsi().delete_interfaces()
elif (iscsi.iscsi().mode == "none" and bind) \
or iscsi.iscsi().mode == "bind":
active = set(network.getActiveNetDevs())
created = set(iscsi.iscsi().ifaces.values())
iscsi.iscsi().create_interfaces(active - created)
wizard = iSCSITextWizard(screen)
login_ok_nodes = pih.drive_iscsi_addition(self.anaconda, wizard)
if len(login_ok_nodes):
return INSTALL_OK
log.info("addIscsiDriveDialog(): no new nodes added")
return INSTALL_BACK