Package pyanaconda :: Package iw :: Module advanced_storage
[hide private]
[frames] | no frames]

Source Code for Module pyanaconda.iw.advanced_storage

  1  # 
  2  # Copyright (C) 2009  Red Hat, Inc.  All rights reserved. 
  3  # 
  4  # This program is free software; you can redistribute it and/or modify 
  5  # it under the terms of the GNU General Public License as published by 
  6  # the Free Software Foundation; either version 2 of the License, or 
  7  # (at your option) any later version. 
  8  # 
  9  # This program is distributed in the hope that it will be useful, 
 10  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 12  # GNU General Public License for more details. 
 13  # 
 14  # You should have received a copy of the GNU General Public License 
 15  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 16  # 
 17   
 18  import gettext 
 19  _ = lambda x: gettext.ldgettext("anaconda", x) 
 20   
 21  # UI methods for supporting adding advanced storage devices. 
 22  import functools 
 23  import gobject 
 24  import gtk 
 25  import gtk.glade 
 26  import datacombo 
 27  import DeviceSelector 
 28  from pyanaconda import gui 
 29  from pyanaconda import iutil 
 30  from pyanaconda import network 
 31  from pyanaconda import partIntfHelpers as pih 
 32  import pyanaconda.storage.fcoe 
 33  import pyanaconda.storage.iscsi 
 34   
 35  import logging 
 36  log = logging.getLogger("anaconda") 
 37   
