def get_deploy_info(node, address, iqn, port=None, lun='1'): """Returns the information required for doing iSCSI deploy in a dictionary. :param node: ironic node object :param address: iSCSI address :param iqn: iSCSI iqn for the target disk :param port: iSCSI port, defaults to one specified in the configuration :param lun: iSCSI lun, defaults to '1' :raises: MissingParameterValue, if some required parameters were not passed. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ i_info = deploy_utils.parse_instance_info(node) params = { 'address': address, 'port': port or CONF.iscsi.portal_port, 'iqn': iqn, 'lun': lun, 'image_path': _get_image_file_path(node.uuid), 'node_uuid': node.uuid } is_whole_disk_image = node.driver_internal_info['is_whole_disk_image'] if not is_whole_disk_image: params.update({ 'root_mb': i_info['root_mb'], 'swap_mb': i_info['swap_mb'], 'ephemeral_mb': i_info['ephemeral_mb'], 'preserve_ephemeral': i_info['preserve_ephemeral'], 'boot_option': deploy_utils.get_boot_option(node), 'boot_mode': _get_boot_mode(node) }) # Append disk label if specified disk_label = deploy_utils.get_disk_label(node) if disk_label is not None: params['disk_label'] = disk_label missing = [key for key in params if params[key] is None] if missing: raise exception.MissingParameterValue( _("Parameters %s were not passed to ironic" " for deploy.") % missing) # configdrive is nullable params['configdrive'] = i_info.get('configdrive') if is_whole_disk_image: return params # ephemeral_format is nullable params['ephemeral_format'] = i_info.get('ephemeral_format') return params
def set_indicator_state(self, task, component, indicator, state): """Set indicator on the hardware component to the desired state. :param task: A task from TaskManager. :param component: The hardware component, one of :mod:`ironic.common.components`. :param indicator: Indicator ID (as reported by `get_supported_indicators`). :param state: Desired state of the indicator, one of :mod:`ironic.common.indicator_states`. :raises: InvalidParameterValue if an invalid component, indicator or state is specified. :raises: MissingParameterValue if a required parameter is missing :raises: RedfishError on an error from the Sushy library """ system = redfish_utils.get_system(task.node) try: if (component == components.SYSTEM and indicator == system.uuid): system.set_indicator_led(INDICATOR_MAP_REV[state]) return elif (component == components.CHASSIS and system.chassis): for chassis in system.chassis: if chassis.uuid == indicator: chassis.set_indicator_led(INDICATOR_MAP_REV[state]) return elif (component == components.DISK and system.simple_storage and system.simple_storage.drives): for drive in system.simple_storage.drives: if drive.uuid == indicator: drive.set_indicator_led(INDICATOR_MAP_REV[state]) return except sushy.exceptions.SushyError as e: error_msg = (_('Redfish set %(component)s indicator %(indicator)s ' 'state %(state)s failed for node %(node)s. Error: ' '%(error)s') % { 'component': component, 'indicator': indicator, 'state': state, 'node': task.node.uuid, 'error': e }) LOG.error(error_msg) raise exception.RedfishError(error=error_msg) raise exception.MissingParameterValue( _("Unknown indicator %(indicator)s for component %(component)s of " "node %(uuid)s") % { 'indicator': indicator, 'component': component, 'uuid': task.node.uuid })
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ if CONF.agent.manage_agent_boot: task.driver.boot.validate(task) node = task.node # Validate node capabilities deploy_utils.validate_capabilities(node) if not task.driver.storage.should_write_image(task): # NOTE(TheJulia): There is no reason to validate # image properties if we will not be writing an image # in a boot from volume case. As such, return to the caller. return params = {} image_source = node.instance_info.get('image_source') params['instance_info.image_source'] = image_source error_msg = _('Node %s failed to validate deploy image info. Some ' 'parameters were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get('image_checksum'): raise exception.MissingParameterValue( _("image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid) check_image_size(task, image_source) # Validate the root device hints try: root_device = node.properties.get('root_device') il_utils.parse_root_device_hints(root_device) except ValueError as e: raise exception.InvalidParameterValue( _('Failed to validate the root device hints for node ' '%(node)s. Error: %(error)s') % { 'node': node.uuid, 'error': e }) validate_image_proxies(node)
def _validate_common(self, task): node = task.node if not driver_utils.get_node_mac_addresses(task): raise exception.MissingParameterValue( _("Node %s does not have any port associated with it.") % node.uuid) if self.ipxe_enabled: if not CONF.deploy.http_url or not CONF.deploy.http_root: raise exception.MissingParameterValue(_( "iPXE boot is enabled but no HTTP URL or HTTP " "root was specified.")) # Check the trusted_boot capabilities value. deploy_utils.validate_capabilities(node) if deploy_utils.is_trusted_boot_requested(node): # Check if 'boot_option' and boot mode is compatible with # trusted boot. if self.ipxe_enabled: # NOTE(TheJulia): So in theory (huge theory here, not put to # practice or tested), that one can define the kernel as tboot # and define the actual kernel and ramdisk as appended data. # Similar to how one can iPXE load the XEN hypervisor. # tboot mailing list seem to indicate pxe/ipxe support, or # more specifically avoiding breaking the scenarios of use, # but there is also no definitive documentation on the subject. LOG.warning('Trusted boot has been requested for %(node)s in ' 'concert with iPXE. This is not a supported ' 'configuration for an ironic deployment.', {'node': node.uuid}) pxe_utils.validate_boot_parameters_for_trusted_boot(node) # Check if we have invalid parameters being passed which will not work # for ramdisk configurations. if (node.instance_info.get('image_source') and node.instance_info.get('boot_iso')): raise exception.InvalidParameterValue(_( "An 'image_source' and 'boot_iso' parameter may not be " "specified at the same time.")) pxe_utils.parse_driver_info(node)
def get_deploy_info(node, **kwargs): """Returns the information required for doing iSCSI deploy in a dictionary. :param node: ironic node object :param kwargs: the keyword args passed from the conductor node. :raises: MissingParameterValue, if some required parameters were not passed. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ deploy_key = kwargs.get('key') i_info = deploy_utils.parse_instance_info(node) if i_info['deploy_key'] != deploy_key: raise exception.InvalidParameterValue(_("Deploy key does not match")) params = { 'address': kwargs.get('address'), 'port': kwargs.get('port', CONF.iscsi.portal_port), 'iqn': kwargs.get('iqn'), 'lun': kwargs.get('lun', '1'), 'image_path': _get_image_file_path(node.uuid), 'node_uuid': node.uuid } is_whole_disk_image = node.driver_internal_info['is_whole_disk_image'] if not is_whole_disk_image: params.update({ 'root_mb': 1024 * int(i_info['root_gb']), 'swap_mb': int(i_info['swap_mb']), 'ephemeral_mb': 1024 * int(i_info['ephemeral_gb']), 'preserve_ephemeral': i_info['preserve_ephemeral'], 'boot_option': deploy_utils.get_boot_option(node), 'boot_mode': _get_boot_mode(node) }) # Append disk label if specified disk_label = deploy_utils.get_disk_label(node) if disk_label is not None: params['disk_label'] = disk_label missing = [key for key in params if params[key] is None] if missing: raise exception.MissingParameterValue( _("Parameters %s were not passed to ironic" " for deploy.") % missing) if is_whole_disk_image: return params # configdrive and ephemeral_format are nullable params['ephemeral_format'] = i_info.get('ephemeral_format') params['configdrive'] = i_info.get('configdrive') return params
def _test_create_configuration_skip_root_skip_non_root( self, ilo_mock, filter_target_raid_config_mock): ilo_mock_object = ilo_mock.return_value with task_manager.acquire(self.context, self.node.uuid) as task: msg = "Node %s has no target RAID configuration" % self.node.uuid filter_target_raid_config_mock.side_effect = ( exception.MissingParameterValue(msg)) self.assertRaises(exception.MissingParameterValue, task.driver.raid.create_configuration, task, False, False) self.assertFalse(ilo_mock_object.create_raid_configuration.called)
def _parse_driver_info(node): """Parses and creates seamicro driver info :param node: An Ironic node object. :returns: SeaMicro driver info. :raises: MissingParameterValue if any required parameters are missing. :raises: InvalidParameterValue if required parameter are invalid. """ info = node.driver_info or {} missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)] if missing_info: raise exception.MissingParameterValue( _("SeaMicro driver requires the following parameters to be set in" " node's driver_info: %s.") % missing_info) api_endpoint = info.get('seamicro_api_endpoint') username = info.get('seamicro_username') password = info.get('seamicro_password') server_id = info.get('seamicro_server_id') api_version = info.get('seamicro_api_version', "2") port = info.get('seamicro_terminal_port') if port: try: port = int(port) except ValueError: raise exception.InvalidParameterValue( _("SeaMicro terminal port is not an integer.")) r = re.compile(r"(^[0-9]+)/([0-9]+$)") if not r.match(server_id): raise exception.InvalidParameterValue( _("Invalid 'seamicro_server_id' parameter in node's " "driver_info. Expected format of 'seamicro_server_id' " "is <int>/<int>")) url = urlparse.urlparse(api_endpoint) if (not (url.scheme == "http") or not url.netloc): raise exception.InvalidParameterValue( _("Invalid 'seamicro_api_endpoint' parameter in node's " "driver_info.")) res = { 'username': username, 'password': password, 'api_endpoint': api_endpoint, 'server_id': server_id, 'api_version': api_version, 'uuid': node.uuid, 'port': port } return res
def _parse_parameters(task): driver_info = task.node.driver_info host = driver_info.get('wol_host', '255.255.255.255') port = driver_info.get('wol_port', 9) port = utils.validate_network_port(port, 'wol_port') if len(task.ports) < 1: raise exception.MissingParameterValue( _('Wake-On-Lan needs at least one port resource to be ' 'registered in the node')) return {'host': host, 'port': port}
def _get_ports_collection(self, node_ident, address, marker, limit, sort_key, sort_dir, resource_url=None, fields=None): if self.from_nodes and not node_ident: raise exception.MissingParameterValue( _("Node identifier not specified.")) limit = api_utils.validate_limit(limit) sort_dir = api_utils.validate_sort_dir(sort_dir) marker_obj = None if marker: marker_obj = objects.Port.get_by_uuid(pecan.request.context, marker) if sort_key in self.invalid_sort_key_list: raise exception.InvalidParameterValue( _("The sort_key value %(key)s is an invalid field for " "sorting") % {'key': sort_key}) if node_ident: # FIXME(comstud): Since all we need is the node ID, we can # make this more efficient by only querying # for that column. This will get cleaned up # as we move to the object interface. node = api_utils.get_rpc_node(node_ident) ports = objects.Port.list_by_node_id(pecan.request.context, node.id, limit, marker_obj, sort_key=sort_key, sort_dir=sort_dir) elif address: ports = self._get_ports_by_address(address) else: ports = objects.Port.list(pecan.request.context, limit, marker_obj, sort_key=sort_key, sort_dir=sort_dir) return PortCollection.convert_with_links(ports, limit, url=resource_url, fields=fields, sort_key=sort_key, sort_dir=sort_dir)
def _check_required_properties(driver_info): """Checks if all required properties are present. :param driver_info: the node's driver_info dict. :raises: MissingParameterValue if one or more required properties are missing. """ missing_properties = set(REQUIRED_PROPERTIES) - set(driver_info) if missing_properties: raise exception.MissingParameterValue( _('The following parameters were missing: %s') % ' '.join(missing_properties))
def validate(self, task): """Validate the Node console info. :param task: a task from TaskManager. :raises: InvalidParameterValue :raises: MissingParameterValue when a required parameter is missing """ driver_info = _parse_driver_info(task.node) if not driver_info['port']: raise exception.MissingParameterValue( _("IPMI terminal port not supplied to IPMI driver."))
def _validate_iscsi_connectors(self, task): """Validate if volume connectors are properly registered for iSCSI. For connecting a node to a iSCSI volume, volume connectors containing an IQN and an IP address are necessary. One of connectors must have a physical ID of the PCI card. Network size of a storage network is also required by iRMC. which should be registered in the node's driver_info. :param task: a TaskManager instance containing the node to act on. :raises: InvalidParameterValue if a volume connector with a required type is not registered. :raises: InvalidParameterValue if a physical ID is not registered in any volume connectors. :raises: InvalidParameterValue if a physical ID is invalid. """ vc_dict = self._get_volume_connectors_by_type(task) node = task.node missing_types = [] for vc_type in ('iqn', 'ip'): vc = vc_dict.get(vc_type) if not vc: missing_types.append(vc_type) if missing_types: raise exception.MissingParameterValue( _('Failed to validate for node %(node)s because of missing ' 'volume connector(s) with type(s) %(types)s') % { 'node': node.uuid, 'types': ', '.join(missing_types) }) if not self._get_connector_physical_id(task, ['iqn', 'ip']): raise exception.MissingParameterValue( _('Failed to validate for node %(node)s because of missing ' 'physical port information for iSCSI connector. This ' 'information must be set in "pci_physical_ids" parameter of ' 'node\'s driver_info as <connector uuid>:<physical id>.') % {'node': node.uuid}) self._get_network_size(node)
def parse_driver_info(node): """Gets the driver specific Node info. This method validates whether the 'driver_info' property of the supplied node contains the required information for this driver. :param node: an ironic Node object. :returns: a dict containing information from driver_info (or where applicable, config values). :raises: InvalidParameterValue if any parameters are incorrect :raises: MissingParameterValue if some mandatory information is missing on the node """ info = node.driver_info d_info = {} missing_info = [] for param in REQUIRED_PROPERTIES: try: d_info[param] = info[param] except KeyError: missing_info.append(param) if missing_info: raise exception.MissingParameterValue( _("The following required iLO parameters are missing from the " "node's driver_info: %s") % missing_info) not_integers = [] for param in OPTIONAL_PROPERTIES: value = info.get(param, CONF.ilo.get(param)) if param == "client_port": d_info[param] = utils.validate_network_port(value, param) else: try: d_info[param] = int(value) except ValueError: not_integers.append(param) for param in CONSOLE_PROPERTIES: value = info.get(param) if value: # Currently there's only "console_port" parameter # in CONSOLE_PROPERTIES if param == "console_port": d_info[param] = utils.validate_network_port(value, param) if not_integers: raise exception.InvalidParameterValue( _("The following iLO parameters from the node's driver_info " "should be integers: %s") % not_integers) return d_info
def parse_driver_info(node): """Parse the information required for Ironic to connect to iBMC. :param node: an Ironic node object :returns: dictionary of parameters :raises: InvalidParameterValue on malformed parameter(s) :raises: MissingParameterValue on missing parameter(s) """ driver_info = node.driver_info or {} missing_info = [key for key in REQUIRED_PROPERTIES if not driver_info.get(key)] if missing_info: raise exception.MissingParameterValue(_( 'Missing the following iBMC properties in node ' '%(node)s driver_info: %(info)s') % {'node': node.uuid, 'info': missing_info}) # Validate the iBMC address address = driver_info['ibmc_address'] if '://' not in address: address = 'https://%s' % address parsed = netutils.urlsplit(address) if not parsed.netloc: raise exception.InvalidParameterValue( _('Invalid iBMC address %(address)s set in ' 'driver_info/ibmc_address on node %(node)s') % {'address': address, 'node': node.uuid}) # Check if verify_ca is a Boolean or a file/directory in the file-system verify_ca = driver_info.get('ibmc_verify_ca', True) if isinstance(verify_ca, str): if not os.path.exists(verify_ca): try: verify_ca = strutils.bool_from_string(verify_ca, strict=True) except ValueError: raise exception.InvalidParameterValue( _('Invalid value type set in driver_info/' 'ibmc_verify_ca on node %(node)s. ' 'The value should be a Boolean or the path ' 'to a file/directory, not "%(value)s"' ) % {'value': verify_ca, 'node': node.uuid}) elif not isinstance(verify_ca, bool): raise exception.InvalidParameterValue( _('Invalid value type set in driver_info/ibmc_verify_ca ' 'on node %(node)s. The value should be a Boolean or the path ' 'to a file/directory, not "%(value)s"') % {'value': verify_ca, 'node': node.uuid}) return {'address': address, 'username': driver_info.get('ibmc_username'), 'password': driver_info.get('ibmc_password'), 'verify_ca': verify_ca}
def validate(self, task): """Validate the Node console info. :param task: a TaskManager instance containing the node to act on. :raises: MissingParameterValue when required IPMI credentials or the IPMI terminal port are missing :raises: InvalidParameterValue when the IPMI terminal port is not an integer. """ driver_info = _parse_driver_info(task.node) if not driver_info['port']: raise exception.MissingParameterValue(_( "IPMI terminal port not supplied to the IPMI driver."))
def _verify_node_info(node_namespace, node_info_dict, info_required): """Verify if info_required is present in node_namespace.""" missing_keys = set(info_required) - set(node_info_dict) if missing_keys: raise exception.MissingParameterValue( _("Missing the keys for the following OneView data in node's " "%(namespace)s: %(missing_keys)s.") % {'namespace': node_namespace, 'missing_keys': ', '.join(missing_keys) } ) # False and 0 can still be considered as valid values missing_values_keys = [k for k in info_required if node_info_dict[k] in ('', None)] if missing_values_keys: missing_keys = ["%s:%s" % (node_namespace, k) for k in missing_values_keys] raise exception.MissingParameterValue( _("Missing parameter value for: '%s'") % "', '".join(missing_keys) )
def validate(self, task): info = task.node.instance_info for item in REQUIRED_PROPERTIES: if not info.get(item): error_msg = _( "Cannot validate driver deploy. Some parameters were missing" " in node's instance_info") exc_msg = _("%(error_msg)s. Missing are: %(missing_info)s") raise exception.MissingParameterValue(exc_msg % { 'error_msg': error_msg, 'missing_info': item })
def validate(self, task): """Validate the Node console info. :param task: a task from TaskManager. :raises: MissingParameterValue if required seamicro parameters are missing :raises: InvalidParameterValue if required parameter are invalid. """ driver_info = _parse_driver_info(task.node) if not driver_info['port']: raise exception.MissingParameterValue( _("Missing 'seamicro_terminal_port' parameter in node's " "driver_info"))
def test_clean_up_fail_get_image_info(self, mock_image_info, mock_cache, mock_pxe_clean, mock_iscsi_clean, mock_unlink): mock_image_info.side_effect = exception.MissingParameterValue('foo') with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: task.driver.deploy.clean_up(task) mock_image_info.assert_called_once_with(task.node, task.context) mock_pxe_clean.assert_called_once_with(task) mock_unlink.assert_called_once_with( pxe._get_token_file_path(task.node.uuid)) mock_iscsi_clean.assert_called_once_with(task.node.uuid) mock_cache.return_value.clean_up.assert_called_once_with()
def parse_driver_info(node): """Gets the driver specific Node deployment info. This method validates whether the 'driver_info' property of the supplied node contains the required information for this driver. :param node: an ironic node object. :returns: a dict containing information from driver_info and default values. :raises: InvalidParameterValue on invalid inputs. :raises: MissingParameterValue if some mandatory information is missing on the node """ info = node.driver_info d_info = {} error_msgs = [] for param in REQUIRED_PROPERTIES: try: d_info[param] = info[param] except KeyError: error_msgs.append(_("'%s' not supplied to IloDriver.") % param) if error_msgs: msg = (_("The following parameters were mising while parsing " "driver_info:\n%s") % "\n".join(error_msgs)) raise exception.MissingParameterValue(msg) for param in OPTIONAL_PROPERTIES: value = info.get(param, CONF.ilo.get(param)) try: value = int(value) except ValueError: error_msgs.append(_("'%s' is not an integer.") % param) continue d_info[param] = value for param in CONSOLE_PROPERTIES: value = info.get(param) if value: try: value = int(value) d_info[param] = value except ValueError: error_msgs.append(_("'%s' is not an integer.") % param) if error_msgs: msg = (_("The following errors were encountered while parsing " "driver_info:\n%s") % "\n".join(error_msgs)) raise exception.InvalidParameterValue(msg) return d_info
def validate(self, task): """Validate the deployment information for the task's node. :param task: a TaskManager instance containing the node to act on. :raises: InvalidParameterValue. :raises: MissingParameterValue """ node = task.node # Check the boot_mode and boot_option capabilities values. deploy_utils.validate_capabilities(node) boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node) if CONF.pxe.ipxe_enabled: if not CONF.pxe.http_url or not CONF.pxe.http_root: raise exception.MissingParameterValue( _("iPXE boot is enabled but no HTTP URL or HTTP " "root was specified.")) # iPXE and UEFI should not be configured together. if boot_mode == 'uefi': LOG.error( _LE("UEFI boot mode is not supported with " "iPXE boot enabled.")) raise exception.InvalidParameterValue( _("Conflict: iPXE is enabled, but cannot be used with node" "%(node_uuid)s configured to use UEFI boot") % {'node_uuid': node.uuid}) # Check if 'boot_option' is compatible with 'boot_mode' of uefi and # image being deployed if boot_mode == 'uefi': validate_boot_option_for_uefi(task.node) if deploy_utils.is_trusted_boot_requested(task.node): # Check if 'boot_option' and boot mode is compatible with # trusted boot. validate_boot_parameters_for_trusted_boot(task.node) d_info = _parse_deploy_info(node) iscsi_deploy.validate(task) if node.driver_internal_info.get('is_whole_disk_image'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] iscsi_deploy.validate_image_properties(task.context, d_info, props)
def driver_validate(self, method, **kwargs): """Validate the driver deployment info. :param method: method to be validated. """ version = kwargs.get('version') if not version: raise exception.MissingParameterValue(_('Missing parameter ' 'version')) if version not in self.supported_payload_versions: raise exception.InvalidParameterValue(_('Unknown lookup ' 'payload version: %s') % version)
def _get_nodes_collection(self, chassis_uuid, instance_uuid, associated, maintenance, marker, limit, sort_key, sort_dir, expand=False, resource_url=None): if self.from_chassis and not chassis_uuid: raise exception.MissingParameterValue( _("Chassis id not specified.")) limit = api_utils.validate_limit(limit) sort_dir = api_utils.validate_sort_dir(sort_dir) marker_obj = None if marker: marker_obj = objects.Node.get_by_uuid(pecan.request.context, marker) if instance_uuid: nodes = self._get_nodes_by_instance(instance_uuid) else: filters = {} if chassis_uuid: filters['chassis_uuid'] = chassis_uuid if associated is not None: filters['associated'] = associated if maintenance is not None: filters['maintenance'] = maintenance nodes = objects.Node.list(pecan.request.context, limit, marker_obj, sort_key=sort_key, sort_dir=sort_dir, filters=filters) parameters = {'sort_key': sort_key, 'sort_dir': sort_dir} if associated: parameters['associated'] = associated if maintenance: parameters['maintenance'] = maintenance return NodeCollection.convert_with_links(nodes, limit, url=resource_url, expand=expand, **parameters)
def _validate_temp_url_config(self): """Validate the required settings for a temporary URL.""" if not CONF.glance.swift_temp_url_key: raise exc.MissingParameterValue(_( 'Swift temporary URLs require a shared secret to be created. ' 'You must provide "swift_temp_url_key" as a config option.')) if not CONF.glance.swift_endpoint_url: raise exc.MissingParameterValue(_( 'Swift temporary URLs require a Swift endpoint URL. ' 'You must provide "swift_endpoint_url" as a config option.')) if not CONF.glance.swift_account: raise exc.MissingParameterValue(_( 'Swift temporary URLs require a Swift account string. ' 'You must provide "swift_account" as a config option.')) if CONF.glance.swift_temp_url_duration < 0: raise exc.InvalidParameterValue(_( '"swift_temp_url_duration" must be a positive integer.')) seed_num_chars = CONF.glance.swift_store_multiple_containers_seed if (seed_num_chars is None or seed_num_chars < 0 or seed_num_chars > 32): raise exc.InvalidParameterValue(_( "An integer value between 0 and 32 is required for" " swift_store_multiple_containers_seed."))
def validate(self, task): """Validate the driver-specific Node deployment info. This method validates whether the properties of the supplied node contain the required information for this driver to deploy images to the node. :param task: a TaskManager instance :raises: MissingParameterValue, if any of the required parameters are missing. :raises: InvalidParameterValue, if any of the parameters have invalid value. """ if CONF.agent.manage_agent_boot: task.driver.boot.validate(task) node = task.node params = {} image_source = node.instance_info.get('image_source') params['instance_info.image_source'] = image_source error_msg = _('Node %s failed to validate deploy image info. Some ' 'parameters were missing') % node.uuid deploy_utils.check_for_missing_params(params, error_msg) if not service_utils.is_glance_image(image_source): if not node.instance_info.get('image_checksum'): raise exception.MissingParameterValue( _("image_source's image_checksum must be provided in " "instance_info for node %s") % node.uuid) check_image_size(task, image_source) is_whole_disk_image = node.driver_internal_info.get( 'is_whole_disk_image') # TODO(sirushtim): Remove once IPA has support for partition images. if is_whole_disk_image is False: raise exception.InvalidParameterValue( _("Node %(node)s is configured to use the %(driver)s driver " "which currently does not support deploying partition " "images.") % { 'node': node.uuid, 'driver': node.driver }) # Validate the root device hints deploy_utils.parse_root_device_hints(node) # Validate node capabilities deploy_utils.validate_capabilities(node) validate_image_proxies(node)
def validate(self, task): # TODO(jroll) validate we can put images in /httpboot? # validate image, kernel, ramdisk as UUID node = task.node missing_keys = [] for key in SECUREBOOT_PROPERTIES: if not node.driver_info.get(key): missing_keys.append(key) if missing_keys: raise ironic_exc.MissingParameterValue( 'Node %s is missing secureboot configuration data %s' % (node.uuid, missing_keys))
def get_indicator_state(self, task, component, indicator): """Get current state of the indicator of the hardware component. :param task: A task from TaskManager. :param component: The hardware component, one of :mod:`ironic.common.components`. :param indicator: Indicator ID (as reported by `get_supported_indicators`). :raises: MissingParameterValue if a required parameter is missing :raises: RedfishError on an error from the Sushy library :returns: Current state of the indicator, one of :mod:`ironic.common.indicator_states`. """ system = redfish_utils.get_system(task.node) try: if (component == components.SYSTEM and indicator == system.uuid): return INDICATOR_MAP[system.indicator_led] if (component == components.CHASSIS and system.chassis): for chassis in system.chassis: if chassis.uuid == indicator: return INDICATOR_MAP[chassis.indicator_led] if (component == components.DISK and system.simple_storage and system.simple_storage.drives): for drive in system.simple_storage.drives: if drive.uuid == indicator: return INDICATOR_MAP[drive.indicator_led] except sushy.exceptions.SushyError as e: error_msg = (_('Redfish get %(component)s indicator %(indicator)s ' 'state failed for node %(node)s. Error: ' '%(error)s') % { 'component': component, 'indicator': indicator, 'node': task.node.uuid, 'error': e }) LOG.error(error_msg) raise exception.RedfishError(error=error_msg) raise exception.MissingParameterValue( _("Unknown indicator %(indicator)s for component %(component)s of " "node %(uuid)s") % { 'indicator': indicator, 'component': component, 'uuid': task.node.uuid })
def _validate_common(self, task): node = task.node if not driver_utils.get_node_mac_addresses(task): raise exception.MissingParameterValue( _("Node %s does not have any port associated with it.") % node.uuid) # TODO(TheJulia): Once ipxe support is remove from the pxe # interface, this can be removed. if CONF.pxe.ipxe_enabled: if (not CONF.deploy.http_url or not CONF.deploy.http_root): raise exception.MissingParameterValue( _("iPXE boot is enabled but no HTTP URL or HTTP " "root was specified.")) # Check the trusted_boot capabilities value. deploy_utils.validate_capabilities(node) if deploy_utils.is_trusted_boot_requested(node): # Check if 'boot_option' and boot mode is compatible with # trusted boot. validate_boot_parameters_for_trusted_boot(node) pxe_utils.parse_driver_info(node)
def heartbeat(self, task, **kwargs): """Method for agent to periodically check in. The agent should be sending its agent_url (so Ironic can talk back) as a kwarg. kwargs should have the following format:: { 'agent_url': 'http://AGENT_HOST:AGENT_PORT' } AGENT_PORT defaults to 9999. """ node = task.node driver_info = node.driver_info LOG.debug( 'Heartbeat from %(node)s, last heartbeat at %(heartbeat)s.', { 'node': node.uuid, 'heartbeat': driver_info.get('agent_last_heartbeat') }) driver_info['agent_last_heartbeat'] = int(_time()) try: driver_info['agent_url'] = kwargs['agent_url'] except KeyError: raise exception.MissingParameterValue( _('For heartbeat operation, ' '"agent_url" must be ' 'specified.')) node.driver_info = driver_info node.save() # Async call backs don't set error state on their own # TODO(jimrollenhagen) improve error messages here msg = _('Failed checking if deploy is done.') try: if node.provision_state == states.DEPLOYWAIT: msg = _('Node failed to get image for deploy.') self._continue_deploy(task, **kwargs) elif (node.provision_state == states.DEPLOYING and self._deploy_is_done(node)): msg = _('Node failed to move to active state.') self._reboot_to_instance(task, **kwargs) except Exception: LOG.exception(_LE('Async exception for %(node)s: %(msg)s'), { 'node': node, 'msg': msg }) deploy_utils.set_failed_state(task, msg)
def _get_route(self, method): """Return the driver interface which contains the given method. :param method: The name of the vendor method. """ if not method: raise exception.MissingParameterValue( _("Method not specified when calling vendor extension.")) try: route = self.mapping[method] except KeyError: raise exception.InvalidParameterValue( _('No handler for method %s') % method) return route