def clean_up(self, task): """Clean up the deployment environment for the task's node. Unlinks TFTP and instance images and triggers image cache cleanup. Removes the TFTP configuration files for this node. :param task: a TaskManager instance containing the node to act on. """ node = task.node try: pxe_info = _get_image_info(node, task.context) except exception.MissingParameterValue as e: LOG.warning(_LW('Could not get image info to clean up images ' 'for node %(node)s: %(err)s'), {'node': node.uuid, 'err': e}) else: for label in pxe_info: path = pxe_info[label][1] utils.unlink_without_raise(path) TFTPImageCache().clean_up() pxe_utils.clean_up_pxe_config(task) iscsi_deploy.destroy_images(node.uuid)
def clean_up(self, task): """Clean up the deployment environment for the task's node. Unlinks TFTP and instance images and triggers image cache cleanup. Removes the TFTP configuration files for this node. As a precaution, this method also ensures the keystone auth token file was removed. :param task: a TaskManager instance containing the node to act on. """ node = task.node try: pxe_info = _get_image_info(node, task.context) except exception.MissingParameterValue as e: LOG.warning( _LW('Could not get image info to clean up images ' 'for node %(node)s: %(err)s'), { 'node': node.uuid, 'err': e }) else: for label in pxe_info: path = pxe_info[label][1] utils.unlink_without_raise(path) TFTPImageCache().clean_up() pxe_utils.clean_up_pxe_config(task) iscsi_deploy.destroy_images(node.uuid) _destroy_token_file(node)
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 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.warning( _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.warning( _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: _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: deploy_utils.try_set_boot_device(task, boot_device)
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 _clean_up_pxe(task): """Clean up left over PXE and DHCP files.""" pxe_info = _get_tftp_image_info(task.node) for label in pxe_info: path = pxe_info[label][1] unlink_without_raise(path) AgentTFTPImageCache().clean_up() pxe_utils.clean_up_pxe_config(task)
def _clean_up_pxe(task): """Clean up left over PXE and DHCP files.""" pxe_info = _get_tftp_image_info(task.node) for label in pxe_info: path = pxe_info[label][1] utils.unlink_without_raise(path) AgentTFTPImageCache().clean_up() 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 test_clean_up_pxe_config(self, unlink_mock, rmtree_mock): address = "aa:aa:aa:aa:aa:aa" object_utils.create_test_port(self.context, node_id=self.node.id, address=address) with task_manager.acquire(self.context, self.node.uuid) as task: pxe_utils.clean_up_pxe_config(task) unlink_mock.assert_called_once_with("/tftpboot/pxelinux.cfg/01-%s" % address.replace(":", "-")) rmtree_mock.assert_called_once_with(os.path.join(CONF.pxe.tftp_root, self.node.uuid))
def take_over(self, task): dhcp_opts = pxe_utils.dhcp_options_for_instance(task) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) if iscsi_deploy.get_boot_option(task.node) == "local": # 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_clean_up_pxe_config(self, unlink_mock, rmtree_mock): address = "aa:aa:aa:aa:aa:aa" object_utils.create_test_port(self.context, node_id=self.node.id, address=address) with task_manager.acquire(self.context, self.node.uuid) as task: pxe_utils.clean_up_pxe_config(task) unlink_mock.assert_called_once_with("/tftpboot/pxelinux.cfg/01-%s" % address.replace(':', '-')) rmtree_mock.assert_called_once_with( os.path.join(CONF.pxe.tftp_root, self.node.uuid))
def take_over(self, task): if not iscsi_deploy.get_boot_option(task.node) == "local": # 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) 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_clean_up_pxe_config(self, unlink_mock, rmtree_mock): address = "aa:aa:aa:aa:aa:aa" pdict = dbutils.get_test_port(node_uuid=self.node.uuid, address=address) self.dbapi.create_port(pdict) with task_manager.acquire(self.context, self.node.uuid) as task: pxe_utils.clean_up_pxe_config(task) unlink_mock.assert_called_once_with("/tftpboot/pxelinux.cfg/01-%s" % address.replace(':', '-')) rmtree_mock.assert_called_once_with( os.path.join(CONF.pxe.tftp_root, self.node.uuid))
def test_clean_up_pxe_config_uefi(self, provider_mock, unlink_mock, rmtree_mock): ip_address = "10.10.0.1" address = "aa:aa:aa:aa:aa:aa" properties = {"capabilities": "boot_mode:uefi"} object_utils.create_test_port(self.context, node_id=self.node.id, address=address) provider_mock.get_ip_addresses.return_value = [ip_address] with task_manager.acquire(self.context, self.node.uuid) as task: task.node.properties = properties pxe_utils.clean_up_pxe_config(task) unlink_mock.assert_called_once_with("/tftpboot/0A0A0001.conf") rmtree_mock.assert_called_once_with(os.path.join(CONF.pxe.tftp_root, self.node.uuid))
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 test_clean_up_ipxe_config_uefi(self, unlink_mock, rmtree_mock): self.config(ipxe_enabled=True, group='pxe') address = "aa:aa:aa:aa:aa:aa" properties = {'capabilities': 'boot_mode:uefi'} object_utils.create_test_port(self.context, node_id=self.node.id, address=address) with task_manager.acquire(self.context, self.node.uuid) as task: task.node.properties = properties pxe_utils.clean_up_pxe_config(task) unlink_mock.assert_called_once_with( '/httpboot/pxelinux.cfg/aa-aa-aa-aa-aa-aa') rmtree_mock.assert_called_once_with( os.path.join(CONF.deploy.http_root, self.node.uuid))
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 _clean_up_pxe_env(task, images_info): """Cleanup PXE environment of all the images in images_info. Cleans up the PXE environment for the mentioned images in images_info. :param task: a TaskManager object :param images_info: A dictionary of images whose keys are the image names to be cleaned up (kernel, ramdisk, etc) and values are a tuple of identifier and absolute path. """ for label in images_info: path = images_info[label][1] ironic_utils.unlink_without_raise(path) pxe_utils.clean_up_pxe_config(task) TFTPImageCache().clean_up()
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 test_clean_up_pxe_config_uefi_instance_info(self, provider_mock, unlink_mock, rmtree_mock): ip_address = '10.10.0.1' address = "aa:aa:aa:aa:aa:aa" object_utils.create_test_port(self.context, node_id=self.node.id, address=address) provider_mock.get_ip_addresses.return_value = [ip_address] with task_manager.acquire(self.context, self.node.uuid) as task: task.node.instance_info['deploy_boot_mode'] = 'uefi' pxe_utils.clean_up_pxe_config(task) unlink_mock.assert_called_once_with('/tftpboot/0A0A0001.conf') rmtree_mock.assert_called_once_with( os.path.join(CONF.pxe.tftp_root, self.node.uuid))
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 clean_up(self, task): """Clean up the deployment environment for the task's node. Unlinks TFTP and instance images and triggers image cache cleanup. Removes the TFTP configuration files for this node. As a precaution, this method also ensures the keystone auth token file was removed. :param task: a TaskManager instance containing the node to act on. """ node = task.node pxe_info = _get_image_info(node, task.context) for label in pxe_info: path = pxe_info[label][1] utils.unlink_without_raise(path) TFTPImageCache().clean_up() pxe_utils.clean_up_pxe_config(task) iscsi_deploy.destroy_images(node.uuid) _destroy_token_file(node)
def clean_up(self, task): """Clean up the deployment environment for the task's node. Unlinks TFTP and instance images and triggers image cache cleanup. Removes the TFTP configuration files for this node. As a precaution, this method also ensures the keystone auth token file was removed. :param task: a TaskManager instance containing the node to act on. """ node = task.node pxe_info = _get_tftp_image_info(node, task.context) d_info = _parse_deploy_info(node) for label in pxe_info: path = pxe_info[label][1] utils.unlink_without_raise(path) TFTPImageCache().clean_up() pxe_utils.clean_up_pxe_config(task) _destroy_images(d_info, node.uuid) _destroy_token_file(node)
def test_clean_up_pxe_config_uefi(self, provider_mock, unlink_mock, rmtree_mock): ip_address = '10.10.0.1' address = "aa:aa:aa:aa:aa:aa" properties = {'capabilities': 'boot_mode:uefi'} object_utils.create_test_port(self.context, node_id=self.node.id, address=address) provider_mock.get_ip_addresses.return_value = [ip_address] with task_manager.acquire(self.context, self.node.uuid) as task: task.node.properties = properties pxe_utils.clean_up_pxe_config(task) unlink_calls = [ mock.call('/tftpboot/10.10.0.1.conf'), mock.call('/tftpboot/0A0A0001.conf') ] unlink_mock.assert_has_calls(unlink_calls) rmtree_mock.assert_called_once_with( os.path.join(CONF.pxe.tftp_root, self.node.uuid))
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 clean_up(self, task): """Clean up the deployment environment for this node. If preparation of the deployment environment ahead of time is possible, this method should be implemented by the driver. It should erase anything cached by the `prepare` method. If implemented, this method must be idempotent. It may be called multiple times for the same node on the same conductor, and it may be called by multiple conductors in parallel. Therefore, it must not require an exclusive lock. This method is called before `tear_down`. :param task: a TaskManager instance. """ pxe_info = _get_tftp_image_info(task.node) for label in pxe_info: path = pxe_info[label][1] utils.unlink_without_raise(path) AgentTFTPImageCache().clean_up() 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 """ ipxe_enabled = CONF.pxe.ipxe_enabled boot_mode_utils.sync_boot_mode(task) node = task.node boot_option = deploy_utils.get_boot_option(node) boot_device = None instance_image_info = {} if boot_option == "ramdisk": instance_image_info = pxe_utils.get_instance_image_info(task) pxe_utils.cache_ramdisk_kernel(task, instance_image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) if deploy_utils.is_iscsi_boot(task) or boot_option == "ramdisk": pxe_utils.prepare_instance_pxe_config( task, instance_image_info, iscsi_boot=deploy_utils.is_iscsi_boot(task), ramdisk_boot=(boot_option == "ramdisk"), ipxe_enabled=CONF.pxe.ipxe_enabled) 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 = pxe_utils.get_instance_image_info(task) pxe_utils.cache_ramdisk_kernel( task, instance_image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) # If it's going to PXE boot we need to update the DHCP server dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled) 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, ipxe_enabled=CONF.pxe.ipxe_enabled) boot_device = boot_devices.DISK else: pxe_utils.build_service_pxe_config(task, instance_image_info, root_uuid_or_disk_id, ipxe_enabled=ipxe_enabled) 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, ipxe_enabled=CONF.pxe.ipxe_enabled) 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: persistent = True if node.driver_info.get('force_persistent_boot_device', 'Default') == 'Never': persistent = False manager_utils.node_set_boot_device(task, boot_device, persistent=persistent)
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'))
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.warning( _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.warning( _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)) # In case boot mode changes from bios to uefi, boot device # order may get lost in some platforms. Better to re-apply # boot device. deploy_utils.try_set_boot_device(task, 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) deploy_utils.try_set_boot_device(task, boot_devices.DISK)
def _continue_deploy(self, task, **kwargs): """Resume a deployment upon getting POST data from deploy ramdisk. This method raises no exceptions because it is intended to be invoked asynchronously as a callback from the deploy ramdisk. """ node = task.node driver_info = _parse_driver_info(node) def _set_failed_state(msg): node.provision_state = states.DEPLOYFAIL node.target_provision_state = states.NOSTATE node.save(task.context) try: manager_utils.node_power_action(task, states.POWER_OFF) except Exception: msg = (_('Node %s failed to power off while handling deploy ' 'failure. This may be a serious condition. Node ' 'should be removed from Ironic or put in maintenance ' 'mode until the problem is resolved.') % node.uuid) LOG.error(msg) finally: # NOTE(deva): node_power_action() erases node.last_error # so we need to set it again here. node.last_error = msg node.save(task.context) if node.provision_state != states.DEPLOYWAIT: LOG.error(_('Node %s is not waiting to be deployed.') % node.uuid) return node.provision_state = states.DEPLOYING node.save(task.context) # remove cached keystone token immediately _destroy_token_file(node) params = self._get_deploy_info(node, **kwargs) ramdisk_error = kwargs.get('error') if ramdisk_error: LOG.error(_('Error returned from PXE deploy ramdisk: %s') % ramdisk_error) _set_failed_state(_('Failure in PXE deploy ramdisk.')) _destroy_images(driver_info, node.uuid) return LOG.info(_('Continuing deployment for node %(node)s, params ' '%(params)s') % {'node': node.uuid, 'params': params}) try: deploy_utils.deploy(**params) except Exception as e: LOG.error(_('PXE deploy failed for instance %(instance)s. ' 'Error: %(error)s') % {'instance': node.instance_uuid, 'error': e}) _set_failed_state(_('PXE driver failed to continue deployment.')) else: LOG.info(_('Deployment to node %s done') % node.uuid) node.provision_state = states.ACTIVE node.target_provision_state = states.NOSTATE node.save(task.context) boot_from_pxe = (node.instance_info.get('kernel') and node.instance_info.get('ramdisk')) if not boot_from_pxe: # Remove PXE boot configuration in order to boot from disk. pxe_utils.clean_up_pxe_config(task) neutron.update_neutron(task, None) _destroy_images(driver_info, node.uuid)
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 """ boot_mode_utils.sync_boot_mode(task) boot_mode_utils.configure_secure_boot_if_needed(task) node = task.node boot_option = deploy_utils.get_boot_option(node) boot_device = None instance_image_info = {} if boot_option == "ramdisk": instance_image_info = pxe_utils.get_instance_image_info( task, ipxe_enabled=self.ipxe_enabled) pxe_utils.cache_ramdisk_kernel(task, instance_image_info, ipxe_enabled=self.ipxe_enabled) if deploy_utils.is_iscsi_boot(task) or boot_option == "ramdisk": pxe_utils.prepare_instance_pxe_config( task, instance_image_info, iscsi_boot=deploy_utils.is_iscsi_boot(task), ramdisk_boot=(boot_option == "ramdisk"), ipxe_enabled=self.ipxe_enabled) 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 = pxe_utils.get_instance_image_info( task, ipxe_enabled=self.ipxe_enabled) pxe_utils.cache_ramdisk_kernel(task, instance_image_info, ipxe_enabled=self.ipxe_enabled) # If it's going to PXE boot we need to update the DHCP server dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=self.ipxe_enabled, ip_version=4) dhcp_opts += pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=self.ipxe_enabled, ip_version=6) 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, ipxe_enabled=self.ipxe_enabled) boot_device = boot_devices.DISK else: pxe_utils.build_service_pxe_config( task, instance_image_info, root_uuid_or_disk_id, ipxe_enabled=self.ipxe_enabled) boot_device = boot_devices.PXE else: # NOTE(dtantsur): create a PXE configuration as a safety net for # hardware uncapable of persistent boot. If on a reboot it will try # to boot from PXE, this configuration will return it back. if CONF.pxe.enable_netboot_fallback: pxe_utils.build_service_pxe_config( task, instance_image_info, task.node.driver_internal_info.get('root_uuid_or_disk_id'), ipxe_enabled=self.ipxe_enabled, # PXE config for whole disk images is identical to what # we need to boot from local disk, so use True even # for partition images. is_whole_disk_image=True) else: # Clean up the deployment configuration pxe_utils.clean_up_pxe_config( task, ipxe_enabled=self.ipxe_enabled) 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 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 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 """ ipxe_enabled = CONF.pxe.ipxe_enabled boot_mode_utils.sync_boot_mode(task) node = task.node boot_option = deploy_utils.get_boot_option(node) boot_device = None instance_image_info = {} if boot_option == "ramdisk": instance_image_info = pxe_utils.get_instance_image_info(task) pxe_utils.cache_ramdisk_kernel(task, instance_image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) if deploy_utils.is_iscsi_boot(task) or boot_option == "ramdisk": pxe_utils.prepare_instance_pxe_config( task, instance_image_info, iscsi_boot=deploy_utils.is_iscsi_boot(task), ramdisk_boot=(boot_option == "ramdisk"), ipxe_enabled=CONF.pxe.ipxe_enabled) 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 = pxe_utils.get_instance_image_info(task) pxe_utils.cache_ramdisk_kernel( task, instance_image_info, ipxe_enabled=CONF.pxe.ipxe_enabled) # If it's going to PXE boot we need to update the DHCP server dhcp_opts = pxe_utils.dhcp_options_for_instance(task, ipxe_enabled) 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, ipxe_enabled=CONF.pxe.ipxe_enabled) boot_device = boot_devices.DISK else: pxe_utils.build_service_pxe_config(task, instance_image_info, root_uuid_or_disk_id, ipxe_enabled=ipxe_enabled) 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, ipxe_enabled=CONF.pxe.ipxe_enabled) 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: persistent = True if node.driver_info.get('force_persistent_boot_device', 'Default') == 'Never': persistent = False manager_utils.node_set_boot_device(task, boot_device, persistent=persistent)
def _continue_deploy(self, task, **kwargs): """Resume a deployment upon getting POST data from deploy ramdisk. This method raises no exceptions because it is intended to be invoked asynchronously as a callback from the deploy ramdisk. """ node = task.node driver_info = _parse_driver_info(node) def _set_failed_state(msg): node.provision_state = states.DEPLOYFAIL node.target_provision_state = states.NOSTATE node.save(task.context) try: manager_utils.node_power_action(task, states.POWER_OFF) except Exception: msg = (_('Node %s failed to power off while handling deploy ' 'failure. This may be a serious condition. Node ' 'should be removed from Ironic or put in maintenance ' 'mode until the problem is resolved.') % node.uuid) LOG.error(msg) finally: # NOTE(deva): node_power_action() erases node.last_error # so we need to set it again here. node.last_error = msg node.save(task.context) if node.provision_state != states.DEPLOYWAIT: LOG.error(_('Node %s is not waiting to be deployed.') % node.uuid) return node.provision_state = states.DEPLOYING node.save(task.context) # remove cached keystone token immediately _destroy_token_file(node) params = self._get_deploy_info(node, **kwargs) ramdisk_error = kwargs.get('error') if ramdisk_error: LOG.error( _('Error returned from PXE deploy ramdisk: %s') % ramdisk_error) _set_failed_state(_('Failure in PXE deploy ramdisk.')) _destroy_images(driver_info, node.uuid) return LOG.info( _('Continuing deployment for node %(node)s, params ' '%(params)s') % { 'node': node.uuid, 'params': params }) try: deploy_utils.deploy(**params) except Exception as e: LOG.error( _('PXE deploy failed for instance %(instance)s. ' 'Error: %(error)s') % { 'instance': node.instance_uuid, 'error': e }) _set_failed_state(_('PXE driver failed to continue deployment.')) else: LOG.info(_('Deployment to node %s done') % node.uuid) node.provision_state = states.ACTIVE node.target_provision_state = states.NOSTATE node.save(task.context) boot_from_pxe = (node.instance_info.get('kernel') and node.instance_info.get('ramdisk')) if not boot_from_pxe: # Remove PXE boot configuration in order to boot from disk. pxe_utils.clean_up_pxe_config(task) neutron.update_neutron(task, None) _destroy_images(driver_info, node.uuid)