38 -class iSCSICredentialsDialog(object):
39 - def __init__(self):
40 pass
41
42 - def _authentication_kind_changed(self, 43 combobox, 44 credentials, 45 rev_credentials):
46 active_value = combobox.get_active_value() 47 if active_value in [pih.CRED_NONE[0], pih.CRED_REUSE[0]]: 48 map(lambda w : w.hide(), credentials) 49 map(lambda w : w.hide(), rev_credentials) 50 elif active_value == pih.CRED_ONE[0]: 51 map(lambda w : w.show(), credentials) 52 map(lambda w : w.hide(), rev_credentials) 53 elif active_value == pih.CRED_BOTH[0]: 54 map(lambda w : w.show(), credentials) 55 map(lambda w : w.show(), rev_credentials)
56
57 - def _combo_box(self, entries, credentials, rev_credentials):
58 combo_store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_INT) 59 combo = datacombo.DataComboBox(store=combo_store) 60 for entry in entries: 61 combo.append(entry[1], entry[0]) 62 if len(entries) > 0: 63 combo.set_active(0) 64 combo.show_all() 65 combo.connect("changed", 66 self._authentication_kind_changed, 67 credentials, 68 rev_credentials) 69 return combo
70
71 - def _credentials_widgets(self, xml):
72 credentials = [xml.get_widget(w_name) for w_name in 73 ['username_label', 74 'username_entry', 75 'password_label', 76 'password_entry']] 77 rev_credentials = [xml.get_widget(w_name) for w_name in 78 ['r_username_label', 79 'r_username_entry', 80 'r_password_label', 81 'r_password_entry']] 82 return (credentials, rev_credentials)
83
84 - def _extract_credentials(self, xml):
85 return { 86 'username' : xml.get_widget("username_entry").get_text(), 87 'password' : xml.get_widget("password_entry").get_text(), 88 'r_username' : xml.get_widget("r_username_entry").get_text(), 89 'r_password' : xml.get_widget("r_password_entry").get_text() 90 }
91
92 -class iSCSIDiscoveryDialog(iSCSICredentialsDialog):
93
94 - def __init__(self, initiator, initiator_set):
95 super(iSCSIDiscoveryDialog, self).__init__() 96 (self.xml, self.dialog) = gui.getGladeWidget("iscsi-dialogs.glade", "discovery_dialog") 97 98 self.initiator = self.xml.get_widget("initiator") 99 self.initiator.set_text(initiator) 100 if initiator_set: 101 self.initiator.set_sensitive(False) 102 103 (credentials, rev_credentials) = self._credentials_widgets(self.xml) 104 self.combobox = self._combo_box([ 105 pih.CRED_NONE, 106 pih.CRED_ONE, 107 pih.CRED_BOTH, 108 ], credentials, rev_credentials) 109 vbox = self.xml.get_widget("d_discovery_vbox") 110 vbox.pack_start(self.combobox, expand=False)
111
112 - def discovery_dict(self):
113 dct = self._extract_credentials(self.xml) 114 115 auth_kind = self.combobox.get_active_value() 116 if auth_kind == pih.CRED_NONE[0]: 117 dct["username"] = dct["password"] = \ 118 dct["r_username"] = dct["r_password"] = None 119 elif auth_kind == pih.CRED_ONE[0]: 120 dct["r_username"] = dct["r_password"] = None 121 122 entered_ip = self.xml.get_widget("target_ip").get_text() 123 (ip, port) = pih.parse_ip(entered_ip) 124 dct["ipaddr"] = ip 125 dct["port"] = port 126 127 return dct
128
129 - def get_initiator(self):
130 return self.initiator.get_text()
131
132 -class iSCSILoginDialog(iSCSICredentialsDialog):
133
134 - def __init__(self):
135 super(iSCSILoginDialog, self).__init__() 136 (xml, self.dialog) = gui.getGladeWidget("iscsi-dialogs.glade", "login_dialog") 137 # take credentials from the discovery dialog 138 (self.credentials_xml, credentials_table) = gui.getGladeWidget("iscsi-dialogs.glade", "table_credentials") 139 (credentials, rev_credentials) = self._credentials_widgets(self.credentials_xml) 140 # and put them into the login dialog alignment 141 alignment = xml.get_widget("login_credentials_alignment") 142 alignment.add(credentials_table) 143 # setup the combobox 144 self.combobox = self._combo_box([ 145 pih.CRED_NONE, 146 pih.CRED_ONE, 147 pih.CRED_BOTH, 148 pih.CRED_REUSE 149 ], credentials, rev_credentials) 150 vbox = xml.get_widget("d_login_vbox") 151 vbox.pack_start(self.combobox, expand=False)
152
153 - def login_dict(self, discovery_dict):
154 dct = self._extract_credentials(self.credentials_xml) 155 156 auth_kind = self.combobox.get_active_value() 157 if auth_kind == pih.CRED_NONE[0]: 158 dct["username"] = dct["password"] = \ 159 dct["r_username"] = dct["r_password"] = None 160 elif auth_kind == pih.CRED_ONE[0]: 161 dct["r_username"] = dct["r_password"] = None 162 elif auth_kind == pih.CRED_REUSE[0]: 163 # only keep what we'll really use: 164 discovery_dict = dict((k,discovery_dict[k]) for k in discovery_dict if k in 165 ['username', 166 'password', 167 'r_username', 168 'r_password']) 169 dct.update(discovery_dict) 170 171 return dct
172
173 -class iSCSIGuiWizard(pih.iSCSIWizard):
174 NODE_NAME_COL = DeviceSelector.IMMUTABLE_COL + 1 175
176 - def __init__(self):
177 self.login_dialog = None 178 self.discovery_dialog = None
179
180 - def _destroy_when_dialog(self, dialog):
181 if dialog and dialog.dialog: 182 dialog.dialog.destroy()
183
184 - def _normalize_dialog_response(self, value):
185 """ 186 Maps the glade return values to a boolean. 187 188 Returns True upon success. 189 """ 190 if value == 1: 191 # gtk.RESPONSE_OK 192 return True 193 elif value == -6: 194 # gtk.RESPONSE_CANCEL 195 return False 196 elif value == gtk.RESPONSE_DELETE_EVENT: 197 # escape pressed to dismiss the dialog 198 return False 199 else: 200 raise ValueError("Unexpected dialog box return value: %d" % value)
201
202 - def _run_dialog(self, dialog):
203 gui.addFrame(dialog) 204 dialog.show() 205 rc = dialog.run() 206 dialog.hide() 207 return self._normalize_dialog_response(rc)
208
209 - def destroy_dialogs(self):
210 self._destroy_when_dialog(self.discovery_dialog) 211 self._destroy_when_dialog(self.login_dialog)
212
213 - def display_discovery_dialog(self, initiator, initiator_set):
214 self._destroy_when_dialog(self.discovery_dialog) 215 self.discovery_dialog = iSCSIDiscoveryDialog(initiator, initiator_set) 216 217 return self._run_dialog(self.discovery_dialog.dialog)
218
219 - def display_login_dialog(self):
220 self._destroy_when_dialog(self.login_dialog) 221 self.login_dialog = iSCSILoginDialog() 222 223 return self._run_dialog(self.login_dialog.dialog)
224
225 - def display_nodes_dialog(self, found_nodes):
226 def _login_button_disabler(device_selector, login_button, checked, item): 227 login_button.set_sensitive(len(device_selector.getSelected()) > 0)
228 229 (xml, dialog) = gui.getGladeWidget("iscsi-dialogs.glade", "nodes_dialog") 230 store = gtk.TreeStore( 231 gobject.TYPE_PYOBJECT, # teh object 232 gobject.TYPE_BOOLEAN, # visible 233 gobject.TYPE_BOOLEAN, # active (checked) 234 gobject.TYPE_BOOLEAN, # immutable 235 gobject.TYPE_STRING # node name 236 ) 237 map(lambda node : store.append(None, ( 238 node, # the object 239 True, # visible 240 True, # active 241 False, # not immutable 242 node.name)), # node's name 243 found_nodes) 244 245 # create and setup the device selector 246 model = store.filter_new() 247 view = gtk.TreeView(model) 248 ds = DeviceSelector.DeviceSelector( 249 store, 250 model, 251 view) 252 callback = functools.partial(_login_button_disabler, 253 ds, 254 xml.get_widget("button_login")) 255 ds.createSelectionCol(toggledCB=callback) 256 ds.addColumn(_("Node Name"), self.NODE_NAME_COL) 257 # attach the treeview to the dialog 258 sw = xml.get_widget("nodes_scrolled_window") 259 sw.add(view) 260 sw.show_all() 261 262 # run the dialog 263 rc = self._run_dialog(dialog) 264 # filter out selected nodes: 265 selected_nodes = map(lambda raw : raw[0], ds.getSelected()) 266 dialog.destroy() 267 return (rc, selected_nodes)
268
269 - def display_success_dialog(self, success_nodes, fail_nodes, fail_reason):
270 (xml, dialog) = gui.getGladeWidget("iscsi-dialogs.glade", "success_dialog") 271 w_success = xml.get_widget("label_success") 272 w_success_win = xml.get_widget("scroll_window_success") 273 w_success_val = xml.get_widget("text_success") 274 w_fail = xml.get_widget("label_fail") 275 w_fail_win = xml.get_widget("scroll_window_fail") 276 w_fail_val = xml.get_widget("text_fail") 277 w_reason = xml.get_widget("label_reason") 278 w_reason_val = xml.get_widget("label_reason_val") 279 w_retry = xml.get_widget("button_retry") 280 w_separator = xml.get_widget("separator") 281 282 if success_nodes: 283 markup = "\n".join(map(lambda n: n.name, success_nodes)) 284 buf = gtk.TextBuffer() 285 buf.set_text(markup) 286 w_success.show() 287 w_success_val.set_buffer(buf) 288 w_success_win.show() 289 if fail_nodes: 290 markup = "\n".join(map(lambda n: n.name, fail_nodes)) 291 buf = gtk.TextBuffer() 292 buf.set_text(markup) 293 w_fail.show() 294 w_fail_val.set_buffer(buf) 295 w_fail_win.show() 296 w_retry.show() 297 if fail_reason: 298 w_reason.show() 299 w_reason_val.set_markup(fail_reason) 300 w_reason_val.show() 301 if success_nodes and fail_nodes: 302 # only if there's anything to be separated display the separator 303 w_separator.show() 304 305 rc = self._run_dialog(dialog) 306 dialog.destroy() 307 return rc
308
309 - def get_discovery_dict(self):
310 return self.discovery_dialog.discovery_dict()
311
312 - def get_initiator(self):
313 return self.discovery_dialog.get_initiator()
314
315 - def get_login_dict(self):
316 return self.login_dialog.login_dict(self.get_discovery_dict())
317
318 - def set_initiator(self, initiator, initiator_set):
319 (self.initiator, self.initiator_set) = initiator, initiator_set
320
321 -def addFcoeDrive(anaconda):
322 (dxml, dialog) = gui.getGladeWidget("fcoe-config.glade", "fcoeDialog") 323 combo = dxml.get_widget("fcoeNicCombo") 324 dcb_cb = dxml.get_widget("dcbCheckbutton") 325 326 # Populate the combo 327 cell = gtk.CellRendererText() 328 combo.pack_start(cell, True) 329 combo.set_attributes(cell, text = 0) 330 cell.set_property("wrap-width", 525) 331 combo.set_size_request(480, -1) 332 store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING) 333 combo.set_model(store) 334 335 netdevs = anaconda.network.netdevices 336 keys = netdevs.keys() 337 keys.sort() 338 selected_interface = None 339 for dev in keys: 340 i = store.append(None) 341 desc = netdevs[dev].description 342 if desc: 343 desc = "%s - %s" %(dev, desc) 344 else: 345 desc = "%s" %(dev,) 346 347 mac = netdevs[dev].get("HWADDR") 348 if mac: 349 desc = "%s - %s" %(desc, mac) 350 351 if selected_interface is None: 352 selected_interface = i 353 354 store[i] = (desc, dev) 355 356 if selected_interface: 357 combo.set_active_iter(selected_interface) 358 else: 359 combo.set_active(0) 360 361 # Show the dialog 362 gui.addFrame(dialog) 363 dialog.show_all() 364 sg = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) 365 sg.add_widget(dxml.get_widget("fcoeNicCombo")) 366 367 while True: 368 # make sure the dialog pops into foreground in case this is the second 369 # time through the loop: 370 dialog.present() 371 rc = dialog.run() 372 373 if rc in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT]: 374 break 375 376 iter = combo.get_active_iter() 377 if iter is None: 378 anaconda.intf.messageWindow(_("Error"), 379 _("You must select a NIC to use."), 380 type="warning", custom_icon="error") 381 continue 382 383 try: 384 anaconda.storage.fcoe.addSan(store.get_value(iter, 1), 385 dcb=dcb_cb.get_active(), 386 intf=anaconda.intf) 387 except IOError as e: 388 anaconda.intf.messageWindow(_("Error"), str(e)) 389 rc = gtk.RESPONSE_CANCEL 390 391 break 392 393 dialog.destroy() 394 return rc
395
396 -def addIscsiDrive(anaconda):
397 """ 398 Displays a series of dialogs that walk the user through discovering and 399 logging into iscsi nodes. 400 401 Returns gtk.RESPONSE_OK if at least one iscsi node has been logged into. 402 """ 403 404 # make sure the network is up 405 if not network.hasActiveNetDev(): 406 if not anaconda.intf.enableNetwork(): 407 log.info("addIscsiDrive(): early exit, network disabled.") 408 return gtk.RESPONSE_CANCEL 409 410 wizard = iSCSIGuiWizard() 411 login_ok_nodes = pih.drive_iscsi_addition(anaconda, wizard) 412 if len(login_ok_nodes): 413 return gtk.RESPONSE_OK 414 log.info("addIscsiDrive(): no new nodes added") 415 return gtk.RESPONSE_CANCEL
416
417 -def addZfcpDrive(anaconda):
418 (dxml, dialog) = gui.getGladeWidget("zfcp-config.glade", "zfcpDialog") 419 gui.addFrame(dialog) 420 dialog.show_all() 421 sg = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) 422 for w in ["devnumEntry", "wwpnEntry", "fcplunEntry"]: 423 sg.add_widget(dxml.get_widget(w)) 424 425 while True: 426 dialog.present() 427 rc = dialog.run() 428 if rc != gtk.RESPONSE_APPLY: 429 break 430 431 devnum = dxml.get_widget("devnumEntry").get_text().strip() 432 wwpn = dxml.get_widget("wwpnEntry").get_text().strip() 433 fcplun = dxml.get_widget("fcplunEntry").get_text().strip() 434 435 try: 436 anaconda.storage.zfcp.addFCP(devnum, wwpn, fcplun) 437 except ValueError as e: 438 anaconda.intf.messageWindow(_("Error"), str(e)) 439 continue 440 441 break 442 443 dialog.destroy() 444 return rc
445
446 -def addDrive(anaconda):
447 (dxml, dialog) = gui.getGladeWidget("adddrive.glade", "addDriveDialog") 448 gui.addFrame(dialog) 449 dialog.show_all() 450 if not iutil.isS390(): 451 dxml.get_widget("zfcpRadio").hide() 452 dxml.get_widget("zfcpRadio").set_group(None) 453 454 if not pyanaconda.storage.iscsi.has_iscsi(): 455 dxml.get_widget("iscsiRadio").set_sensitive(False) 456 dxml.get_widget("iscsiRadio").set_active(False) 457 458 if not pyanaconda.storage.fcoe.has_fcoe(): 459 dxml.get_widget("fcoeRadio").set_sensitive(False) 460 dxml.get_widget("fcoeRadio").set_active(False) 461 462 #figure out what advanced devices we have available and put focus on the first one 463 group = dxml.get_widget("iscsiRadio").get_group() 464 for button in reversed(group): 465 if button is not None and button.get_property("sensitive"): 466 button.set_active(True) 467 button.grab_focus() 468 break 469 470 rc = dialog.run() 471 dialog.hide() 472 473 if rc in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT]: 474 return False 475 476 if dxml.get_widget("iscsiRadio").get_active() and pyanaconda.storage.iscsi.has_iscsi(): 477 rc = addIscsiDrive(anaconda) 478 elif dxml.get_widget("fcoeRadio").get_active() and pyanaconda.storage.fcoe.has_fcoe(): 479 rc = addFcoeDrive(anaconda) 480 elif dxml.get_widget("zfcpRadio") is not None and dxml.get_widget("zfcpRadio").get_active(): 481 rc = addZfcpDrive(anaconda) 482 483 dialog.destroy() 484 485 if rc in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT]: 486 return False 487 else: 488 return True
489