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) pxe_utils.parse_driver_info(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) # 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 prepare(self, task): """Prepare the deployment environment for this task's node. Generates the TFTP configuration for PXE-booting both the deployment and user images, fetches the TFTP image from Glance and add it to the local cache. :param task: a TaskManager instance containing the node to act on. """ node = task.node # TODO(deva): optimize this if rerun on existing files if CONF.pxe.ipxe_enabled: # Copy the iPXE boot script to HTTP root directory bootfile_path = os.path.join( CONF.pxe.http_root, os.path.basename(CONF.pxe.ipxe_boot_script)) shutil.copyfile(CONF.pxe.ipxe_boot_script, bootfile_path) pxe_info = _get_image_info(node, task.context) pxe_options = _build_pxe_config_options(node, pxe_info, task.context) if deploy_utils.get_boot_mode_for_deploy(node) == 'uefi': pxe_config_template = CONF.pxe.uefi_pxe_config_template else: pxe_config_template = CONF.pxe.pxe_config_template pxe_utils.create_pxe_config(task, pxe_options, pxe_config_template) # FIXME(lucasagomes): If it's local boot we should not cache # the image kernel and ramdisk (Or even require it). _cache_ramdisk_kernel(task.context, node, pxe_info) # NOTE(deva): prepare may be called from conductor._do_takeover # in which case, it may need to regenerate the PXE config file for an # already-active deployment. if node.provision_state == states.ACTIVE: # this should have been stashed when the deploy was done # but let's guard, just in case it's missing iwdi = node.driver_internal_info.get('is_whole_disk_image') try: root_uuid_or_disk_id = ( node.driver_internal_info['root_uuid_or_disk_id']) except KeyError: if not iwdi: LOG.warn( _LW("The UUID for the root partition can't be " "found, unable to switch the pxe config from " "deployment mode to service (boot) mode for node " "%(node)s"), {"node": node.uuid}) else: LOG.warn( _LW("The disk id for the whole disk image can't " "be found, unable to switch the pxe config from " "deployment mode to service (boot) mode for " "node %(node)s"), {"node": node.uuid}) else: pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, deploy_utils.get_boot_mode_for_deploy(node), iwdi, deploy_utils.is_trusted_boot_requested(node))
def build_service_pxe_config(task, instance_image_info, root_uuid_or_disk_id, ramdisk_boot=False, ipxe_enabled=False): node = task.node pxe_config_path = get_pxe_config_file_path(node.uuid, ipxe_enabled=ipxe_enabled) # NOTE(pas-ha) if it is takeover of ACTIVE node or node performing # unrescue operation, first ensure that basic PXE configs and links # are in place before switching pxe config # NOTE(TheJulia): Also consider deploying a valid state to go ahead # and check things before continuing, as otherwise deployments can # fail if the agent was booted outside the direct actions of the # boot interface. if (node.provision_state in [states.ACTIVE, states.UNRESCUING, states.DEPLOYING] and not os.path.isfile(pxe_config_path)): pxe_options = build_pxe_config_options(task, instance_image_info, service=True, ipxe_enabled=ipxe_enabled) pxe_config_template = deploy_utils.get_pxe_config_template(node) create_pxe_config(task, pxe_options, pxe_config_template, ipxe_enabled=ipxe_enabled) iwdi = node.driver_internal_info.get('is_whole_disk_image') deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, boot_mode_utils.get_boot_mode(node), iwdi, deploy_utils.is_trusted_boot_requested(node), deploy_utils.is_iscsi_boot(task), ramdisk_boot, ipxe_enabled=ipxe_enabled)
def build_service_pxe_config(task, instance_image_info, root_uuid_or_disk_id, ramdisk_boot=False, ipxe_enabled=False): node = task.node pxe_config_path = get_pxe_config_file_path(node.uuid) # NOTE(pas-ha) if it is takeover of ACTIVE node or node performing # unrescue operation, first ensure that basic PXE configs and links # are in place before switching pxe config # NOTE(TheJulia): Also consider deploying a valid state to go ahead # and check things before continuing, as otherwise deployments can # fail if the agent was booted outside the direct actions of the # boot interface. if (node.provision_state in [states.ACTIVE, states.UNRESCUING, states.DEPLOYING] and not os.path.isfile(pxe_config_path)): pxe_options = build_pxe_config_options(task, instance_image_info, service=True, ipxe_enabled=ipxe_enabled) pxe_config_template = deploy_utils.get_pxe_config_template(node) create_pxe_config(task, pxe_options, pxe_config_template, ipxe_enabled=ipxe_enabled) iwdi = node.driver_internal_info.get('is_whole_disk_image') deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, boot_mode_utils.get_boot_mode(node), iwdi, deploy_utils.is_trusted_boot_requested(node), deploy_utils.is_iscsi_boot(task), ramdisk_boot, ipxe_enabled=ipxe_enabled)
def build_service_pxe_config(task, instance_image_info, root_uuid_or_disk_id, ramdisk_boot=False, ipxe_enabled=False): node = task.node pxe_config_path = get_pxe_config_file_path(node.uuid) # NOTE(pas-ha) if it is takeover of ACTIVE node or node performing # unrescue operation, first ensure that basic PXE configs and links # are in place before switching pxe config if (node.provision_state in [states.ACTIVE, states.UNRESCUING] and not os.path.isfile(pxe_config_path)): pxe_options = build_pxe_config_options(task, instance_image_info, service=True, ipxe_enabled=ipxe_enabled) pxe_config_template = deploy_utils.get_pxe_config_template(node) create_pxe_config(task, pxe_options, pxe_config_template, ipxe_enabled=ipxe_enabled) iwdi = node.driver_internal_info.get('is_whole_disk_image') deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, boot_mode_utils.get_boot_mode(node), iwdi, deploy_utils.is_trusted_boot_requested(node), deploy_utils.is_iscsi_boot(task), ramdisk_boot, ipxe_enabled=ipxe_enabled)
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.")) # NOTE(zer0c00l): When 'kickstart' boot option is used we need to store # kickstart and squashfs files in http_root directory. These files # will be eventually requested by anaconda installer during deployment # over http(s). if deploy_utils.get_boot_option(node) == 'kickstart': if not CONF.deploy.http_url or not CONF.deploy.http_root: raise exception.MissingParameterValue( _("'kickstart' boot option is set on the node but no HTTP " "URL or HTTP root was specified.")) if not CONF.anaconda.default_ks_template: raise exception.MissingParameterValue( _("'kickstart' boot option is set on the node but no " "default kickstart template is 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 validate(self, task): """Validate the PXE-specific info for booting deploy/instance images. This method validates the PXE-specific info for booting the ramdisk and instance on the node. If invalid, raises an exception; otherwise returns None. :param task: a task from TaskManager. :returns: None :raises: InvalidParameterValue, if some parameters are invalid. :raises: MissingParameterValue, if some required parameters are missing. """ 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 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. # 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.validate_boot_parameters_for_trusted_boot(node) pxe_utils.parse_driver_info(node) # NOTE(TheJulia): If we're not writing an image, we can skip # the remainder of this method. if (not task.driver.storage.should_write_image(task)): return d_info = deploy_utils.get_image_instance_info(node) if (node.driver_internal_info.get('is_whole_disk_image') or deploy_utils.get_boot_option(node) == 'local'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
def validate(self, task): """Validate the PXE-specific info for booting deploy/instance images. This method validates the PXE-specific info for booting the ramdisk and instance on the node. If invalid, raises an exception; otherwise returns None. :param task: a task from TaskManager. :returns: None :raises: InvalidParameterValue, if some parameters are invalid. :raises: MissingParameterValue, if some required parameters are missing. """ 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 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. # 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.validate_boot_parameters_for_trusted_boot(node) pxe_utils.parse_driver_info(node) # NOTE(TheJulia): If we're not writing an image, we can skip # the remainder of this method. if (not task.driver.storage.should_write_image(task)): return d_info = deploy_utils.get_image_instance_info(node) if (node.driver_internal_info.get('is_whole_disk_image') or deploy_utils.get_boot_option(node) == 'local'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
def validate(self, task): """Validate the PXE-specific info for booting deploy/instance images. This method validates the PXE-specific info for booting the ramdisk and instance on the node. If invalid, raises an exception; otherwise returns None. :param task: a task from TaskManager. :returns: None :raises: InvalidParameterValue, if some parameters are invalid. :raises: MissingParameterValue, if some required parameters are missing. """ 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) # Get the boot_mode capability value. boot_mode = deploy_utils.get_boot_mode_for_deploy(node) 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.")) # 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}) if boot_mode == 'uefi': validate_boot_option_for_uefi(node) # 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) _parse_driver_info(node) d_info = _parse_instance_info(node) 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'] deploy_utils.validate_image_properties(task.context, d_info, props)
def prepare_instance(self, task): """Prepares the boot of instance. This method prepares the boot of the instance after reading relevant information from the node's instance_info. In case of netboot, it updates the dhcp entries and switches the PXE config. In case of localboot, it cleans up the PXE config. :param task: a task from TaskManager. :returns: None """ node = task.node boot_option = deploy_utils.get_boot_option(node) if boot_option != "local": # Make sure that the instance kernel/ramdisk is cached. # This is for the takeover scenario for active nodes. instance_image_info = _get_instance_image_info( task.node, task.context) _cache_ramdisk_kernel(task.context, task.node, instance_image_info) # If it's going to PXE boot we need to update the DHCP server dhcp_opts = pxe_utils.dhcp_options_for_instance(task) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) iwdi = task.node.driver_internal_info.get('is_whole_disk_image') try: root_uuid_or_disk_id = task.node.driver_internal_info[ 'root_uuid_or_disk_id'] except KeyError: if not iwdi: LOG.warn( _LW("The UUID for the root partition can't be " "found, unable to switch the pxe config from " "deployment mode to service (boot) mode for " "node %(node)s"), {"node": task.node.uuid}) else: LOG.warn( _LW("The disk id for the whole disk image can't " "be found, unable to switch the pxe config " "from deployment mode to service (boot) mode " "for node %(node)s"), {"node": task.node.uuid}) else: pxe_config_path = pxe_utils.get_pxe_config_file_path( task.node.uuid) deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, deploy_utils.get_boot_mode_for_deploy(node), iwdi, deploy_utils.is_trusted_boot_requested(node)) else: # If it's going to boot from the local disk, we don't need # PXE config files. They still need to be generated as part # of the prepare() because the deployment does PXE boot the # deploy ramdisk pxe_utils.clean_up_pxe_config(task)
def prepare_instance(self, task): """Prepares the boot of instance. This method prepares the boot of the instance after reading relevant information from the node's instance_info. In case of netboot, it updates the dhcp entries and switches the PXE config. In case of localboot, it cleans up the PXE config. :param task: a task from TaskManager. :returns: None """ node = task.node boot_option = deploy_utils.get_boot_option(node) if boot_option != "local": # Make sure that the instance kernel/ramdisk is cached. # This is for the takeover scenario for active nodes. instance_image_info = _get_instance_image_info( task.node, task.context) _cache_ramdisk_kernel(task.context, task.node, instance_image_info) # If it's going to PXE boot we need to update the DHCP server dhcp_opts = pxe_utils.dhcp_options_for_instance(task) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) iwdi = task.node.driver_internal_info.get('is_whole_disk_image') try: root_uuid_or_disk_id = task.node.driver_internal_info[ 'root_uuid_or_disk_id' ] except KeyError: if not iwdi: LOG.warn(_LW("The UUID for the root partition can't be " "found, unable to switch the pxe config from " "deployment mode to service (boot) mode for " "node %(node)s"), {"node": task.node.uuid}) else: LOG.warn(_LW("The disk id for the whole disk image can't " "be found, unable to switch the pxe config " "from deployment mode to service (boot) mode " "for node %(node)s"), {"node": task.node.uuid}) else: pxe_config_path = pxe_utils.get_pxe_config_file_path( task.node.uuid) deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, deploy_utils.get_boot_mode_for_deploy(node), iwdi, deploy_utils.is_trusted_boot_requested(node)) else: # If it's going to boot from the local disk, we don't need # PXE config files. They still need to be generated as part # of the prepare() because the deployment does PXE boot the # deploy ramdisk pxe_utils.clean_up_pxe_config(task)
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 validate(self, task): """Validate the PXE-specific info for booting deploy/instance images. This method validates the PXE-specific info for booting the ramdisk and instance on the node. If invalid, raises an exception; otherwise returns None. :param task: a task from TaskManager. :returns: None :raises: InvalidParameterValue, if some parameters are invalid. :raises: MissingParameterValue, if some required parameters are missing. """ 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) # NOTE(TheJulia): If we're not writing an image, we can skip # the remainder of this method. if (not task.driver.storage.should_write_image(task)): return d_info = deploy_utils.get_image_instance_info(node) if (node.driver_internal_info.get('is_whole_disk_image') or deploy_utils.get_boot_option(node) == 'local'): props = [] elif service_utils.is_glance_image(d_info['image_source']): props = ['kernel_id', 'ramdisk_id'] else: props = ['kernel', 'ramdisk'] deploy_utils.validate_image_properties(task.context, d_info, props)
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 _build_service_pxe_config(task, instance_image_info, root_uuid_or_disk_id): node = task.node pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) # NOTE(pas-ha) if it is takeover of ACTIVE node, # first ensure that basic PXE configs and links # are in place before switching pxe config if (node.provision_state == states.ACTIVE and not os.path.isfile(pxe_config_path)): pxe_options = _build_pxe_config_options(task, instance_image_info, service=True) pxe_config_template = deploy_utils.get_pxe_config_template(node) pxe_utils.create_pxe_config(task, pxe_options, pxe_config_template) iwdi = node.driver_internal_info.get('is_whole_disk_image') deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, deploy_utils.get_boot_mode_for_deploy(node), iwdi, deploy_utils.is_trusted_boot_requested(node))
def _build_service_pxe_config(task, instance_image_info, root_uuid_or_disk_id): node = task.node pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) # NOTE(pas-ha) if it is takeover of ACTIVE node, # first ensure that basic PXE configs and links # are in place before switching pxe config if (node.provision_state == states.ACTIVE and not os.path.isfile(pxe_config_path)): pxe_options = _build_pxe_config_options(task, instance_image_info, service=True) pxe_config_template = deploy_utils.get_pxe_config_template(node) pxe_utils.create_pxe_config(task, pxe_options, pxe_config_template) iwdi = node.driver_internal_info.get('is_whole_disk_image') deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, deploy_utils.get_boot_mode_for_deploy(node), iwdi, deploy_utils.is_trusted_boot_requested(node), deploy_utils.is_iscsi_boot(task))
def continue_deploy(self, task, **kwargs): """Method invoked when deployed with the IPA ramdisk. This method is invoked during a heartbeat from an agent when the node is in wait-call-back state. This deploys the image on the node and then configures the node to boot according to the desired boot option (netboot or localboot). :param task: a TaskManager object containing the node. :param kwargs: the kwargs passed from the heartbeat method. :raises: InstanceDeployFailure, if it encounters some error during the deploy. """ task.process_event('resume') node = task.node LOG.debug('Continuing the deployment on node %s', node.uuid) uuid_dict = iscsi_deploy.do_agent_iscsi_deploy(task, self._client) is_whole_disk_image = node.driver_internal_info['is_whole_disk_image'] if iscsi_deploy.get_boot_option(node) == "local": # Install the boot loader root_uuid = uuid_dict.get('root uuid') efi_sys_uuid = uuid_dict.get('efi system partition uuid') self.configure_local_boot( task, root_uuid=root_uuid, efi_system_part_uuid=efi_sys_uuid) # If it's going to boot from the local disk, get rid of # the PXE configuration files used for the deployment pxe_utils.clean_up_pxe_config(task) else: root_uuid_or_disk_id = uuid_dict.get( 'root uuid', uuid_dict.get('disk identifier')) pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) boot_mode = deploy_utils.get_boot_mode_for_deploy(node) deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, boot_mode, is_whole_disk_image, deploy_utils.is_trusted_boot_requested(node)) self.reboot_and_finish_deploy(task)
def continue_deploy(self, task, **kwargs): """Method invoked when deployed with the IPA ramdisk. This method is invoked during a heartbeat from an agent when the node is in wait-call-back state. This deploys the image on the node and then configures the node to boot according to the desired boot option (netboot or localboot). :param task: a TaskManager object containing the node. :param kwargs: the kwargs passed from the heartbeat method. :raises: InstanceDeployFailure, if it encounters some error during the deploy. """ task.process_event('resume') node = task.node LOG.debug('Continuing the deployment on node %s', node.uuid) uuid_dict = iscsi_deploy.do_agent_iscsi_deploy(task, self._client) is_whole_disk_image = node.driver_internal_info['is_whole_disk_image'] if iscsi_deploy.get_boot_option(node) == "local": # Install the boot loader root_uuid = uuid_dict.get('root uuid') efi_sys_uuid = uuid_dict.get('efi system partition uuid') self.configure_local_boot(task, root_uuid=root_uuid, efi_system_part_uuid=efi_sys_uuid) # If it's going to boot from the local disk, get rid of # the PXE configuration files used for the deployment pxe_utils.clean_up_pxe_config(task) else: root_uuid_or_disk_id = uuid_dict.get( 'root uuid', uuid_dict.get('disk identifier')) pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) boot_mode = deploy_utils.get_boot_mode_for_deploy(node) deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, boot_mode, is_whole_disk_image, deploy_utils.is_trusted_boot_requested(node)) self.reboot_and_finish_deploy(task)
def pass_deploy_info(self, task, **kwargs): """Continues the deployment of baremetal node over iSCSI. This method continues the deployment of the baremetal node over iSCSI from where the deployment ramdisk has left off. :param task: a TaskManager instance containing the node to act on. :param kwargs: kwargs for performing iscsi deployment. :raises: InvalidState """ node = task.node task.process_event('resume') LOG.debug('Continuing the deployment on node %s', node.uuid) is_whole_disk_image = node.driver_internal_info['is_whole_disk_image'] uuid_dict = iscsi_deploy.continue_deploy(task, **kwargs) root_uuid_or_disk_id = uuid_dict.get( 'root uuid', uuid_dict.get('disk identifier')) # save the node's root disk UUID so that another conductor could # rebuild the PXE config file. Due to a shortcoming in Nova objects, # we have to assign to node.driver_internal_info so the node knows it # has changed. driver_internal_info = node.driver_internal_info driver_internal_info['root_uuid_or_disk_id'] = root_uuid_or_disk_id node.driver_internal_info = driver_internal_info node.save() try: if iscsi_deploy.get_boot_option(node) == "local": deploy_utils.try_set_boot_device(task, boot_devices.DISK) # If it's going to boot from the local disk, get rid of # the PXE configuration files used for the deployment pxe_utils.clean_up_pxe_config(task) # Ask the ramdisk to install bootloader and # wait for the call-back through the vendor passthru # 'pass_bootloader_install_info', if it's not a # whole disk image. if not is_whole_disk_image: LOG.debug('Installing the bootloader on node %s', node.uuid) deploy_utils.notify_ramdisk_to_proceed(kwargs['address']) task.process_event('wait') return else: pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) boot_mode = deploy_utils.get_boot_mode_for_deploy(node) deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, boot_mode, is_whole_disk_image, deploy_utils.is_trusted_boot_requested(node)) except Exception as e: LOG.error(_LE('Deploy failed for instance %(instance)s. ' 'Error: %(error)s'), {'instance': node.instance_uuid, 'error': e}) msg = _('Failed to continue iSCSI deployment.') deploy_utils.set_failed_state(task, msg) else: iscsi_deploy.finish_deploy(task, kwargs.get('address'))
def prepare(self, task): """Prepare the deployment environment for this task's node. Generates the TFTP configuration for PXE-booting both the deployment and user images, fetches the TFTP image from Glance and add it to the local cache. :param task: a TaskManager instance containing the node to act on. """ node = task.node # TODO(deva): optimize this if rerun on existing files if CONF.pxe.ipxe_enabled: # Copy the iPXE boot script to HTTP root directory bootfile_path = os.path.join( CONF.deploy.http_root, os.path.basename(CONF.pxe.ipxe_boot_script)) shutil.copyfile(CONF.pxe.ipxe_boot_script, bootfile_path) pxe_info = _get_image_info(node, task.context) pxe_options = _build_pxe_config_options(node, pxe_info, task.context) if deploy_utils.get_boot_mode_for_deploy(node) == 'uefi': pxe_config_template = CONF.pxe.uefi_pxe_config_template else: pxe_config_template = CONF.pxe.pxe_config_template pxe_utils.create_pxe_config(task, pxe_options, pxe_config_template) # FIXME(lucasagomes): If it's local boot we should not cache # the image kernel and ramdisk (Or even require it). _cache_ramdisk_kernel(task.context, node, pxe_info) # NOTE(deva): prepare may be called from conductor._do_takeover # in which case, it may need to regenerate the PXE config file for an # already-active deployment. if node.provision_state == states.ACTIVE: # this should have been stashed when the deploy was done # but let's guard, just in case it's missing iwdi = node.driver_internal_info.get('is_whole_disk_image') try: root_uuid_or_disk_id = ( node.driver_internal_info['root_uuid_or_disk_id']) except KeyError: if not iwdi: LOG.warn(_LW( "The UUID for the root partition can't be " "found, unable to switch the pxe config from " "deployment mode to service (boot) mode for node " "%(node)s"), {"node": node.uuid}) else: LOG.warn(_LW( "The disk id for the whole disk image can't " "be found, unable to switch the pxe config from " "deployment mode to service (boot) mode for " "node %(node)s"), {"node": node.uuid}) else: pxe_config_path = pxe_utils.get_pxe_config_file_path( node.uuid) deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, deploy_utils.get_boot_mode_for_deploy(node), iwdi, deploy_utils.is_trusted_boot_requested(node))
def pass_deploy_info(self, task, **kwargs): """Continues the deployment of baremetal node over iSCSI. This method continues the deployment of the baremetal node over iSCSI from where the deployment ramdisk has left off. :param task: a TaskManager instance containing the node to act on. :param kwargs: kwargs for performing iscsi deployment. :raises: InvalidState """ node = task.node task.process_event('resume') LOG.debug('Continuing the deployment on node %s', node.uuid) is_whole_disk_image = node.driver_internal_info['is_whole_disk_image'] uuid_dict = iscsi_deploy.continue_deploy(task, **kwargs) root_uuid_or_disk_id = uuid_dict.get('root uuid', uuid_dict.get('disk identifier')) # save the node's root disk UUID so that another conductor could # rebuild the PXE config file. Due to a shortcoming in Nova objects, # we have to assign to node.driver_internal_info so the node knows it # has changed. driver_internal_info = node.driver_internal_info driver_internal_info['root_uuid_or_disk_id'] = root_uuid_or_disk_id node.driver_internal_info = driver_internal_info node.save() try: if iscsi_deploy.get_boot_option(node) == "local": deploy_utils.try_set_boot_device(task, boot_devices.DISK) # If it's going to boot from the local disk, get rid of # the PXE configuration files used for the deployment pxe_utils.clean_up_pxe_config(task) # Ask the ramdisk to install bootloader and # wait for the call-back through the vendor passthru # 'pass_bootloader_install_info', if it's not a # whole disk image. if not is_whole_disk_image: LOG.debug('Installing the bootloader on node %s', node.uuid) deploy_utils.notify_ramdisk_to_proceed(kwargs['address']) task.process_event('wait') return else: pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) boot_mode = deploy_utils.get_boot_mode_for_deploy(node) deploy_utils.switch_pxe_config( pxe_config_path, root_uuid_or_disk_id, boot_mode, is_whole_disk_image, deploy_utils.is_trusted_boot_requested(node)) except Exception as e: LOG.error( _LE('Deploy failed for instance %(instance)s. ' 'Error: %(error)s'), { 'instance': node.instance_uuid, 'error': e }) msg = _('Failed to continue iSCSI deployment.') deploy_utils.set_failed_state(task, msg) else: iscsi_deploy.finish_deploy(task, kwargs.get('address'))