usr/src/cmd/gui-install/src/disk_discovery_screen.py
changeset 1720 ebaf262338a3
parent 1644 e827d1934114
equal deleted inserted replaced
1719:e6d6162c0410 1720:ebaf262338a3
    40 
    40 
    41 from solaris_install import run, CalledProcessError
    41 from solaris_install import run, CalledProcessError
    42 from solaris_install.engine import InstallEngine
    42 from solaris_install.engine import InstallEngine
    43 from solaris_install.gui_install.base_screen import BaseScreen, \
    43 from solaris_install.gui_install.base_screen import BaseScreen, \
    44     NotOkToProceedError
    44     NotOkToProceedError
    45 from solaris_install.gui_install.gui_install_common import \
    45 from solaris_install.gui_install.gui_install_common import _, \
    46     is_discovery_complete, modal_dialog, queue_td_iscsi, \
    46     is_discovery_complete, modal_dialog, queue_td_iscsi, \
    47     set_td_results_state, start_td_iscsi, GLADE_ERROR_MSG, \
    47     set_td_results_state, start_td_iscsi, GLADE_ERROR_MSG, \
    48     ISCSI_LABEL
    48     ISCSI_LABEL
    49 from solaris_install.gui_install.install_profile import InstallProfile
    49 from solaris_install.gui_install.install_profile import InstallProfile
    50 from solaris_install.logger import INSTALL_LOGGER_NAME
    50 from solaris_install.logger import INSTALL_LOGGER_NAME
    54 from solaris_install.sysconfig.profile.ip_address import IPAddress
    54 from solaris_install.sysconfig.profile.ip_address import IPAddress
    55 
    55 
    56 
    56 
    57 LOGGER = None
    57 LOGGER = None
    58 
    58 
    59 # Strings displayed in GUI
       
    60 INTERNAL_ERROR_MSG = _("Internal error")
       
    61 DISK_DISCOVERY_TITLE = _("Disk Discovery")
       
    62 DISK_DISCOVERY_SUBTITLE = _("What types of disks would you like the "
       
    63                             "Installer to discover?")
       
    64 NO_DISK_TYPE_SELECTED_MSG = _("No disk types selected.")
       
    65 SELECT_DISK_TYPE_MSG = _("You must select at least one of the disk types "
       
    66                          "to search for.")
       
    67 TARGET_IP_1_FIELD = _("Target IP (#1)")
       
    68 TARGET_IP_2_FIELD = _("Target IP (#2)")
       
    69 TARGET_IP_3_FIELD = _("Target IP (#3)")
       
    70 TARGET_IP_4_FIELD = _("Target IP (#4)")
       
    71 TARGET_LUN_FIELD = _("LUN")
       
    72 NO_INITIATOR_NODE_MSG = _("Could not obtain Initiator Node Name for this host")
       
    73 REQUIRED_FIELD_MISSING_MSG = _("Required field not entered")
       
    74 ENTER_REQUIRED_FIELD_MSG = _("You must enter a value for : ")
       
    75 INVALID_IP_MSG = _("Invalid Target IP address")
       
    76 INVALID_TARGET_IQN_MSG = _("Invalid Target IQN string")
       
    77 INVALID_INITIATOR_IQN_MSG = _("Invalid Initiator IQN string")
       
    78 ENTER_VALID_VALUE_MSG = _("Enter a valid value")
       
    79 CHAP_PASSWORD_INVALID_LENGTH = _("CHAP password must be between 12 and 16 "
       
    80                                  "characters")
       
    81 CHAP_USERNAME_MISSING = _("CHAP username not specified")
       
    82 CHAP_PASSWORD_MISSING = _("CHAP password not specified")
       
    83 MAPPING_TARGET_MSG = _("Mapping iSCSI Target...")
       
    84 MAPPING_LUN_MSG = _("Mapping iSCSI LUN...")
       
    85 CANNOT_MAP_LUN_MSG = _("Unable to map iSCSI LUN")
       
    86 
       
    87 # Regex for iSCSI Qualified Names
    59 # Regex for iSCSI Qualified Names
    88 IQN_RE = re.compile("iqn\.\d{4}\-\d{2}\.\w+\.\w+", re.I)
    60 IQN_RE = re.compile("iqn\.\d{4}\-\d{2}\.\w+\.\w+", re.I)
    89 
    61 
    90 
    62 
    91 class DiskDiscoveryScreen(BaseScreen):
    63 class DiskDiscoveryScreen(BaseScreen):
    92     '''
    64     '''
    93         The Disk Discovery screen.
    65         The Disk Discovery screen.
    94     '''
    66     '''
       
    67 
       
    68     # Strings displayed in GUI
       
    69     INTERNAL_ERROR_MSG = _("Internal error")
       
    70     DISK_DISCOVERY_TITLE = _("Disk Discovery")
       
    71     DISK_DISCOVERY_SUBTITLE = _("What types of disks would you like the "
       
    72                                 "Installer to discover?")
       
    73     NO_DISK_TYPE_SELECTED_MSG = _("No disk types selected.")
       
    74     SELECT_DISK_TYPE_MSG = _("You must select at least one of the disk types "
       
    75                              "to search for.")
       
    76     TARGET_IP_1_FIELD = _("Target IP (#1)")
       
    77     TARGET_IP_2_FIELD = _("Target IP (#2)")
       
    78     TARGET_IP_3_FIELD = _("Target IP (#3)")
       
    79     TARGET_IP_4_FIELD = _("Target IP (#4)")
       
    80     TARGET_LUN_FIELD = _("LUN")
       
    81     NO_INITIATOR_NODE_MSG = _("Could not obtain Initiator Node Name for "
       
    82                               "this host")
       
    83     REQUIRED_FIELD_MISSING_MSG = _("Required field not entered")
       
    84     ENTER_REQUIRED_FIELD_MSG = _("You must enter a value for : ")
       
    85     INVALID_IP_MSG = _("Invalid Target IP address")
       
    86     INVALID_TARGET_IQN_MSG = _("Invalid Target IQN string")
       
    87     INVALID_INITIATOR_IQN_MSG = _("Invalid Initiator IQN string")
       
    88     ENTER_VALID_VALUE_MSG = _("Enter a valid value")
       
    89     CHAP_PASSWORD_INVALID_LENGTH = _("CHAP password must be between 12 and 16 "
       
    90                                      "characters")
       
    91     CHAP_USERNAME_MISSING = _("CHAP username not specified")
       
    92     CHAP_PASSWORD_MISSING = _("CHAP password not specified")
       
    93     MAPPING_TARGET_MSG = _("Mapping iSCSI Target...")
       
    94     MAPPING_LUN_MSG = _("Mapping iSCSI LUN...")
       
    95     CANNOT_MAP_LUN_MSG = _("Unable to map iSCSI LUN")
    95 
    96 
    96     def __init__(self, builder):
    97     def __init__(self, builder):
    97         ''' Initializer method - called from constructor.
    98         ''' Initializer method - called from constructor.
    98 
    99 
    99             Params:
   100             Params:
   190                     self._discovery_chap_check,
   191                     self._discovery_chap_check,
   191                     self._discovery_chap_detail_table,
   192                     self._discovery_chap_detail_table,
   192                     self._discovery_chap_name_entry,
   193                     self._discovery_chap_name_entry,
   193                     self._discovery_chap_pass_entry,
   194                     self._discovery_chap_pass_entry,
   194                     self._discovery_status_label]:
   195                     self._discovery_status_label]:
   195             modal_dialog(INTERNAL_ERROR_MSG, GLADE_ERROR_MSG)
   196             modal_dialog(self.INTERNAL_ERROR_MSG, GLADE_ERROR_MSG)
   196             raise RuntimeError(GLADE_ERROR_MSG)
   197             raise RuntimeError(GLADE_ERROR_MSG)
   197 
   198 
   198     def enter(self):
   199     def enter(self):
   199         ''' Show the Disk Screen.
   200         ''' Show the Disk Screen.
   200 
   201 
   207         self._toplevel = self.set_main_window_content(
   208         self._toplevel = self.set_main_window_content(
   208             "discovery_top_level_vbox")
   209             "discovery_top_level_vbox")
   209 
   210 
   210         # Screen-specific configuration
   211         # Screen-specific configuration
   211         self.activate_stage_label("diskdiscoverystagelabel")
   212         self.activate_stage_label("diskdiscoverystagelabel")
   212         self.set_titles(DISK_DISCOVERY_TITLE, DISK_DISCOVERY_SUBTITLE, None)
   213         self.set_titles(self.DISK_DISCOVERY_TITLE,
       
   214                         self.DISK_DISCOVERY_SUBTITLE, None)
   213         self._discovery_status_label.hide()
   215         self._discovery_status_label.hide()
   214         self._toplevel.show()
   216         self._toplevel.show()
   215         self._discovery_local_check.grab_focus()
   217         self._discovery_local_check.grab_focus()
   216         self.set_back_next(back_sensitive=True, next_sensitive=True)
   218         self.set_back_next(back_sensitive=True, next_sensitive=True)
   217 
   219 
   237 
   239 
   238         # error #1 - at least one disk type must be checked
   240         # error #1 - at least one disk type must be checked
   239         if not self._discovery_local_check.get_active() and \
   241         if not self._discovery_local_check.get_active() and \
   240            not self._discovery_iscsi_check.get_active():
   242            not self._discovery_iscsi_check.get_active():
   241             msg = "No disk types selected."
   243             msg = "No disk types selected."
   242             LOGGER.error(NO_DISK_TYPE_SELECTED_MSG)
   244             LOGGER.error(self.NO_DISK_TYPE_SELECTED_MSG)
   243             modal_dialog(NO_DISK_TYPE_SELECTED_MSG, SELECT_DISK_TYPE_MSG)
   245             modal_dialog(self.NO_DISK_TYPE_SELECTED_MSG,
       
   246                          self.SELECT_DISK_TYPE_MSG)
   244             raise NotOkToProceedError(msg)
   247             raise NotOkToProceedError(msg)
   245 
   248 
   246         if self._discovery_iscsi_check.get_active():
   249         if self._discovery_iscsi_check.get_active():
   247             # Save the on-screen iSCSI criteria values
   250             # Save the on-screen iSCSI criteria values
   248             self._target_ip = ''
   251             self._target_ip = ''
   316                 log_pw = None
   319                 log_pw = None
   317             LOGGER.info("CHAP Password: %s", log_pw)
   320             LOGGER.info("CHAP Password: %s", log_pw)
   318 
   321 
   319             # ERROR - required fields must be entered
   322             # ERROR - required fields must be entered
   320             required_fields = {
   323             required_fields = {
   321                 self._discovery_target_ip_1_entry: TARGET_IP_1_FIELD,
   324                 self._discovery_target_ip_1_entry: self.TARGET_IP_1_FIELD,
   322                 self._discovery_target_ip_2_entry: TARGET_IP_2_FIELD,
   325                 self._discovery_target_ip_2_entry: self.TARGET_IP_2_FIELD,
   323                 self._discovery_target_ip_3_entry: TARGET_IP_3_FIELD,
   326                 self._discovery_target_ip_3_entry: self.TARGET_IP_3_FIELD,
   324                 self._discovery_target_ip_4_entry: TARGET_IP_4_FIELD,
   327                 self._discovery_target_ip_4_entry: self.TARGET_IP_4_FIELD,
   325             }
   328             }
   326             #    self._discovery_lun_entry: TARGET_LUN_FIELD,
   329             #    self._discovery_lun_entry: self.TARGET_LUN_FIELD,
   327             for field in required_fields:
   330             for field in required_fields:
   328                 if not field.get_text():
   331                 if not field.get_text():
   329                     msg = REQUIRED_FIELD_MISSING_MSG
   332                     msg = self.REQUIRED_FIELD_MISSING_MSG
   330                     LOGGER.error(msg + " : " + required_fields[field])
   333                     LOGGER.error(msg + " : " + required_fields[field])
   331                     modal_dialog(msg,
   334                     modal_dialog(msg,
   332                         ENTER_REQUIRED_FIELD_MSG + required_fields[field])
   335                         self.ENTER_REQUIRED_FIELD_MSG + required_fields[field])
   333                     field.grab_focus()
   336                     field.grab_focus()
   334                     raise NotOkToProceedError(msg)
   337                     raise NotOkToProceedError(msg)
   335 
   338 
   336             # ERROR - IP must be proper IP address
   339             # ERROR - IP must be proper IP address
   337             try:
   340             try:
   338                 IPAddress.convert_address(self._target_ip)
   341                 IPAddress.convert_address(self._target_ip)
   339             except ValueError as error:
   342             except ValueError as error:
   340                 msg = INVALID_IP_MSG
   343                 msg = self.INVALID_IP_MSG
   341                 LOGGER.error(msg + " : " + str(error))
   344                 LOGGER.error(msg + " : " + str(error))
   342                 modal_dialog(msg, str(error))
   345                 modal_dialog(msg, str(error))
   343                 self._discovery_target_ip_1_entry.grab_focus()
   346                 self._discovery_target_ip_1_entry.grab_focus()
   344                 raise NotOkToProceedError(msg)
   347                 raise NotOkToProceedError(msg)
   345 
   348 
   346             # ERROR - Initiator Name must match regular expression
   349             # ERROR - Initiator Name must match regular expression
   347             if self._initiator_name is not None:
   350             if self._initiator_name is not None:
   348                 if IQN_RE.match(self._initiator_name) is None:
   351                 if IQN_RE.match(self._initiator_name) is None:
   349                     msg = INVALID_INITIATOR_IQN_MSG
   352                     msg = self.INVALID_INITIATOR_IQN_MSG
   350                     LOGGER.error(msg)
   353                     LOGGER.error(msg)
   351                     modal_dialog(msg, ENTER_VALID_VALUE_MSG)
   354                     modal_dialog(msg, self.ENTER_VALID_VALUE_MSG)
   352                     self._discovery_initiator_name_entry.grab_focus()
   355                     self._discovery_initiator_name_entry.grab_focus()
   353                     raise NotOkToProceedError(msg)
   356                     raise NotOkToProceedError(msg)
   354 
   357 
   355             # ERROR - Target name name must match regular expression
   358             # ERROR - Target name name must match regular expression
   356             if self._target_name is not None:
   359             if self._target_name is not None:
   357                 if IQN_RE.match(self._target_name) is None:
   360                 if IQN_RE.match(self._target_name) is None:
   358                     msg = INVALID_TARGET_IQN_MSG
   361                     msg = self.INVALID_TARGET_IQN_MSG
   359                     LOGGER.error(msg)
   362                     LOGGER.error(msg)
   360                     modal_dialog(msg, ENTER_VALID_VALUE_MSG)
   363                     modal_dialog(msg, self.ENTER_VALID_VALUE_MSG)
   361                     self._discovery_target_name_entry.grab_focus()
   364                     self._discovery_target_name_entry.grab_focus()
   362                     raise NotOkToProceedError(msg)
   365                     raise NotOkToProceedError(msg)
   363 
   366 
   364             # ERROR - if CHAP name is given, password must also
   367             # ERROR - if CHAP name is given, password must also
   365             # be given, and vice versa
   368             # be given, and vice versa
   366             if self._chap_name and not self._chap_password:
   369             if self._chap_name and not self._chap_password:
   367                 msg = CHAP_PASSWORD_MISSING
   370                 msg = self.CHAP_PASSWORD_MISSING
   368                 LOGGER.error(msg)
   371                 LOGGER.error(msg)
   369                 modal_dialog(msg, ENTER_VALID_VALUE_MSG)
   372                 modal_dialog(msg, self.ENTER_VALID_VALUE_MSG)
   370                 self._discovery_chap_pass_entry.grab_focus()
   373                 self._discovery_chap_pass_entry.grab_focus()
   371                 raise NotOkToProceedError(msg)
   374                 raise NotOkToProceedError(msg)
   372             if self._chap_password and not self._chap_name:
   375             if self._chap_password and not self._chap_name:
   373                 msg = CHAP_USERNAME_MISSING
   376                 msg = self.CHAP_USERNAME_MISSING
   374                 LOGGER.error(msg)
   377                 LOGGER.error(msg)
   375                 modal_dialog(msg, ENTER_VALID_VALUE_MSG)
   378                 modal_dialog(msg, self.ENTER_VALID_VALUE_MSG)
   376                 self._discovery_chap_name_entry.grab_focus()
   379                 self._discovery_chap_name_entry.grab_focus()
   377                 raise NotOkToProceedError(msg)
   380                 raise NotOkToProceedError(msg)
   378 
   381 
   379             # ERROR - CHAP password must be correct length
   382             # ERROR - CHAP password must be correct length
   380             if self._chap_password is not None:
   383             if self._chap_password is not None:
   381                 if not 12 <= len(self._chap_password) <= 16:
   384                 if not 12 <= len(self._chap_password) <= 16:
   382                     msg = CHAP_PASSWORD_INVALID_LENGTH
   385                     msg = self.CHAP_PASSWORD_INVALID_LENGTH
   383                     LOGGER.error(msg)
   386                     LOGGER.error(msg)
   384                     modal_dialog(msg, ENTER_VALID_VALUE_MSG)
   387                     modal_dialog(msg, self.ENTER_VALID_VALUE_MSG)
   385                     self._discovery_chap_pass_entry.grab_focus()
   388                     self._discovery_chap_pass_entry.grab_focus()
   386                     raise NotOkToProceedError(msg)
   389                     raise NotOkToProceedError(msg)
   387 
   390 
   388             # ERROR - must be able to connect to LUN to verify iSCSI disk
   391             # ERROR - must be able to connect to LUN to verify iSCSI disk
   389             # teardown any currently-configured Iscsis before trying
   392             # teardown any currently-configured Iscsis before trying
   424             # in a separate thread (otherwise the GUI would appear
   427             # in a separate thread (otherwise the GUI would appear
   425             # unresponsive and the user may think it has hung).
   428             # unresponsive and the user may think it has hung).
   426             self.set_back_next(back_sensitive=False, next_sensitive=False)
   429             self.set_back_next(back_sensitive=False, next_sensitive=False)
   427             if iscsi.target_lun is not None:
   430             if iscsi.target_lun is not None:
   428                 self._discovery_status_label.set_markup(
   431                 self._discovery_status_label.set_markup(
   429                     '<span font_desc="Bold">%s</span>' % MAPPING_LUN_MSG)
   432                     '<span font_desc="Bold">%s</span>' % self.MAPPING_LUN_MSG)
   430             else:
   433             else:
   431                 self._discovery_status_label.set_markup(
   434                 self._discovery_status_label.set_markup(
   432                     '<span font_desc="Bold">%s</span>' % MAPPING_TARGET_MSG)
   435                     '<span font_desc="Bold">%s</span>' %
       
   436                     self.MAPPING_TARGET_MSG)
   433             self._discovery_status_label.show()
   437             self._discovery_status_label.show()
   434             thread = SetupIscsiThread(iscsi)
   438             thread = SetupIscsiThread(iscsi)
   435             # Keep passing control back to Gtk+ to process its event queue
   439             # Keep passing control back to Gtk+ to process its event queue
   436             # until SetupIscsiThread has completed.
   440             # until SetupIscsiThread has completed.
   437             while thread.is_alive():
   441             while thread.is_alive():
   441                 time.sleep(0.1)
   445                 time.sleep(0.1)
   442 
   446 
   443             self._discovery_status_label.hide()
   447             self._discovery_status_label.hide()
   444             self.set_back_next(back_sensitive=True, next_sensitive=True)
   448             self.set_back_next(back_sensitive=True, next_sensitive=True)
   445             if thread.error is not None:
   449             if thread.error is not None:
   446                 msg = CANNOT_MAP_LUN_MSG
   450                 msg = self.CANNOT_MAP_LUN_MSG
   447                 LOGGER.error(msg)
   451                 LOGGER.error(msg)
   448                 LOGGER.error(str(thread.error))
   452                 LOGGER.error(str(thread.error))
   449                 modal_dialog(msg, str(thread.error))
   453                 modal_dialog(msg, str(thread.error))
   450                 self._discovery_target_ip_1_entry.grab_focus()
   454                 self._discovery_target_ip_1_entry.grab_focus()
   451                 raise NotOkToProceedError(msg)
   455                 raise NotOkToProceedError(msg)
   508         if widget.get_active():
   512         if widget.get_active():
   509             if self._dhcp_ip is not None:
   513             if self._dhcp_ip is not None:
   510                 try:
   514                 try:
   511                     segments = IPAddress.convert_address(self._dhcp_ip)
   515                     segments = IPAddress.convert_address(self._dhcp_ip)
   512                 except ValueError as error:
   516                 except ValueError as error:
   513                     title = INVALID_IP_MSG
   517                     title = self.INVALID_IP_MSG
   514                     msg = self._dhcp_ip + ' : ' + str(error)
   518                     msg = self._dhcp_ip + ' : ' + str(error)
   515                     LOGGER.error(title)
   519                     LOGGER.error(title)
   516                     LOGGER.error(msg)
   520                     LOGGER.error(msg)
   517                     modal_dialog(title, msg)
   521                     modal_dialog(title, msg)
   518                 else:
   522                 else:
   610         LOGGER.debug("Getting iSCSI Initiator Node Name for this system")
   614         LOGGER.debug("Getting iSCSI Initiator Node Name for this system")
   611         cmd = [ISCSIADM, "list", "initiator-node"]
   615         cmd = [ISCSIADM, "list", "initiator-node"]
   612         try:
   616         try:
   613             popen = run(cmd)
   617             popen = run(cmd)
   614         except CalledProcessError as error:
   618         except CalledProcessError as error:
   615             title = NO_INITIATOR_NODE_MSG
   619             title = self.NO_INITIATOR_NODE_MSG
   616             msg = cmd + " failed : " + str(error)
   620             msg = cmd + " failed : " + str(error)
   617             LOGGER.error(title)
   621             LOGGER.error(title)
   618             LOGGER.error(msg)
   622             LOGGER.error(msg)
   619             modal_dialog(title, msg)
   623             modal_dialog(title, msg)
   620         else:
   624         else:
   624                     break
   628                     break
   625             if initiator_name is not None:
   629             if initiator_name is not None:
   626                 self._discovery_initiator_name_entry.set_text(initiator_name)
   630                 self._discovery_initiator_name_entry.set_text(initiator_name)
   627                 LOGGER.debug("iSCSI Initiator Name is: ", initiator_name)
   631                 LOGGER.debug("iSCSI Initiator Name is: ", initiator_name)
   628             else:
   632             else:
   629                 title = NO_INITIATOR_NODE_MSG
   633                 title = self.NO_INITIATOR_NODE_MSG
   630                 msg = popen.stdout.splitlines()
   634                 msg = popen.stdout.splitlines()
   631                 LOGGER.error(title)
   635                 LOGGER.error(title)
   632                 LOGGER.error(msg)
   636                 LOGGER.error(msg)
   633                 modal_dialog(title, msg)
   637                 modal_dialog(title, msg)
   634 
   638