def get_disk_label(node): """Return the disk label requested for deploy, if any. :param node: a single Node. :raises: InvalidParameterValue if the capabilities string is not a dictionary or is malformed. :returns: the disk label or None if no disk label was specified. """ capabilities = utils.parse_instance_info_capabilities(node) return capabilities.get('disk_label')
def get_boot_option(node): """Gets the boot option. :param node: A single Node. :raises: InvalidParameterValue if the capabilities string is not a dict or is malformed. :returns: A string representing the boot option type. Defaults to 'netboot'. """ # NOTE(TheJulia): Software raid always implies local deployment if is_software_raid(node): return 'local' capabilities = utils.parse_instance_info_capabilities(node) return capabilities.get('boot_option', get_default_boot_option()).lower()
def is_trusted_boot_requested(node): """Returns True if trusted_boot is requested for deploy. This method checks instance property for trusted_boot and returns True if it is requested. :param node: a single Node. :raises: InvalidParameterValue if the capabilities string is not a dictionary or is malformed. :returns: True if trusted_boot is requested. """ capabilities = common_utils.parse_instance_info_capabilities(node) trusted_boot = capabilities.get('trusted_boot', 'false').lower() return trusted_boot == 'true'
def validate_capabilities(node): """Validates that specified supported capabilities have valid value This method checks if the any of the supported capability is present in Node capabilities. For all supported capabilities specified for a Node, it validates that it has a valid value. The node can have capability as part of the 'properties' or 'instance_info' or both. Note that the actual value of a capability does not need to be the same in the node's 'properties' and 'instance_info'. :param node: an ironic node object. :raises: InvalidParameterValue, if the capability is not set to a valid value. """ exp_str = _("The parameter '%(capability)s' from %(field)s has an " "invalid value: '%(value)s'. Acceptable values are: " "%(valid_values)s.") for capability_name, valid_values in SUPPORTED_CAPABILITIES.items(): # Validate capability_name in node's properties/capabilities value = driver_utils.get_node_capability(node, capability_name) if value and (value not in valid_values): field = "properties/capabilities" raise exception.InvalidParameterValue( exp_str % { 'capability': capability_name, 'field': field, 'value': value, 'valid_values': ', '.join(valid_values) }) # Validate capability_name in node's instance_info/['capabilities'] capabilities = utils.parse_instance_info_capabilities(node) value = capabilities.get(capability_name) if value and (value not in valid_values): field = "instance_info['capabilities']" raise exception.InvalidParameterValue( exp_str % { 'capability': capability_name, 'field': field, 'value': value, 'valid_values': ', '.join(valid_values) })
def get_boot_mode_for_deploy(node): """Returns the boot mode that would be used for deploy. This method returns boot mode to be used for deploy. It returns 'uefi' if 'secure_boot' is set to 'true' or returns 'bios' if 'trusted_boot' is set to 'true' in 'instance_info/capabilities' of node. Otherwise it returns value of 'boot_mode' in 'properties/capabilities' of node if set. If that is not set, it returns boot mode in 'internal_driver_info/deploy_boot_mode' for the node. If that is not set, it returns boot mode in 'instance_info/deploy_boot_mode' for the node. It would return None if boot mode is present neither in 'capabilities' of node 'properties' nor in node's 'internal_driver_info' nor in node's 'instance_info' (which could also be None). :param node: an ironic node object. :returns: 'bios', 'uefi' or None :raises: InvalidParameterValue, if the node boot mode disagrees with the boot mode set to node properties/capabilities """ if is_secure_boot_requested(node): LOG.debug('Deploy boot mode is uefi for %s.', node.uuid) return 'uefi' if is_trusted_boot_requested(node): # TODO(lintan) Trusted boot also supports uefi, but at the moment, # it should only boot with bios. LOG.debug('Deploy boot mode is bios for %s.', node.uuid) return 'bios' # NOTE(etingof): # The search for a boot mode should be in the priority order: # # 1) instance_info.capabilities # 2) instance_info.deploy_boot_mode (deprecated in Wallaby) # 3) properties.capabilities # 4) driver_internal_info.deploy_boot_mode (internal) # # Because: # # (1) and (2) are deleted during teardown # (4) will never be touched if node properties/capabilities # are still present. # (3) becomes operational default as the last resort inst_boot_mode = ( common_utils.parse_instance_info_capabilities(node).get('boot_mode')) cap_boot_mode = driver_utils.get_node_capability(node, 'boot_mode') old_boot_mode = node.instance_info.get('deploy_boot_mode') if old_boot_mode: LOG.warning( 'Using instance_info/deploy_boot_mode is deprecated, ' 'please use instance_info/capabilities with boot mode ' 'for node %s', node.uuid) boot_mode = (inst_boot_mode or old_boot_mode or cap_boot_mode or node.driver_internal_info.get('deploy_boot_mode')) if not boot_mode: return boot_mode = boot_mode.lower() # NOTE(etingof): # Make sure that the ultimate boot_mode agrees with the one set to # node properties/capabilities. This locks down node to use only # boot mode specified in properties/capabilities. # TODO(etingof): this logic will have to go away when we switch to traits if cap_boot_mode: cap_boot_mode = cap_boot_mode.lower() if cap_boot_mode != boot_mode: msg = (_("Node %(uuid)s boot mode %(boot_mode)s violates " "node properties/capabilities %(caps)s") % { 'uuid': node.uuid, 'boot_mode': boot_mode, 'caps': cap_boot_mode }) LOG.error(msg) raise exception.InvalidParameterValue(msg) LOG.debug('Deploy boot mode is %(boot_mode)s for %(node)s.', { 'boot_mode': boot_mode, 'node': node.uuid }) return boot_mode
def prepare(self, task): """Prepare the deployment environment for this node. :param task: a TaskManager instance. :raises: NetworkError: if the previous cleaning ports cannot be removed or if new cleaning ports cannot be created. :raises: InvalidParameterValue when the wrong power state is specified or the wrong driver info is specified for power management. :raises: StorageError If the storage driver is unable to attach the configured volumes. :raises: other exceptions by the node's power driver if something wrong occurred during the power action. :raises: exception.ImageRefValidationFailed if image_source is not Glance href and is not HTTP(S) URL. :raises: exception.InvalidParameterValue if network validation fails. :raises: any boot interface's prepare_ramdisk exceptions. """ def _update_instance_info(): node.instance_info = ( deploy_utils.build_instance_info_for_deploy(task)) node.save() node = task.node deploy_utils.populate_storage_driver_internal_info(task) if node.provision_state == states.DEPLOYING: # Validate network interface to ensure that it supports boot # options configured on the node. try: task.driver.network.validate(task) except exception.InvalidParameterValue: # For 'neutron' network interface validation will fail # if node is using 'netboot' boot option while provisioning # a whole disk image. Updating 'boot_option' in node's # 'instance_info' to 'local for backward compatibility. # TODO(stendulker): Fail here once the default boot # option is local. # NOTE(TheJulia): Fixing the default boot mode only # masks the failure as the lack of a user definition # can be perceived as both an invalid configuration and # reliance upon the default configuration. The reality # being that in most scenarios, users do not want network # booting, so the changed default should be valid. with excutils.save_and_reraise_exception(reraise=False) as ctx: instance_info = node.instance_info capabilities = utils.parse_instance_info_capabilities(node) if 'boot_option' not in capabilities: capabilities['boot_option'] = 'local' instance_info['capabilities'] = capabilities node.instance_info = instance_info node.save() # Re-validate the network interface task.driver.network.validate(task) else: ctx.reraise = True # Determine if this is a fast track sequence fast_track_deploy = manager_utils.is_fast_track(task) if fast_track_deploy: # The agent has already recently checked in and we are # configured to take that as an indicator that we can # skip ahead. LOG.debug( 'The agent for node %(node)s has recently checked ' 'in, and the node power will remain unmodified.', {'node': task.node.uuid}) else: # Powering off node to setup networking for port and # ensure that the state is reset if it is inadvertently # on for any unknown reason. manager_utils.node_power_action(task, states.POWER_OFF) if task.driver.storage.should_write_image(task): # NOTE(vdrok): in case of rebuild, we have tenant network # already configured, unbind tenant ports if present if not fast_track_deploy: power_state_to_restore = ( manager_utils.power_on_node_if_needed(task)) task.driver.network.unconfigure_tenant_networks(task) task.driver.network.add_provisioning_network(task) if not fast_track_deploy: manager_utils.restore_power_state_if_needed( task, power_state_to_restore) else: # Fast track sequence in progress _update_instance_info() # Signal to storage driver to attach volumes task.driver.storage.attach_volumes(task) if (not task.driver.storage.should_write_image(task) or fast_track_deploy): # We have nothing else to do as this is handled in the # backend storage system, and we can return to the caller # as we do not need to boot the agent to deploy. # Alternatively, we could be in a fast track deployment # and again, we should have nothing to do here. return if node.provision_state in (states.ACTIVE, states.UNRESCUING): # Call is due to conductor takeover task.driver.boot.prepare_instance(task) elif node.provision_state != states.ADOPTING: if node.provision_state not in (states.RESCUING, states.RESCUEWAIT, states.RESCUE, states.RESCUEFAIL): _update_instance_info() if CONF.agent.manage_agent_boot: deploy_opts = deploy_utils.build_agent_options(node) task.driver.boot.prepare_ramdisk(task, deploy_opts)