components/openstack/ironic/files/drivers/modules/solaris_ipmitool.py
changeset 6855 ea44e7e0ca98
parent 5517 7758049098f4
child 7350 7cd865fc284a
equal deleted inserted replaced
6854:52081f923019 6855:ea44e7e0ca98
    36 import urllib2
    36 import urllib2
    37 from urlparse import urlparse
    37 from urlparse import urlparse
    38 
    38 
    39 from lockfile import LockFile, LockTimeout
    39 from lockfile import LockFile, LockTimeout
    40 from oslo_concurrency import processutils
    40 from oslo_concurrency import processutils
    41 from oslo.config import cfg
    41 from oslo_config import cfg
    42 from oslo_utils import excutils
    42 from oslo_log import log as logging
       
    43 from oslo_service import loopingcall
       
    44 from oslo_utils import excutils, strutils
    43 from scp import SCPClient
    45 from scp import SCPClient
       
    46 import six
    44 
    47 
    45 from ironic.common import boot_devices, exception, images, keystone, states, \
    48 from ironic.common import boot_devices, exception, images, keystone, states, \
    46     utils
    49     utils
    47 from ironic.common.i18n import _, _LE, _LI, _LW
    50 from ironic.common.i18n import _, _LE, _LI, _LW
    48 from ironic.conductor import task_manager
    51 from ironic.conductor import task_manager
    49 from ironic.conductor import utils as manager_utils
    52 from ironic.conductor import utils as manager_utils
    50 from ironic.db import api as dbapi
    53 from ironic.db import api as dbapi
    51 from ironic.drivers import base
    54 from ironic.drivers import base
    52 from ironic.drivers.modules import ipmitool
    55 from ironic.drivers.modules import ipmitool
    53 from ironic.drivers import utils as driver_utils
    56 from ironic.drivers import utils as driver_utils
    54 from ironic.openstack.common import log as logging
    57 from ironic import objects
    55 from ironic.openstack.common import loopingcall
       
    56 
    58 
    57 PLATFORM = platform.system()
    59 PLATFORM = platform.system()
    58 if PLATFORM != "SunOS":
    60 if PLATFORM != "SunOS":
    59     import tarfile
    61     import tarfile
    60 else:
    62 else:
    88                help='SSH Filename to use.'),
    90                help='SSH Filename to use.'),
    89     cfg.StrOpt('ssh_key_contents',
    91     cfg.StrOpt('ssh_key_contents',
    90                help='Actual SSH Key contents to use.')
    92                help='Actual SSH Key contents to use.')
    91     ]
    93     ]
    92 
    94 
    93 AUTH_OPTS = [
       
    94     cfg.StrOpt('auth_strategy',
       
    95                default='keystone',
       
    96                help='Method to use for authentication: noauth or keystone.')
       
    97     ]
       
    98 
       
    99 SOLARIS_IPMI_OPTS = [
    95 SOLARIS_IPMI_OPTS = [
   100     cfg.StrOpt('imagecache_dirname',
    96     cfg.StrOpt('imagecache_dirname',
   101                default='/var/lib/ironic/images',
    97                default='/var/lib/ironic/images',
   102                help='Default path to image cache.'),
    98                help='Default path to image cache.'),
   103     cfg.StrOpt('imagecache_lock_timeout',
    99     cfg.StrOpt('imagecache_lock_timeout',
   107 
   103 
   108 LOG = logging.getLogger(__name__)
   104 LOG = logging.getLogger(__name__)
   109 
   105 
   110 CONF = cfg.CONF
   106 CONF = cfg.CONF
   111 OPT_GROUP = cfg.OptGroup(name='ai',
   107 OPT_GROUP = cfg.OptGroup(name='ai',
   112                          title='Options for the Automated Install driver')
   108                          title='Options for the Solaris driver')
   113 CONF.register_group(OPT_GROUP)
   109 CONF.register_group(OPT_GROUP)
   114 CONF.register_opts(AI_OPTS, OPT_GROUP)
   110 CONF.register_opts(AI_OPTS, OPT_GROUP)
   115 CONF.register_opts(AUTH_OPTS)
       
   116 SOLARIS_IPMI_GROUP = cfg.OptGroup(
   111 SOLARIS_IPMI_GROUP = cfg.OptGroup(
   117     name="solaris_ipmi",
   112     name="solaris_ipmi",
   118     title="Options defined in ironic.drivers.modules.solaris_ipmi")
   113     title="Options defined in ironic.drivers.modules.solaris_ipmi")
   119 CONF.register_group(SOLARIS_IPMI_GROUP)
   114 CONF.register_group(SOLARIS_IPMI_GROUP)
   120 CONF.register_opts(SOLARIS_IPMI_OPTS, SOLARIS_IPMI_GROUP)
   115 CONF.register_opts(SOLARIS_IPMI_OPTS, SOLARIS_IPMI_GROUP)
   142     'fmri': _("List of IPS package FMRIs to be installed. "
   137     'fmri': _("List of IPS package FMRIs to be installed. "
   143               "Required if publishers property is set."),
   138               "Required if publishers property is set."),
   144     'install_profiles': _("List of configuration profiles to be applied "
   139     'install_profiles': _("List of configuration profiles to be applied "
   145                           "to the installation environment during an install. "
   140                           "to the installation environment during an install. "
   146                           "Optional."),
   141                           "Optional."),
       
   142     'publishers': _("List of IPS publishers to install from, in the format "
       
   143                     "name@origin. Required if fmri property is set."),
       
   144     'sc_profiles': _("List of system configuration profiles to be applied "
       
   145                      "to an installed system. Optional."),
       
   146 
       
   147     'ipmi_port': _("remote IPMI RMCP port. Optional."),
       
   148     'ipmi_priv_level':
       
   149         _("privilege level; default is ADMINISTRATOR. "
       
   150           "One of %s. Optional.") % '. '.join(ipmitool.VALID_PRIV_LEVELS),
   147     'ipmi_bridging': _("bridging_type; default is \"no\". One of \"single\", "
   151     'ipmi_bridging': _("bridging_type; default is \"no\". One of \"single\", "
   148                        "\"dual\", \"no\". Optional."),
   152                        "\"dual\", \"no\". Optional."),
       
   153     'ipmi_transit_channel': _("transit channel for bridged request. Required "
       
   154                               "only if ipmi_bridging is set to \"dual\"."),
       
   155     'ipmi_transit_address': _("transit address for bridged request. Required "
       
   156                               "only if ipmi_bridging is set to \"dual\"."),
       
   157     'ipmi_target_channel': _("destination channel for bridged request. "
       
   158                              "Required only if ipmi_bridging is set to "
       
   159                              "\"single\" or \"dual\"."),
       
   160     'ipmi_target_address': _("destination address for bridged request. "
       
   161                              "Required only if ipmi_bridging is set "
       
   162                              "to \"single\" or \"dual\"."),
   149     'ipmi_local_address': _("local IPMB address for bridged requests. "
   163     'ipmi_local_address': _("local IPMB address for bridged requests. "
   150                             "Used only if ipmi_bridging is set "
   164                             "Used only if ipmi_bridging is set "
   151                             "to \"single\" or \"dual\". Optional."),
   165                             "to \"single\" or \"dual\". Optional."),
   152     'ipmi_priv_level':
   166     'ipmi_protocol_version': _('the version of the IPMI protocol; default '
   153         _("privilege level; default is ADMINISTRATOR. "
   167                                'is "2.0". One of "1.5", "2.0". Optional.'),
   154           "One of %s. Optional.") % '. '.join(ipmitool.VALID_PRIV_LEVELS),
   168     'ipmi_force_boot_device': _("Whether Ironic should specify the boot "
   155     'ipmi_target_address': _("destination address for bridged request. "
   169                                 "device to the BMC each time the server "
   156                              "Required only if ipmi_bridging is set "
   170                                 "is turned on, eg. because the BMC is not "
   157                              "to \"single\" or \"dual\"."),
   171                                 "capable of remembering the selected boot "
   158     'ipmi_target_channel': _("destination channel for bridged request. "
   172                                 "device across power cycles; default value "
   159                              "Required only if ipmi_bridging is set to "
   173                                 "is False. Optional.")
   160                              "\"single\" or \"dual\"."),
       
   161     'ipmi_transit_address': _("transit address for bridged request. Required "
       
   162                               "only if ipmi_bridging is set to \"dual\"."),
       
   163     'ipmi_transit_channel': _("transit channel for bridged request. Required "
       
   164                               "only if ipmi_bridging is set to \"dual\"."),
       
   165     'publishers': _("List of IPS publishers to install from, in the format "
       
   166                     "name@origin. Required if fmri property is set."),
       
   167     'sc_profiles': _("List of system configuration profiles to be applied "
       
   168                      "to an installed system. Optional.")
       
   169 }
   174 }
   170 
   175 
   171 COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
   176 COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
   172 COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
   177 COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
   173 
   178 
   184 ]
   189 ]
   185 
   190 
   186 CPU_LOCATION = '/System/Processors/CPUs/CPU_%d/total_cores'
   191 CPU_LOCATION = '/System/Processors/CPUs/CPU_%d/total_cores'
   187 
   192 
   188 LAST_CMD_TIME = {}
   193 LAST_CMD_TIME = {}
   189 TIMING_SUPPORT = None
       
   190 SINGLE_BRIDGE_SUPPORT = None
       
   191 DUAL_BRIDGE_SUPPORT = None
       
   192 
   194 
   193 
   195 
   194 def _ssh_execute(ssh_obj, ssh_cmd, raise_exception=True, err_msg=None):
   196 def _ssh_execute(ssh_obj, ssh_cmd, raise_exception=True, err_msg=None):
   195     """Execute a command via SSH.
   197     """Execute a command via SSH.
   196 
   198 
   206     returncode = 0
   208     returncode = 0
   207     stdout = None
   209     stdout = None
   208     try:
   210     try:
   209         stdout = processutils.ssh_execute(ssh_obj, ssh_cmd)[0]
   211         stdout = processutils.ssh_execute(ssh_obj, ssh_cmd)[0]
   210     except Exception as err:
   212     except Exception as err:
   211         LOG.debug(_("Cannot execute SSH cmd %(cmd)s. Reason: %(err)s.") %
   213         LOG.error(_LE("Cannot execute SSH cmd %(cmd)s. Reason: %(err)s.") %
   212                   {'cmd': ssh_cmd, 'err': err})
   214                   {'cmd': ssh_cmd, 'err': err})
   213         returncode = 1
   215         returncode = 1
   214         if raise_exception:
   216         if raise_exception:
   215             if err_msg:
   217             if err_msg:
   216                 raise SolarisIPMIError(msg=err_msg)
   218                 raise SolarisIPMIError(msg=err_msg)
   235     LOG.debug("_parse_driver_info()")
   237     LOG.debug("_parse_driver_info()")
   236     info = node.driver_info or {}
   238     info = node.driver_info or {}
   237     bridging_types = ['single', 'dual']
   239     bridging_types = ['single', 'dual']
   238     missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)]
   240     missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)]
   239     if missing_info:
   241     if missing_info:
   240         raise exception.MissingParameterValue(
   242         raise exception.MissingParameterValue(_(
   241             _("The following IPMI credentials are not supplied"
   243             "Missing the following IPMI credentials in node's"
   242               " to IPMI driver: %s.") % missing_info)
   244             " driver_info: %s.") % missing_info)
   243 
   245 
   244     address = info.get('ipmi_address')
   246     address = info.get('ipmi_address')
   245     username = info.get('ipmi_username')
   247     username = info.get('ipmi_username')
   246     password = info.get('ipmi_password')
   248     password = six.text_type(info.get('ipmi_password', ''))
       
   249     dest_port = info.get('ipmi_port')
   247     port = info.get('ipmi_terminal_port')
   250     port = info.get('ipmi_terminal_port')
   248     priv_level = info.get('ipmi_priv_level', 'ADMINISTRATOR')
   251     priv_level = info.get('ipmi_priv_level', 'ADMINISTRATOR')
   249     bridging_type = info.get('ipmi_bridging', 'no')
   252     bridging_type = info.get('ipmi_bridging', 'no')
   250     local_address = info.get('ipmi_local_address')
   253     local_address = info.get('ipmi_local_address')
   251     transit_channel = info.get('ipmi_transit_channel')
   254     transit_channel = info.get('ipmi_transit_channel')
   252     transit_address = info.get('ipmi_transit_address')
   255     transit_address = info.get('ipmi_transit_address')
   253     target_channel = info.get('ipmi_target_channel')
   256     target_channel = info.get('ipmi_target_channel')
   254     target_address = info.get('ipmi_target_address')
   257     target_address = info.get('ipmi_target_address')
   255 
   258     protocol_version = str(info.get('ipmi_protocol_version', '2.0'))
   256     if port:
   259     force_boot_device = info.get('ipmi_force_boot_device', False)
   257         try:
   260 
   258             port = int(port)
   261     if not username:
   259         except ValueError:
   262         LOG.warning(_LW('ipmi_username is not defined or empty for node %s: '
   260             raise exception.InvalidParameterValue(_(
   263                         'NULL user will be utilized.') % node.uuid)
   261                 "IPMI terminal port is not an integer."))
   264     if not password:
       
   265         LOG.warning(_LW('ipmi_password is not defined or empty for node %s: '
       
   266                         'NULL password will be utilized.') % node.uuid)
       
   267 
       
   268     if protocol_version not in ipmitool.VALID_PROTO_VERSIONS:
       
   269         valid_versions = ', '.join(ipmitool.VALID_PROTO_VERSIONS)
       
   270         raise exception.InvalidParameterValue(_(
       
   271             "Invalid IPMI protocol version value %(version)s, the valid "
       
   272             "value can be one of %(valid_versions)s") %
       
   273             {'version': protocol_version, 'valid_versions': valid_versions})
       
   274 
       
   275     if port is not None:
       
   276         port = utils.validate_network_port(port, 'ipmi_terminal_port')
       
   277 
       
   278     if dest_port is not None:
       
   279         dest_port = utils.validate_network_port(dest_port, 'ipmi_port')
   262 
   280 
   263     # check if ipmi_bridging has proper value
   281     # check if ipmi_bridging has proper value
   264     if bridging_type == 'no':
   282     if bridging_type == 'no':
   265         # if bridging is not selected, then set all bridging params to None
   283         # if bridging is not selected, then set all bridging params to None
   266         local_address = transit_channel = transit_address = \
   284         (local_address, transit_channel, transit_address, target_channel,
   267             target_channel = target_address = None
   285          target_address) = (None,) * 5
   268     elif bridging_type in bridging_types:
   286     elif bridging_type in bridging_types:
   269         # check if the particular bridging option is supported on host
   287         # check if the particular bridging option is supported on host
   270         if not ipmitool._is_option_supported('%s_bridge' % bridging_type):
   288         if not ipmitool._is_option_supported('%s_bridge' % bridging_type):
   271             raise exception.InvalidParameterValue(_(
   289             raise exception.InvalidParameterValue(_(
   272                 "Value for ipmi_bridging is provided as %s, but IPMI "
   290                 "Value for ipmi_bridging is provided as %s, but IPMI "
   307             " can be one of %(valid_levels)s") %
   325             " can be one of %(valid_levels)s") %
   308             {'priv_level': priv_level, 'valid_levels': valid_priv_lvls})
   326             {'priv_level': priv_level, 'valid_levels': valid_priv_lvls})
   309 
   327 
   310     return {
   328     return {
   311         'address': address,
   329         'address': address,
       
   330         'dest_port': dest_port,
   312         'username': username,
   331         'username': username,
   313         'password': password,
   332         'password': password,
   314         'port': port,
   333         'port': port,
   315         'uuid': node.uuid,
   334         'uuid': node.uuid,
   316         'priv_level': priv_level,
   335         'priv_level': priv_level,
   317         'local_address': local_address,
   336         'local_address': local_address,
   318         'transit_channel': transit_channel,
   337         'transit_channel': transit_channel,
   319         'transit_address': transit_address,
   338         'transit_address': transit_address,
   320         'target_channel': target_channel,
   339         'target_channel': target_channel,
   321         'target_address': target_address
   340         'target_address': target_address,
   322         }
   341         'protocol_version': protocol_version,
       
   342         'force_boot_device': force_boot_device
       
   343     }
   323 
   344 
   324 
   345 
   325 def _exec_ipmitool(driver_info, command):
   346 def _exec_ipmitool(driver_info, command):
   326     """Execute the ipmitool command.
   347     """Execute the ipmitool command.
   327 
   348 
   342     :raises: processutils.ProcessExecutionError from executing the command.
   363     :raises: processutils.ProcessExecutionError from executing the command.
   343 
   364 
   344     """
   365     """
   345     LOG.debug("SolarisDeploy._exec_ipmitool:driver_info: '%s', "
   366     LOG.debug("SolarisDeploy._exec_ipmitool:driver_info: '%s', "
   346               "command: '%s'" % (driver_info, command))
   367               "command: '%s'" % (driver_info, command))
       
   368     ipmi_version = ('lanplus'
       
   369                     if driver_info['protocol_version'] == '2.0'
       
   370                     else 'lan')
   347     args = ['/usr/sbin/ipmitool',
   371     args = ['/usr/sbin/ipmitool',
   348             '-I',
   372             '-I',
   349             'lanplus',
   373             ipmi_version,
   350             '-H',
   374             '-H',
   351             driver_info['address'],
   375             driver_info['address'],
   352             '-L', driver_info['priv_level']
   376             '-L', driver_info['priv_level']
   353             ]
   377             ]
       
   378 
       
   379     if driver_info['dest_port']:
       
   380         args.append('-p')
       
   381         args.append(driver_info['dest_port'])
       
   382 
   354     if driver_info['username']:
   383     if driver_info['username']:
   355         args.append('-U')
   384         args.append('-U')
   356         args.append(driver_info['username'])
   385         args.append(driver_info['username'])
   357 
   386 
   358     for name, option in ipmitool.BRIDGING_OPTIONS:
   387     for name, option in ipmitool.BRIDGING_OPTIONS:
   376     while True:
   405     while True:
   377         num_tries = num_tries - 1
   406         num_tries = num_tries - 1
   378         # NOTE(deva): ensure that no communications are sent to a BMC more
   407         # NOTE(deva): ensure that no communications are sent to a BMC more
   379         #             often than once every min_command_interval seconds.
   408         #             often than once every min_command_interval seconds.
   380         time_till_next_poll = CONF.ipmi.min_command_interval - (
   409         time_till_next_poll = CONF.ipmi.min_command_interval - (
   381                 time.time() - LAST_CMD_TIME.get(driver_info['address'], 0))
   410             time.time() - LAST_CMD_TIME.get(driver_info['address'], 0))
   382         if time_till_next_poll > 0:
   411         if time_till_next_poll > 0:
   383             time.sleep(time_till_next_poll)
   412             time.sleep(time_till_next_poll)
   384         # Resetting the list that will be utilized so the password arguments
   413         # Resetting the list that will be utilized so the password arguments
   385         # from any previous execution are preserved.
   414         # from any previous execution are preserved.
   386         cmd_args = args[:]
   415         cmd_args = args[:]
   397                 out, err = utils.execute(*cmd_args)
   426                 out, err = utils.execute(*cmd_args)
   398                 return out, err
   427                 return out, err
   399             except processutils.ProcessExecutionError as e:
   428             except processutils.ProcessExecutionError as e:
   400                 with excutils.save_and_reraise_exception() as ctxt:
   429                 with excutils.save_and_reraise_exception() as ctxt:
   401                     err_list = [x for x in ipmitool.IPMITOOL_RETRYABLE_FAILURES
   430                     err_list = [x for x in ipmitool.IPMITOOL_RETRYABLE_FAILURES
   402                                 if x in e.message]
   431                                 if x in six.text_type(e)]
   403                     if ((time.time() > end_time) or
   432                     if ((time.time() > end_time) or
   404                         (num_tries == 0) or
   433                         (num_tries == 0) or
   405                             not err_list):
   434                             not err_list):
   406                         LOG.error(_LE('IPMI Error while attempting '
   435                         LOG.error(_LE('IPMI Error while attempting '
   407                                       '"%(cmd)s" for node %(node)s. '
   436                                       '"%(cmd)s" for node %(node)s. '
   441         driver_info = _parse_driver_info(node)
   470         driver_info = _parse_driver_info(node)
   442         try:
   471         try:
   443             cpu_arch, _err = _exec_ipmitool(driver_info, ipmi_cmd_args)
   472             cpu_arch, _err = _exec_ipmitool(driver_info, ipmi_cmd_args)
   444         except Exception as err:
   473         except Exception as err:
   445             LOG.error(_LE("Failed to get node architecture from IPMI : %s" %
   474             LOG.error(_LE("Failed to get node architecture from IPMI : %s" %
   446                           (err)))
   475                       (err)))
   447             raise exception.IPMIFailure(cmd=err)
   476             raise exception.IPMIFailure(cmd=err)
   448 
   477 
   449         propdict = {'cpu_arch': cpu_arch}
   478         propdict = {'cpu_arch': cpu_arch}
   450         node_properties = node.properties
   479         node_properties = node.properties
   451         node_properties.update(propdict)
   480         node_properties.update(propdict)
   482     """
   511     """
   483     LOG.debug("_check_deploy_state()")
   512     LOG.debug("_check_deploy_state()")
   484     LOG.debug("_check_deploy_state() deploy_thread_state: %s" %
   513     LOG.debug("_check_deploy_state() deploy_thread_state: %s" %
   485               (deploy_thread.state))
   514               (deploy_thread.state))
   486 
   515 
   487     # Get DB instance
       
   488     mydbapi = dbapi.get_instance()
       
   489     try:
   516     try:
   490         # Get current DB copy of node
   517         # Get current DB copy of node
   491         cur_node = mydbapi.get_node_by_uuid(node_uuid)
   518         cur_node = objects.Node.get_by_uuid(task.context, node_uuid)
   492     except exception.NodeNotFound:
   519     except exception.NodeNotFound:
   493         LOG.info(_("During check_deploy_state, node %(node)s was not "
   520         LOG.info(_LI("During check_deploy_state, node %(node)s was not "
   494                    "found and presumed deleted by another process.") %
   521                      "found and presumed deleted by another process.") %
   495                  {'node': node_uuid})
   522                  {'node': node_uuid})
   496         # Thread should have stopped already, but let's make sure.
   523         # Thread should have stopped already, but let's make sure.
   497         deploy_thread.stop()
   524         deploy_thread.stop()
   498         if deploy_thread.state in [states.DEPLOYING, states.DEPLOYWAIT]:
   525         if deploy_thread.state in [states.DEPLOYING, states.DEPLOYWAIT]:
   499             # Update node with done/fail state
   526             # Update node with done/fail state
   504                                        (task.node.target_provision_state)
   531                                        (task.node.target_provision_state)
   505                 task.node.target_provision_state = states.NOSTATE
   532                 task.node.target_provision_state = states.NOSTATE
   506                 task.node.save()
   533                 task.node.save()
   507         raise loopingcall.LoopingCallDone()
   534         raise loopingcall.LoopingCallDone()
   508     except Exception as err:
   535     except Exception as err:
   509         LOG.info(_("During check_deploy_state, node %(node)s could "
   536         LOG.info(_LI("During check_deploy_state, node %(node)s could "
   510                    "not be retrieved: %(err)s") %
   537                      "not be retrieved: %(err)s") %
   511                  {'node': node_uuid, 'err': err})
   538                  {'node': node_uuid, 'err': err})
   512         # Thread should have stopped already, but lets make sure.
   539         # Thread should have stopped already, but lets make sure.
   513         deploy_thread.stop()
   540         deploy_thread.stop()
   514         if deploy_thread.state in [states.DEPLOYING, states.DEPLOYWAIT]:
   541         if deploy_thread.state in [states.DEPLOYING, states.DEPLOYWAIT]:
   515             # Update node with done/fail state
   542             # Update node with done/fail state
   536 
   563 
   537         # Update node with done/fail state
   564         # Update node with done/fail state
   538         if deploy_thread.state == states.DEPLOYDONE:
   565         if deploy_thread.state == states.DEPLOYDONE:
   539             cur_node.provision_state = states.ACTIVE
   566             cur_node.provision_state = states.ACTIVE
   540         elif deploy_thread.state == states.DEPLOYFAIL:
   567         elif deploy_thread.state == states.DEPLOYFAIL:
   541             cur_node.last_error = "Install failed; check install.log for " + \
   568             if deploy_thread.error is not None:
   542                                   "more details."
   569                 cur_node.last_error = deploy_thread.error
       
   570             else:
       
   571                 cur_node.last_error = "Install failed; check install.log " + \
       
   572                                       "for more details."
   543             cur_node.provision_state = deploy_thread.state
   573             cur_node.provision_state = deploy_thread.state
   544         else:
   574         else:
   545             cur_node.provision_state = deploy_thread.state
   575             cur_node.provision_state = deploy_thread.state
   546         cur_node.target_provision_state = states.NOSTATE
   576         cur_node.target_provision_state = states.NOSTATE
   547         cur_node.save()
   577         cur_node.save()
   564                   (cur_node.target_provision_state))
   594                   (cur_node.target_provision_state))
   565         deploy_thread.stop()
   595         deploy_thread.stop()
   566         raise loopingcall.LoopingCallDone()
   596         raise loopingcall.LoopingCallDone()
   567 
   597 
   568     elif cur_node.provision_state == states.DEPLOYFAIL:
   598     elif cur_node.provision_state == states.DEPLOYFAIL:
   569         # Node deployment as for some reason failed already, exist thread
   599         # Node deployment has for some reason failed already, exit thread
   570         LOG.debug("_check_deploy_state().deploy_failed: %s" %
   600         LOG.debug("_check_deploy_state().deploy_failed: %s" %
   571                   (cur_node.provision_state))
   601                   (cur_node.provision_state))
   572         deploy_thread.stop()
   602         deploy_thread.stop()
   573         raise loopingcall.LoopingCallDone()
   603         raise loopingcall.LoopingCallDone()
   574 
   604 
   635     LOG.debug("_image_refcount_adjust: image_path: %s, "
   665     LOG.debug("_image_refcount_adjust: image_path: %s, "
   636               "count: %s" % (image_path, str(count)))
   666               "count: %s" % (image_path, str(count)))
   637 
   667 
   638     if count == 0:
   668     if count == 0:
   639         # Adjusting by zero makes no sense just return
   669         # Adjusting by zero makes no sense just return
   640         err_msg = _("Zero reference count adjustment attempted "
   670         err_msg = _LE("Zero reference count adjustment attempted "
   641                     "on file: %s") % (image_path)
   671                       "on file: %s") % (image_path)
   642         LOG.error(err_msg)
   672         LOG.error(err_msg)
   643         raise SolarisIPMIError(msg=err_msg)
   673         raise SolarisIPMIError(msg=err_msg)
   644 
   674 
   645     ref_filename = image_path + ".ref"
   675     ref_filename = image_path + ".ref"
   646 
   676 
   647     if not os.path.exists(ref_filename):
   677     if not os.path.exists(ref_filename):
   648         if count < 0:
   678         if count < 0:
   649             # Cannot decrement reference on non-existent file
   679             # Cannot decrement reference on non-existent file
   650             err_msg = _("Negative reference count adjustment attempted on "
   680             err_msg = _LE("Negative reference count adjustment attempted on "
   651                         "non-existent file: %s") % (image_path)
   681                           "non-existent file: %s") % (image_path)
   652             LOG.error(err_msg)
   682             LOG.error(err_msg)
   653             raise SolarisIPMIError(msg=err_msg)
   683             raise SolarisIPMIError(msg=err_msg)
   654 
   684 
   655         # Create reference count file
   685         # Create reference count file
   656         with open(ref_filename, "w") as fp:
   686         with open(ref_filename, "w") as fp:
   657             fp.write("0")
   687             fp.write("0")
   658 
   688 
   659     # Acquire lock on refcount file
   689     # Acquire lock on refcount file
   660     lock = _image_refcount_acquire_lock(image_path)
   690     lock = _image_refcount_acquire_lock(image_path)
   661     if lock is None:
   691     if lock is None:
   662         err_msg = _("Failed to acquire lock on image: %s") % (image_path)
   692         err_msg = _LE("Failed to acquire lock on image: %s") % (image_path)
   663         LOG.error(err_msg)
   693         LOG.error(err_msg)
   664         raise SolarisIPMIError(msg=err_msg)
   694         raise SolarisIPMIError(msg=err_msg)
   665 
   695 
   666     with open(ref_filename, "r+") as fp:
   696     with open(ref_filename, "r+") as fp:
   667         ref_count = fp.readline()
   697         ref_count = fp.readline()
   721 
   751 
   722                     # Release acquired lock now that file is retrieved
   752                     # Release acquired lock now that file is retrieved
   723                     lock.release()
   753                     lock.release()
   724 
   754 
   725                 except Exception as err:
   755                 except Exception as err:
   726                     LOG.error(_("Unable to fetch Glance image: id %s: %s")
   756                     LOG.error(_LE("Unable to fetch Glance image: id %s: %s")
   727                               % (url.netloc, err))
   757                               % (url.netloc, err))
   728                     raise
   758                     raise
   729             else:
   759             else:
   730                 # Increase reference count for this image
   760                 # Increase reference count for this image
   731                 _image_refcount_adjust(temp_uri, 1)
   761                 _image_refcount_adjust(temp_uri, 1)
   761 
   791 
   762                         # Release acquired lock now that file is retrieved
   792                         # Release acquired lock now that file is retrieved
   763                         lock.release()
   793                         lock.release()
   764 
   794 
   765                     except Exception as err:
   795                     except Exception as err:
   766                         LOG.error(_("Unable to fetch image: id %s: %s")
   796                         LOG.error(_LE("Unable to fetch image: id %s: %s")
   767                                   % (url.netloc, err))
   797                                   % (url.netloc, err))
   768                         raise
   798                         raise
   769                 else:
   799                 else:
   770                     # Increase reference count for this image
   800                     # Increase reference count for this image
   771                     _image_refcount_adjust(temp_uri, 1)
   801                     _image_refcount_adjust(temp_uri, 1)
   772     except Exception as err:
   802     except Exception as err:
   773         # Only remove the temporary file if exception occurs
   803         # Only remove the temporary file if exception occurs
   774         # as noted above Caller is responsible for its removal
   804         # as noted above Caller is responsible for its removal
   775         LOG.error(_("Unable to fetch image: uri %s: %s") % (uri, err))
   805         LOG.error(_LE("Unable to fetch image: uri %s: %s") % (uri, err))
   776         if url.scheme == "glance":
   806         if url.scheme == "glance":
   777             _image_refcount_adjust(temp_uri, -1)
   807             _image_refcount_adjust(temp_uri, -1)
   778         else:
   808         else:
   779             try:
   809             try:
   780                 os.remove(temp_uri)
   810                 os.remove(temp_uri)
  1239     try:
  1269     try:
  1240         # scp temp file to AI Server
  1270         # scp temp file to AI Server
  1241         remote_file = os.path.join("/tmp", obj_name) + ".xml"
  1271         remote_file = os.path.join("/tmp", obj_name) + ".xml"
  1242         aiservice.copy_remote_file(temp_file, remote_file)
  1272         aiservice.copy_remote_file(temp_file, remote_file)
  1243     except Exception as err:
  1273     except Exception as err:
  1244         LOG.error(_("Fetch and create failed for %s: name: %s: %s") %
  1274         LOG.error(_LE("Fetch and create failed for %s: name: %s: %s") %
  1245                   (obj_type, obj_uri, err))
  1275                   (obj_type, obj_uri, err))
  1246         if url.scheme == "glance":
  1276         if url.scheme == "glance":
  1247             _image_refcount_adjust(temp_file, -1)
  1277             _image_refcount_adjust(temp_file, -1)
  1248         else:
  1278         else:
  1249             os.remove(temp_file)
  1279             os.remove(temp_file)
  1282         Thread.__init__(self)
  1312         Thread.__init__(self)
  1283 
  1313 
  1284         self.task = task
  1314         self.task = task
  1285         self.node = task.node
  1315         self.node = task.node
  1286         self._state = states.DEPLOYWAIT
  1316         self._state = states.DEPLOYWAIT
       
  1317         self._error = None
  1287         self.ssh_connection = None
  1318         self.ssh_connection = None
  1288         self.running = True
  1319         self.running = True
  1289 
  1320 
  1290     @property
  1321     @property
  1291     def state(self):
  1322     def state(self):
  1292         """Deployment state property"""
  1323         """Deployment state property"""
  1293         return self._state
  1324         return self._state
  1294 
  1325 
       
  1326     @property
       
  1327     def error(self):
       
  1328         """Deployment error property"""
       
  1329         return self._error
       
  1330 
  1295     def run(self):
  1331     def run(self):
  1296         """Start the thread """
  1332         """Start the thread """
  1297         LOG.debug("DeployStateChecker.run(): Connecting...")
  1333         LOG.debug("DeployStateChecker.run(): Connecting...")
  1298         client = utils.ssh_connect(self._get_ssh_dict())
  1334         # Occasionally SSH connection fails, make three attempts
       
  1335         # before returning failure.
       
  1336         connection_attempt = 0
       
  1337         while (connection_attempt < 3):
       
  1338             try:
       
  1339                 connection_attempt += 1
       
  1340                 client = utils.ssh_connect(self._get_ssh_dict())
       
  1341                 break
       
  1342             except SSHException as err:
       
  1343                 if connection_attempt < 3:
       
  1344                     continue
       
  1345                 else:
       
  1346                     self._state = states.DEPLOYFAIL
       
  1347                     self._error = str(err)
       
  1348                     self.stop()
       
  1349                     return
       
  1350         else:
       
  1351             self._state = states.DEPLOYFAIL
       
  1352             self._error = "Failed to establish SSH Connection with node."
       
  1353             self.stop()
       
  1354             return
       
  1355 
  1299         channel = client.invoke_shell()
  1356         channel = client.invoke_shell()
  1300         channel.settimeout(0.0)
  1357         channel.settimeout(0.0)
  1301         channel.set_combine_stderr(True)
  1358         channel.set_combine_stderr(True)
  1302 
  1359 
  1303         # Continuously read stdout from console and parse
  1360         # Continuously read stdout from console and parse
  1823             aiservice = AIService(task, ai_service)
  1880             aiservice = AIService(task, ai_service)
  1824 
  1881 
  1825         # Check if AI Service exists, log message if already removed
  1882         # Check if AI Service exists, log message if already removed
  1826         if not aiservice.exists:
  1883         if not aiservice.exists:
  1827             # There is nothing to clean up as service removed
  1884             # There is nothing to clean up as service removed
  1828             LOG.info(_("AI Service %s already removed.") % (aiservice.name))
  1885             LOG.info(_LI("AI Service %s already removed.") % (aiservice.name))
  1829         else:
  1886         else:
  1830             for mac in driver_utils.get_node_mac_addresses(task):
  1887             for mac in driver_utils.get_node_mac_addresses(task):
  1831                 # 1. Delete AI Client for this MAC Address
  1888                 # 1. Delete AI Client for this MAC Address
  1832                 if mac.lower() in aiservice.clients:
  1889                 if mac.lower() in aiservice.clients:
  1833                     aiservice.delete_client(mac)
  1890                     aiservice.delete_client(mac)
  1877         except OSError:
  1934         except OSError:
  1878             raise exception.DriverLoadError(
  1935             raise exception.DriverLoadError(
  1879                 driver=self.__class__.__name__,
  1936                 driver=self.__class__.__name__,
  1880                 reason=_("Unable to locate usable ipmitool command in "
  1937                 reason=_("Unable to locate usable ipmitool command in "
  1881                          "the system path when checking ipmitool version"))
  1938                          "the system path when checking ipmitool version"))
       
  1939         ipmitool._check_temp_dir()
  1882 
  1940 
  1883     def validate(self, task):
  1941     def validate(self, task):
  1884         """Check that 'driver_info' contains IPMI credentials.
  1942         """Check that 'driver_info' contains IPMI credentials.
  1885 
  1943 
  1886         Validates whether the 'driver_info' property of the supplied
  1944         Validates whether the 'driver_info' property of the supplied
  1911             if cpu_arch == 'x86':
  1969             if cpu_arch == 'x86':
  1912                 return [boot_devices.PXE, boot_devices.DISK,
  1970                 return [boot_devices.PXE, boot_devices.DISK,
  1913                         boot_devices.CDROM, boot_devices.BIOS,
  1971                         boot_devices.CDROM, boot_devices.BIOS,
  1914                         boot_devices.SAFE]
  1972                         boot_devices.SAFE]
  1915             elif cpu_arch == 'SPARC':
  1973             elif cpu_arch == 'SPARC':
  1916                 return [boot_devices.DISK, 'wanboot']
  1974                 return [boot_devices.DISK, boot_devices.WANBOOT]
  1917             else:
  1975             else:
  1918                 raise exception.InvalidParameterValue(
  1976                 raise exception.InvalidParameterValue(
  1919                     _("Invalid node architecture of '%s'.") % (cpu_arch))
  1977                     _("Invalid node architecture of '%s'.") % (cpu_arch))
  1920 
  1978 
  1921     @task_manager.require_exclusive_lock
  1979     @task_manager.require_exclusive_lock
  1940         cpu_arch = _get_node_architecture(task.node)
  1998         cpu_arch = _get_node_architecture(task.node)
  1941         archive_uri = task.node.driver_info.get('archive_uri')
  1999         archive_uri = task.node.driver_info.get('archive_uri')
  1942         publishers = task.node.driver_info.get('publishers')
  2000         publishers = task.node.driver_info.get('publishers')
  1943         fmri = task.node.driver_info.get('fmri')
  2001         fmri = task.node.driver_info.get('fmri')
  1944 
  2002 
       
  2003         if task.node.driver_info.get('ipmi_force_boot_device', False):
       
  2004             driver_utils.force_persistent_boot(task,
       
  2005                                                device,
       
  2006                                                persistent)
       
  2007             # Reset persistent to False, in case of BMC does not support
       
  2008             # persistent or we do not have admin rights.
       
  2009             persistent = False
       
  2010 
  1945         if cpu_arch == 'x86':
  2011         if cpu_arch == 'x86':
  1946             if device not in self.get_supported_boot_devices(task=task):
  2012             if device not in self.get_supported_boot_devices(task=task):
  1947                 raise exception.InvalidParameterValue(_(
  2013                 raise exception.InvalidParameterValue(_(
  1948                     "Invalid boot device %s specified.") % device)
  2014                     "Invalid boot device %s specified.") % device)
  1949             cmd = ["chassis", "bootdev", device]
  2015             cmd = ["chassis", "bootdev", device]
  1950             if persistent:
  2016             if persistent:
  1951                 cmd.append("options=persistent")
  2017                 cmd.append("options=persistent")
  1952         elif cpu_arch == 'SPARC':
  2018         elif cpu_arch == 'SPARC':
  1953             # Set bootmode script to network DHCP or disk
  2019             # Set bootmode script to network DHCP or disk
  1954             if device == 'wanboot':
  2020             if device == boot_devices.WANBOOT:
  1955                 boot_cmd = 'set /HOST/bootmode script="'
  2021                 boot_cmd = 'set /HOST/bootmode script="'
  1956                 script_str = 'boot net:dhcp - install'
  2022                 script_str = 'boot net:dhcp - install'
  1957                 if archive_uri:
  2023                 if archive_uri:
  1958                     new_uri, auth_token = _format_archive_uri(task,
  2024                     new_uri, auth_token = _format_archive_uri(task,
  1959                                                               archive_uri)
  2025                                                               archive_uri)
  2023             :persistent: Whether the boot device will persist to all
  2089             :persistent: Whether the boot device will persist to all
  2024                 future boots or not, None if it is unknown.
  2090                 future boots or not, None if it is unknown.
  2025 
  2091 
  2026         """
  2092         """
  2027         LOG.debug("SolarisManagement.get_boot_device")
  2093         LOG.debug("SolarisManagement.get_boot_device")
       
  2094         driver_info = task.node.driver_info
       
  2095         driver_internal_info = task.node.driver_internal_info
       
  2096 
       
  2097         if (driver_info.get('ipmi_force_boot_device', False) and
       
  2098                 driver_internal_info.get('persistent_boot_device') and
       
  2099                 driver_internal_info.get('is_next_boot_persistent', True)):
       
  2100             return {
       
  2101                 'boot_device': driver_internal_info['persistent_boot_device'],
       
  2102                 'persistent': True
       
  2103             }
       
  2104 
  2028         cpu_arch = _get_node_architecture(task.node)
  2105         cpu_arch = _get_node_architecture(task.node)
  2029         driver_info = _parse_driver_info(task.node)
  2106         driver_info = _parse_driver_info(task.node)
  2030         response = {'boot_device': None, 'persistent': None}
  2107         response = {'boot_device': None, 'persistent': None}
  2031 
  2108 
  2032         if cpu_arch == 'x86':
  2109         if cpu_arch == 'x86':
  2065                     response['boot_device'] = boot_devices.CDROM
  2142                     response['boot_device'] = boot_devices.CDROM
  2066 
  2143 
  2067             response['persistent'] = 'Options apply to all future boots' in out
  2144             response['persistent'] = 'Options apply to all future boots' in out
  2068         elif cpu_arch == 'SPARC':
  2145         elif cpu_arch == 'SPARC':
  2069             if "net:dhcp" in out:
  2146             if "net:dhcp" in out:
  2070                 response['boot_device'] = 'wanboot'
  2147                 response['boot_device'] = boot_devices.WANBOOT
  2071             else:
  2148             else:
  2072                 response['boot_device'] = boot_devices.DISK
  2149                 response['boot_device'] = boot_devices.DISK
  2073         LOG.debug(response)
  2150         LOG.debug(response)
  2074         return response
  2151         return response
  2075 
  2152 
  2139                    for line in ipmi_props.strip().splitlines()]
  2216                    for line in ipmi_props.strip().splitlines()]
  2140         propdict = dict(zip(keys, vallist))
  2217         propdict = dict(zip(keys, vallist))
  2141 
  2218 
  2142         # Installed memory size is returned in GB, Nova assumes this is MB
  2219         # Installed memory size is returned in GB, Nova assumes this is MB
  2143         # so convert if returned in GB
  2220         # so convert if returned in GB
  2144         memsize, memtype = propdict['memory_mb'].split(' ')
  2221         try:
  2145         if memtype == 'GB':
  2222             size_bytes = strutils.string_to_bytes(
  2146             propdict['memory_mb'] = int(memsize) * 1024
  2223                 propdict['memory_mb'].replace(' ', ''))
       
  2224             propdict['memory_mb'] = int(size_bytes / float(1 << 20))
       
  2225         except ValueError:
       
  2226             # Size conversion failed, just ensure value is an int
       
  2227             propdict['memory_mb'] = int(propdict['memory_mb'])
       
  2228 
       
  2229         if propdict['local_gb'] == 'Not Available':
       
  2230             del propdict['local_gb']
  2147         else:
  2231         else:
  2148             propdict['memory_mb'] = int(memsize)
  2232             # Local disk size can be returned with size type identifier
       
  2233             # remove identifier as this needs to be an int value
       
  2234             try:
       
  2235                 size_bytes = strutils.string_to_bytes(
       
  2236                     propdict['local_gb'].replace(' ', ''))
       
  2237                 propdict['local_gb'] = int(size_bytes / float(1 << 30))
       
  2238             except ValueError:
       
  2239                 # Size conversion failed, just ensure value is an int
       
  2240                 propdict['local_gb'] = int(propdict['local_gb'])
  2149 
  2241 
  2150         cpu_props = self._get_cpu_cores(task, propdict['cpus'])
  2242         cpu_props = self._get_cpu_cores(task, propdict['cpus'])
  2151 
  2243 
  2152         vallist = [line.split(': ')[1]
  2244         vallist = [line.split(': ')[1]
  2153                    for line in cpu_props.strip().splitlines()]
  2245                    for line in cpu_props.strip().splitlines()]