def test_switch_pxe_config(self): fname = self._create_config() utils.switch_pxe_config(fname, '12345678-1234-1234-1234-1234567890abcdef') with open(fname, 'r') as f: pxeconf = f.read() self.assertEqual(_PXECONF_BOOT, pxeconf)
def _continue_deploy(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') _destroy_token_file(node) root_uuid = iscsi_deploy.continue_deploy(task, **kwargs) if not root_uuid: return try: pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) deploy_utils.switch_pxe_config(pxe_config_path, root_uuid, driver_utils.get_node_capability(node, 'boot_mode')) deploy_utils.notify_deploy_complete(kwargs['address']) LOG.info(_LI('Deployment to node %s done'), node.uuid) task.process_event('done') 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)
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 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 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)
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 _prepare_instance_pxe_config(self, task, image_info, iscsi_boot=False, ramdisk_boot=False): """Prepares the config file for PXE boot :param task: a task from TaskManager. :param image_info: a dict of values of instance image metadata to set on the configuration file. :param iscsi_boot: if boot is from an iSCSI volume or not. :param ramdisk_boot: if the boot is to a ramdisk configuration. :returns: None """ node = task.node dhcp_opts = pxe_utils.dhcp_options_for_instance(task) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) if not os.path.isfile(pxe_config_path): pxe_options = _build_pxe_config_options(task, image_info, service=ramdisk_boot) pxe_config_template = (deploy_utils.get_pxe_config_template(node)) pxe_utils.create_pxe_config(task, pxe_options, pxe_config_template) deploy_utils.switch_pxe_config( pxe_config_path, None, boot_mode_utils.get_boot_mode_for_deploy(node), False, iscsi_boot=iscsi_boot, ramdisk_boot=ramdisk_boot)
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 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') _destroy_token_file(node) 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: 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) 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 test_switch_ipxe_config(self): cfg.CONF.set_override('ipxe_enabled', True, 'pxe') fname = self._create_config(ipxe=True) utils.switch_pxe_config(fname, '12345678-1234-1234-1234-1234567890abcdef') with open(fname, 'r') as f: pxeconf = f.read() self.assertEqual(_IPXECONF_BOOT, pxeconf)
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 test_switch_uefi_pxe_config(self): boot_mode = 'uefi' fname = self._create_config(boot_mode=boot_mode) utils.switch_pxe_config(fname, '12345678-1234-1234-1234-1234567890abcdef', boot_mode) with open(fname, 'r') as f: pxeconf = f.read() self.assertEqual(_UEFI_PXECONF_BOOT, pxeconf)
def test_switch_uefi_pxe_config(self): boot_mode = 'uefi' fname = self._create_config(boot_mode=boot_mode) utils.switch_pxe_config(fname, '12345678-1234-1234-1234-1234567890abcdef', boot_mode) with open(fname, 'r') as f: pxeconf = f.read() self.assertEqual(_UEFI_PXECONF_BOOT, pxeconf)
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 test_switch_ipxe_config(self): boot_mode = 'bios' cfg.CONF.set_override('ipxe_enabled', True, 'pxe') fname = self._create_config(ipxe=True) utils.switch_pxe_config(fname, '12345678-1234-1234-1234-1234567890abcdef', boot_mode) with open(fname, 'r') as f: pxeconf = f.read() self.assertEqual(_IPXECONF_BOOT, pxeconf)
def _continue_deploy(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') _destroy_token_file(node) root_uuid = iscsi_deploy.continue_deploy(task, **kwargs) if not root_uuid: return # 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'] = root_uuid node.driver_internal_info = driver_internal_info node.save() try: if iscsi_deploy.get_boot_option(node) == "local": 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) else: pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) deploy_utils.switch_pxe_config(pxe_config_path, root_uuid, driver_utils.get_node_capability(node, 'boot_mode')) deploy_utils.notify_deploy_complete(kwargs['address']) LOG.info(_LI('Deployment to node %s done'), node.uuid) task.process_event('done') 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)
def prepare_instance_pxe_config(task, image_info, iscsi_boot=False, ramdisk_boot=False, ipxe_enabled=False): """Prepares the config file for PXE boot :param task: a task from TaskManager. :param image_info: a dict of values of instance image metadata to set on the configuration file. :param iscsi_boot: if boot is from an iSCSI volume or not. :param ramdisk_boot: if the boot is to a ramdisk configuration. :param ipxe_enabled: Default false boolean to indicate if ipxe is in use by the caller. :returns: None """ node = task.node # Generate options for both IPv4 and IPv6, and they can be # filtered down later based upon the port options. # TODO(TheJulia): This should be re-tooled during the Victoria # development cycle so that we call a single method and return # combined options. The method we currently call is relied upon # by two eternal projects, to changing the behavior is not ideal. dhcp_opts = dhcp_options_for_instance(task, ipxe_enabled, ip_version=4) dhcp_opts += dhcp_options_for_instance(task, ipxe_enabled, ip_version=6) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) pxe_config_path = get_pxe_config_file_path(node.uuid, ipxe_enabled=ipxe_enabled) if not os.path.isfile(pxe_config_path): pxe_options = build_pxe_config_options(task, image_info, service=ramdisk_boot, ipxe_enabled=ipxe_enabled) if ipxe_enabled: pxe_config_template = (deploy_utils.get_ipxe_config_template(node)) else: pxe_config_template = (deploy_utils.get_pxe_config_template(node)) create_pxe_config(task, pxe_options, pxe_config_template, ipxe_enabled=ipxe_enabled) deploy_utils.switch_pxe_config(pxe_config_path, None, boot_mode_utils.get_boot_mode(node), False, iscsi_boot=iscsi_boot, ramdisk_boot=ramdisk_boot, ipxe_enabled=ipxe_enabled)
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) # NOTE(lucasagomes): We don't use the token file with the agent, # but as it's created as part of deploy() we are going to remove # it here. _destroy_token_file(node) 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) 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) # NOTE(lucasagomes): We don't use the token file with the agent, # but as it's created as part of deploy() we are going to remove # it here. _destroy_token_file(node) 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) self.reboot_and_finish_deploy(task)
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 _continue_deploy(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. """ node = task.node if node.provision_state != states.DEPLOYWAIT: LOG.error(_LE('Node %s is not waiting to be deployed.'), node.uuid) return _destroy_token_file(node) root_uuid = iscsi_deploy.continue_deploy(task, **kwargs) if not root_uuid: return try: pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) deploy_utils.switch_pxe_config( pxe_config_path, root_uuid, driver_utils.get_node_capability(node, 'boot_mode')) deploy_utils.notify_deploy_complete(kwargs['address']) LOG.info(_LI('Deployment to node %s done'), node.uuid) node.provision_state = states.ACTIVE node.target_provision_state = states.NOSTATE node.save() 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.') iscsi_deploy.set_failed_state(task, msg)
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 prepare_instance_pxe_config(task, image_info, iscsi_boot=False, ramdisk_boot=False, ipxe_enabled=False): """Prepares the config file for PXE boot :param task: a task from TaskManager. :param image_info: a dict of values of instance image metadata to set on the configuration file. :param iscsi_boot: if boot is from an iSCSI volume or not. :param ramdisk_boot: if the boot is to a ramdisk configuration. :param ipxe_enabled: Default false boolean to indicate if ipxe is in use by the caller. :returns: None """ node = task.node dhcp_opts = dhcp_options_for_instance(task, ipxe_enabled) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) pxe_config_path = get_pxe_config_file_path(node.uuid, ipxe_enabled=ipxe_enabled) if not os.path.isfile(pxe_config_path): pxe_options = build_pxe_config_options(task, image_info, service=ramdisk_boot, 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) deploy_utils.switch_pxe_config(pxe_config_path, None, boot_mode_utils.get_boot_mode(node), False, iscsi_boot=iscsi_boot, ramdisk_boot=ramdisk_boot, ipxe_enabled=ipxe_enabled)
def _continue_deploy(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. """ node = task.node if node.provision_state != states.DEPLOYWAIT: LOG.error(_LE('Node %s is not waiting to be deployed.'), node.uuid) return _destroy_token_file(node) root_uuid = iscsi_deploy.continue_deploy(task, **kwargs) if not root_uuid: return try: pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) deploy_utils.switch_pxe_config(pxe_config_path, root_uuid, driver_utils.get_node_capability(node, 'boot_mode')) deploy_utils.notify_deploy_complete(kwargs['address']) LOG.info(_LI('Deployment to node %s done'), node.uuid) node.provision_state = states.ACTIVE node.target_provision_state = states.NOSTATE node.save() 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.') iscsi_deploy.set_failed_state(task, msg)
def _continue_deploy(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') _destroy_token_file(node) root_uuid = iscsi_deploy.continue_deploy(task, **kwargs) if not root_uuid: return try: if iscsi_deploy.get_boot_option(node) == "local": 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) else: pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid) deploy_utils.switch_pxe_config(pxe_config_path, root_uuid, driver_utils.get_node_capability(node, 'boot_mode')) deploy_utils.notify_deploy_complete(kwargs['address']) LOG.info(_LI('Deployment to node %s done'), node.uuid) task.process_event('done') 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)
def prepare_instance_pxe_config(task, image_info, iscsi_boot=False, ramdisk_boot=False, ipxe_enabled=False): """Prepares the config file for PXE boot :param task: a task from TaskManager. :param image_info: a dict of values of instance image metadata to set on the configuration file. :param iscsi_boot: if boot is from an iSCSI volume or not. :param ramdisk_boot: if the boot is to a ramdisk configuration. :param ipxe_enabled: Default false boolean to indicate if ipxe is in use by the caller. :returns: None """ node = task.node dhcp_opts = dhcp_options_for_instance(task, ipxe_enabled) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) pxe_config_path = get_pxe_config_file_path( node.uuid) if not os.path.isfile(pxe_config_path): pxe_options = build_pxe_config_options( task, image_info, service=ramdisk_boot, 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) deploy_utils.switch_pxe_config( pxe_config_path, None, boot_mode_utils.get_boot_mode(node), False, iscsi_boot=iscsi_boot, ramdisk_boot=ramdisk_boot, ipxe_enabled=ipxe_enabled)
def test_switch_pxe_config(self): utils.switch_pxe_config(self.fname, '12345678-1234-1234-1234-1234567890abcdef') with open(self.fname, 'r') as f: pxeconf = f.read() self.assertEqual(pxeconf, _PXECONF_BOOT)
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) boot_device = None if deploy_utils.is_iscsi_boot(task): dhcp_opts = pxe_utils.dhcp_options_for_instance(task) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) # configure iPXE for iscsi boot pxe_config_path = pxe_utils.get_pxe_config_file_path( task.node.uuid) if not os.path.isfile(pxe_config_path): pxe_options = _build_pxe_config_options(task, {}) pxe_config_template = ( deploy_utils.get_pxe_config_template(node)) pxe_utils.create_pxe_config( task, pxe_options, pxe_config_template) deploy_utils.switch_pxe_config( pxe_config_path, None, deploy_utils.get_boot_mode_for_deploy(node), False, iscsi_boot=True) boot_device = boot_devices.PXE elif boot_option != "local": if task.driver.storage.should_write_image(task): # 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 task.driver.storage.should_write_image(task): pass elif not iwdi: LOG.warning("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.warning("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. Booting the instance " "from disk.", {"node": task.node.uuid}) pxe_utils.clean_up_pxe_config(task) boot_device = boot_devices.DISK else: _build_service_pxe_config(task, instance_image_info, root_uuid_or_disk_id) boot_device = boot_devices.PXE 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) boot_device = boot_devices.DISK # NOTE(pas-ha) do not re-set boot device on ACTIVE nodes # during takeover if boot_device and task.node.provision_state != states.ACTIVE: manager_utils.node_set_boot_device(task, boot_device, persistent=True)
def test_switch_pxe_config(self): utils.switch_pxe_config(self.fname, '12345678-1234-1234-1234-1234567890abcdef') with open(self.fname, 'r') as f: pxeconf = f.read() self.assertEqual(pxeconf, _PXECONF_BOOT)
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) boot_device = None if deploy_utils.is_iscsi_boot(task): dhcp_opts = pxe_utils.dhcp_options_for_instance(task) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) # configure iPXE for iscsi boot pxe_config_path = pxe_utils.get_pxe_config_file_path( task.node.uuid) if not os.path.isfile(pxe_config_path): pxe_options = _build_pxe_config_options(task, {}) pxe_config_template = ( deploy_utils.get_pxe_config_template(node)) pxe_utils.create_pxe_config( task, pxe_options, pxe_config_template) deploy_utils.switch_pxe_config( pxe_config_path, None, deploy_utils.get_boot_mode_for_deploy(node), False, iscsi_boot=True) boot_device = boot_devices.PXE elif boot_option != "local": if task.driver.storage.should_write_image(task): # 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 task.driver.storage.should_write_image(task): pass elif not iwdi: LOG.warning("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.warning("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: _build_service_pxe_config(task, instance_image_info, root_uuid_or_disk_id) boot_device = boot_devices.PXE 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) boot_device = boot_devices.DISK # NOTE(pas-ha) do not re-set boot device on ACTIVE nodes # during takeover if boot_device and task.node.provision_state != states.ACTIVE: manager_utils.node_set_boot_device(task, boot_device, persistent=True)
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) _destroy_token_file(node) 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) 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'))