def prepare(self, task): node = task.node # Log a warning if the boot_option is wrong... and # otherwise reset it. boot_option = deploy_utils.get_boot_option(node) if boot_option != 'ramdisk': LOG.warning('Incorrect "boot_option" set for node %(node)s ' 'and will be overridden to "ramdisk" as to ' 'match the deploy interface. Found: %(boot_opt)s.', {'node': node.uuid, 'boot_opt': boot_option}) i_info = task.node.instance_info i_info.update({'capabilities': {'boot_option': 'ramdisk'}}) node.instance_info = i_info node.save() deploy_utils.populate_storage_driver_internal_info(task) if node.provision_state == states.DEPLOYING: # Ask the network interface to validate itself so # we can ensure we are able to proceed. task.driver.network.validate(task) manager_utils.node_power_action(task, states.POWER_OFF) # NOTE(TheJulia): If this was any other interface, we would # unconfigure tenant networks, add provisioning networks, etc. task.driver.storage.attach_volumes(task) if node.provision_state in (states.ACTIVE, states.UNRESCUING): # In the event of takeover or unrescue. task.driver.boot.prepare_instance(task)
def _plug_provisioning(self, task, **kwargs): LOG.debug("Plugging the provisioning!") if task.node.power_state != states.POWER_ON: manager_utils.node_power_action(task, states.REBOOT) client = neutron._build_client(task.context.auth_token) port = client.create_port({ 'port': { "network_id": CONF.neutron.cleaning_network_uuid, "extra_dhcp_opts": pxe_utils.dhcp_options_for_instance(task), } }) name = port['port']['id'] network = client.show_network(port['port']['network_id']) seg_id = network['network']['provider:segmentation_id'] try: common.add_vnic( task, name, port['port']['mac_address'], seg_id, True) except imcsdk.ImcException: client.delete_port(name) raise new_port = objects.Port( task.context, node_id=task.node.id, address=port['port']['mac_address'], extra={"vif_port_id": port['port']['id'], "type": "deploy", "state": "ACTIVE"}) new_port.create() return port['port']['fixed_ips'][0]['ip_address']
def deploy(self, task): if 'configdrive' in task.node.instance_info: LOG.warning('A configuration drive is present with ' 'in the deployment request of node %(node)s. ' 'The configuration drive will be ignored for ' 'this deployment.', {'node': task.node}) manager_utils.node_power_action(task, states.POWER_OFF) # Tenant neworks must enable connectivity to the boot # location, as reboot() can otherwise be very problematic. # IDEA(TheJulia): Maybe a "trusted environment" mode flag # that we otherwise fail validation on for drivers that # require explicit security postures. power_state_to_restore = manager_utils.power_on_node_if_needed(task) task.driver.network.configure_tenant_networks(task) manager_utils.restore_power_state_if_needed( task, power_state_to_restore) # calling boot.prepare_instance will also set the node # to PXE boot, and update PXE templates accordingly task.driver.boot.prepare_instance(task) # Power-on the instance, with PXE prepared, we're done. manager_utils.node_power_action(task, states.POWER_ON) LOG.info('Deployment setup for node %s done', task.node.uuid) return None
def _prepare_node_for_deploy(task): """Common preparatory steps for all iLO drivers. This method performs common preparatory steps required for all drivers. 1. Power off node 2. Disables secure boot, if it is in enabled state. 3. Updates boot_mode capability to 'uefi' if secure boot is requested. 4. Changes boot mode of the node if secure boot is disabled currently. :param task: a TaskManager instance containing the node to act on. :raises: IloOperationError, if some operation on iLO failed. """ manager_utils.node_power_action(task, states.POWER_OFF) # Boot mode can be changed only if secure boot is in disabled state. # secure boot and boot mode cannot be changed together. change_boot_mode = True # Disable secure boot on the node if it is in enabled state. if _disable_secure_boot(task): change_boot_mode = False if change_boot_mode: ilo_common.update_boot_mode(task) else: # Need to update boot mode that will be used during deploy, if one is # not provided. # Since secure boot was disabled, we are in 'uefi' boot mode. if deploy_utils.get_boot_mode_for_deploy(task.node) is None: instance_info = task.node.instance_info instance_info['deploy_boot_mode'] = 'uefi' task.node.instance_info = instance_info task.node.save()
def _reboot_and_finish_deploy(task): wait = CONF.ansible.post_deploy_get_power_state_retry_interval * 1000 attempts = CONF.ansible.post_deploy_get_power_state_retries + 1 @retrying.retry( stop_max_attempt_number=attempts, retry_on_result=lambda state: state != states.POWER_OFF, wait_fixed=wait ) def _wait_until_powered_off(task): return task.driver.power.get_power_state(task) try: _wait_until_powered_off(task) except Exception as e: LOG.warning(_LW('Failed to soft power off node %(node_uuid)s ' 'in at least %(timeout)d seconds. Error: %(error)s'), {'node_uuid': task.node.uuid, 'timeout': (wait * (attempts - 1)) / 1000, 'error': e}) manager_utils.node_power_action(task, states.POWER_OFF) task.driver.network.remove_provisioning_network(task) task.driver.network.configure_tenant_networks(task) manager_utils.node_power_action(task, states.POWER_ON)
def prepare(self, task): """Prepare the deployment environment for this node. :param task: a TaskManager instance. :raises: NetworkError: if the previous cleaning ports cannot be removed or if new cleaning ports cannot be created. :raises: InvalidParameterValue when the wrong power state is specified or the wrong driver info is specified for power management. :raises: other exceptions by the node's power driver if something wrong occurred during the power action. :raises: exception.ImageRefValidationFailed if image_source is not Glance href and is not HTTP(S) URL. :raises: any boot interface's prepare_ramdisk exceptions. """ # Nodes deployed by AgentDeploy always boot from disk now. So there # is nothing to be done in prepare() when it's called during # take over. node = task.node if node.provision_state == states.DEPLOYING: # Adding the node to provisioning network so that the dhcp # options get added for the provisioning port. manager_utils.node_power_action(task, states.POWER_OFF) task.driver.network.add_provisioning_network(task) if node.provision_state != states.ACTIVE: node.instance_info = build_instance_info_for_deploy(task) node.save() if CONF.agent.manage_agent_boot: deploy_opts = deploy_utils.build_agent_options(node) task.driver.boot.prepare_ramdisk(task, deploy_opts)
def _cleaning_reboot(self, task): """Reboots a node out of band after a clean step that requires it. If an agent clean step has 'reboot_requested': True, reboots the node when the step is completed. Will put the node in CLEANFAIL if the node cannot be rebooted. :param task: a TaskManager instance """ try: manager_utils.node_power_action(task, states.REBOOT) except Exception as e: msg = (_('Reboot requested by clean step %(step)s failed for ' 'node %(node)s: %(err)s') % {'step': task.node.clean_step, 'node': task.node.uuid, 'err': e}) LOG.error(msg) # do not set cleaning_reboot if we didn't reboot manager_utils.cleaning_error_handler(task, msg) return # Signify that we've rebooted driver_internal_info = task.node.driver_internal_info driver_internal_info['cleaning_reboot'] = True task.node.driver_internal_info = driver_internal_info task.node.save()
def _reboot_into(task, iso, ramdisk_options): """Reboots the node into a given boot ISO. This method attaches the given bootable ISO as virtual media, prepares the arguments for ramdisk in virtual media floppy, and then reboots the node. :param task: a TaskManager instance containing the node to act on. :param iso: a bootable ISO image href to attach to. Should be either of below: * A Swift object - It should be of format 'swift:<object-name>'. It is assumed that the image object is present in CONF.ilo.swift_ilo_container; * A Glance image - It should be format 'glance://<glance-image-uuid>' or just <glance-image-uuid>; * An HTTP URL. :param ramdisk_options: the options to be passed to the ramdisk in virtual media floppy. :raises: ImageCreationFailed, if it failed while creating the floppy image. :raises: IloOperationError, if some operation on iLO failed. """ ilo_common.setup_vmedia_for_boot(task, iso, ramdisk_options) # In UEFI boot mode, upon inserting virtual CDROM, one has to reset the # system to see it as a valid boot device in persistent boot devices. # But virtual CDROM device is always available for one-time boot. # During enable/disable of secure boot settings, iLO internally resets # the server twice. But it retains one time boot settings across internal # resets. Hence no impact of this change for secure boot deploy. manager_utils.node_set_boot_device(task, boot_devices.CDROM) manager_utils.node_power_action(task, states.REBOOT)
def tear_down_cleaning(self, task): """Clean up the PXE and DHCP files after cleaning.""" manager_utils.node_power_action(task, states.POWER_OFF) # If we created cleaning ports, delete them provider = dhcp_factory.DHCPFactory().provider if getattr(provider, 'delete_cleaning_ports', None): provider.delete_cleaning_ports(task)
def set_failed_state(task, msg): """Sets the deploy status as failed with relevant messages. This method sets the deployment as fail with the given message. It sets node's provision_state to DEPLOYFAIL and updates last_error with the given error message. It also powers off the baremetal node. :param task: a TaskManager instance containing the node to act on. :param msg: the message to set in last_error of the node. """ node = task.node node.provision_state = states.DEPLOYFAIL node.target_provision_state = states.NOSTATE node.save() try: manager_utils.node_power_action(task, states.POWER_OFF) except Exception: msg2 = (_LE('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.exception(msg2) finally: # NOTE(deva): node_power_action() erases node.last_error # so we need to set it again here. node.last_error = msg node.save()
def deploy(self, task): """Start deployment of the task's node. Fetches the instance image, prepares the options for the deployment ramdisk, sets the node to boot from virtual media cdrom, and reboots the given node. :param task: a TaskManager instance containing the node to act on. :returns: deploy state DEPLOYWAIT. :raises: InstanceDeployFailure, if image size if greater than root partition. :raises: ImageCreationFailed, if it failed while creating the floppy image. :raises: IloOperationError, if some operation on iLO fails. """ node = task.node manager_utils.node_power_action(task, states.POWER_OFF) iscsi_deploy.cache_instance_image(task.context, node) iscsi_deploy.check_image_size(task) deploy_ramdisk_opts = iscsi_deploy.build_deploy_ramdisk_options(node) deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task) deploy_ramdisk_opts['BOOTIF'] = deploy_nic_mac deploy_iso_uuid = node.driver_info['ilo_deploy_iso'] deploy_iso = 'glance:' + deploy_iso_uuid _reboot_into(task, deploy_iso, deploy_ramdisk_opts) return states.DEPLOYWAIT
def test_node_power_action_in_same_state(self): """Test setting node state to its present state. Test that we don't try to set the power state if the requested state is the same as the current state. """ node = obj_utils.create_test_node(self.context, uuid=uuidutils.generate_uuid(), driver='fake', last_error='anything but None', power_state=states.POWER_ON) task = task_manager.TaskManager(self.context, node.uuid) with mock.patch.object(self.driver.power, 'get_power_state') as get_power_mock: get_power_mock.return_value = states.POWER_ON with mock.patch.object(self.driver.power, 'set_power_state') as set_power_mock: conductor_utils.node_power_action(task, states.POWER_ON) node.refresh() get_power_mock.assert_called_once_with(mock.ANY) self.assertFalse(set_power_mock.called, "set_power_state unexpectedly called") self.assertEqual(states.POWER_ON, node['power_state']) self.assertIsNone(node['target_power_state']) self.assertIsNone(node['last_error'])
def prepare_inband_cleaning(task, manage_boot=True): """Prepares the node to boot into agent for in-band cleaning. This method does the following: 1. Prepares the cleaning ports for the bare metal node and updates the clean parameters in node's driver_internal_info. 2. If 'manage_boot' parameter is set to true, it also calls the 'prepare_ramdisk' method of boot interface to boot the agent ramdisk. 3. Reboots the bare metal node. :param task: a TaskManager object containing the node :param manage_boot: If this is set to True, this method calls the 'prepare_ramdisk' method of boot interface to boot the agent ramdisk. If False, it skips preparing the boot agent ramdisk using boot interface, and assumes that the environment is setup to automatically boot agent ramdisk every time bare metal node is rebooted. :returns: states.CLEANWAIT to signify an asynchronous prepare. :raises NodeCleaningFailure: if the previous cleaning ports cannot be removed or if new cleaning ports cannot be created """ prepare_cleaning_ports(task) # Append required config parameters to node's driver_internal_info # to pass to IPA. agent_add_clean_params(task) if manage_boot: ramdisk_opts = build_agent_options(task.node) task.driver.boot.prepare_ramdisk(task, ramdisk_opts) manager_utils.node_power_action(task, states.REBOOT) # Tell the conductor we are waiting for the agent to boot. return states.CLEANWAIT
def test_node_power_action_in_same_state_db_not_in_sync(self): """Test setting node state to its present state if DB is out of sync. Under rare conditions (see bug #1403106) database might contain stale information, make sure we fix it. """ node = obj_utils.create_test_node(self.context, uuid=uuidutils.generate_uuid(), driver='fake', last_error='anything but None', power_state=states.POWER_ON) task = task_manager.TaskManager(self.context, node.uuid) with mock.patch.object(self.driver.power, 'get_power_state') as get_power_mock: get_power_mock.return_value = states.POWER_OFF with mock.patch.object(self.driver.power, 'set_power_state') as set_power_mock: conductor_utils.node_power_action(task, states.POWER_OFF) node.refresh() get_power_mock.assert_called_once_with(mock.ANY) self.assertFalse(set_power_mock.called, "set_power_state unexpectedly called") self.assertEqual(states.POWER_OFF, node['power_state']) self.assertIsNone(node['target_power_state']) self.assertIsNone(node['last_error'])
def deploy(self, task): """Start deployment of the task's node'. Fetches instance image, creates a temporary keystone token file, updates the DHCP port options for next boot, and issues a reboot request to the power driver. This causes the node to boot into the deployment ramdisk and triggers the next phase of PXE-based deployment via VendorPassthru.pass_deploy_info(). :param task: a TaskManager instance containing the node to act on. :returns: deploy state DEPLOYWAIT. """ iscsi_deploy.cache_instance_image(task.context, task.node) iscsi_deploy.check_image_size(task) # TODO(yuriyz): more secure way needed for pass auth token # to deploy ramdisk _create_token_file(task) dhcp_opts = pxe_utils.dhcp_options_for_instance(task) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) deploy_utils.try_set_boot_device(task, boot_devices.PXE) manager_utils.node_power_action(task, states.REBOOT) return states.DEPLOYWAIT
def clean_up_instance(self, task): """Cleans up the boot of instance. This method cleans up the PXE environment that was setup for booting the instance. It unlinks the instance kernel/ramdisk in the node's directory in tftproot and removes it's PXE config. In case of UEFI iSCSI booting, it cleans up iSCSI target information from the node. :param task: a task from TaskManager. :returns: None :raises: IloOperationError, if some operation on iLO failed. """ manager_utils.node_power_action(task, states.POWER_OFF) disable_secure_boot_if_supported(task) driver_internal_info = task.node.driver_internal_info if (deploy_utils.is_iscsi_boot(task) and task.node.driver_internal_info.get('ilo_uefi_iscsi_boot')): # It will clear iSCSI info from iLO in case of booting from # volume in UEFI boot mode task.driver.management.clear_iscsi_boot_target(task) driver_internal_info.pop('ilo_uefi_iscsi_boot', None) task.node.driver_internal_info = driver_internal_info task.node.save() else: # Volume boot in BIOS boot mode is handled using # PXE boot interface super(IloPXEBoot, self).clean_up_instance(task)
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. :raises: NetworkError: if the previous cleaning ports cannot be removed or if new cleaning ports cannot be created. :raises: InvalidParameterValue when the wrong power state is specified or the wrong driver info is specified for power management. :raises: StorageError If the storage driver is unable to attach the configured volumes. :raises: other exceptions by the node's power driver if something wrong occurred during the power action. :raises: any boot interface's prepare_ramdisk exceptions. """ node = task.node deploy_utils.populate_storage_driver_internal_info(task) if node.provision_state in [states.ACTIVE, states.ADOPTING]: task.driver.boot.prepare_instance(task) else: if node.provision_state == states.DEPLOYING: fast_track_deploy = manager_utils.is_fast_track(task) if fast_track_deploy: # The agent has already recently checked in and we are # configured to take that as an indicator that we can # skip ahead. LOG.debug('The agent for node %(node)s has recently ' 'checked in, and the node power will remain ' 'unmodified.', {'node': task.node.uuid}) else: # Adding the node to provisioning network so that the dhcp # options get added for the provisioning port. manager_utils.node_power_action(task, states.POWER_OFF) # NOTE(vdrok): in case of rebuild, we have tenant network # already configured, unbind tenant ports if present if task.driver.storage.should_write_image(task): if not fast_track_deploy: power_state_to_restore = ( manager_utils.power_on_node_if_needed(task)) task.driver.network.unconfigure_tenant_networks(task) task.driver.network.add_provisioning_network(task) if not fast_track_deploy: manager_utils.restore_power_state_if_needed( task, power_state_to_restore) task.driver.storage.attach_volumes(task) if (not task.driver.storage.should_write_image(task) or fast_track_deploy): # We have nothing else to do as this is handled in the # backend storage system, and we can return to the caller # as we do not need to boot the agent to deploy. # Alternatively, we are in a fast track deployment # and have nothing else to do. return deploy_opts = deploy_utils.build_agent_options(node) task.driver.boot.prepare_ramdisk(task, deploy_opts)
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. :raises: NetworkError: if the previous cleaning ports cannot be removed or if new cleaning ports cannot be created. :raises: InvalidParameterValue when the wrong power state is specified or the wrong driver info is specified for power management. :raises: other exceptions by the node's power driver if something wrong occurred during the power action. :raises: any boot interface's prepare_ramdisk exceptions. """ node = task.node if node.provision_state in [states.ACTIVE, states.ADOPTING]: task.driver.boot.prepare_instance(task) else: if node.provision_state == states.DEPLOYING: # Adding the node to provisioning network so that the dhcp # options get added for the provisioning port. manager_utils.node_power_action(task, states.POWER_OFF) task.driver.network.add_provisioning_network(task) deploy_opts = deploy_utils.build_agent_options(node) task.driver.boot.prepare_ramdisk(task, deploy_opts)
def tear_down(self, task): """Tear down a previous deployment on the task's node. Power off the node. All actual clean-up is done in the clean_up() method which should be called separately. :param task: a TaskManager instance containing the node to act on. :returns: deploy state DELETED. :raises: NetworkError if the cleaning ports cannot be removed. :raises: InvalidParameterValue when the wrong state is specified or the wrong driver info is specified. :raises: StorageError when volume detachment fails. :raises: other exceptions by the node's power driver if something wrong occurred during the power action. """ manager_utils.node_power_action(task, states.POWER_OFF) task.driver.storage.detach_volumes(task) deploy_utils.tear_down_storage_configuration(task) power_state_to_restore = manager_utils.power_on_node_if_needed(task) task.driver.network.unconfigure_tenant_networks(task) # NOTE(mgoddard): If the deployment was unsuccessful the node may have # ports on the provisioning network which were not deleted. task.driver.network.remove_provisioning_network(task) manager_utils.restore_power_state_if_needed( task, power_state_to_restore) return states.DELETED
def tear_down(self, task, node): """Tear down a previous deployment. Given a node that has been previously deployed to, do all cleanup and tear down necessary to "un-deploy" that node. :param task: a TaskManager instance. :param node: the Node to act upon. :returns: deploy state DELETED. """ #FIXME(ghe): Possible error to get image info if eliminated from glance # Retrieve image info and store in db # If we keep master images, no need to get the info, we may ignore this pxe_info = _get_tftp_image_info(node) d_info = _parse_driver_info(node) for label in pxe_info: (uuid, path) = pxe_info[label] master_path = os.path.join(CONF.pxe.tftp_master_path, uuid) utils.unlink_without_raise(path) _unlink_master_image(master_path) utils.unlink_without_raise(_get_pxe_config_file_path( node['instance_uuid'])) for port in _get_node_mac_addresses(task, node): mac_path = _get_pxe_mac_path(port) utils.unlink_without_raise(mac_path) utils.rmtree_without_raise( os.path.join(CONF.pxe.tftp_root, node['instance_uuid'])) _destroy_images(d_info) manager_utils.node_power_action(task, node, states.POWER_OFF) return states.DELETED
def _cleaning_reboot(task): """Reboots a node out of band after a clean step that requires it. If an agent clean step has 'reboot_requested': True, reboots the node when the step is completed. Will put the node in CLEANFAIL if the node cannot be rebooted. :param task: a TaskManager instance """ try: # NOTE(fellypefca): Call prepare_ramdisk on ensure that the # baremetal node boots back into the ramdisk after reboot. deploy_opts = deploy_utils.build_agent_options(task.node) task.driver.boot.prepare_ramdisk(task, deploy_opts) manager_utils.node_power_action(task, states.REBOOT) except Exception as e: msg = (_('Reboot requested by clean step %(step)s failed for ' 'node %(node)s: %(err)s') % {'step': task.node.clean_step, 'node': task.node.uuid, 'err': e}) LOG.error(msg, exc_info=not isinstance(e, exception.IronicException)) # do not set cleaning_reboot if we didn't reboot manager_utils.cleaning_error_handler(task, msg) return # Signify that we've rebooted driver_internal_info = task.node.driver_internal_info driver_internal_info['cleaning_reboot'] = True task.node.driver_internal_info = driver_internal_info task.node.save()
def finish_deploy(task, address): """Notifies the ramdisk to reboot the node and makes the instance active. This method notifies the ramdisk to proceed to reboot and then makes the instance active. :param task: a TaskManager object. :param address: The IP address of the bare metal node. :raises: InstanceDeployFailure, if notifying ramdisk failed. """ node = task.node try: deploy_utils.notify_ramdisk_to_proceed(address) 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 notify ramdisk to reboot after bootloader " "installation. Error: %s") % e deploy_utils.set_failed_state(task, msg) raise exception.InstanceDeployFailure(msg) # TODO(lucasagomes): When deploying a node with the DIB ramdisk # Ironic will not power control the node at the end of the deployment, # it's the DIB ramdisk that reboots the node. But, for the SSH driver # some changes like setting the boot device only gets applied when the # machine is powered off and on again. So the code below is enforcing # it. For Liberty we need to change the DIB ramdisk so that Ironic # always controls the power state of the node for all drivers. if deploy_utils.get_boot_option(node) == "local" and "ssh" in node.driver: manager_utils.node_power_action(task, states.REBOOT) LOG.info(_LI("Deployment to node %s done"), node.uuid) task.process_event("done")
def deploy(self, task): """Start deployment of the task's node'. Config host file and xcat dhcp, generate image info for xcat and issues a reboot request to the power driver. This causes the node to boot into the deployment ramdisk and triggers the next phase of PXE-based deployment via VendorPassthru._continue_deploy(). :param task: a TaskManager instance containing the node to act on. :returns: deploy state DEPLOYDONE. """ d_info = _parse_deploy_info(task.node) if not task.node.instance_info.get('fixed_ip_address') or not task.node.instance_info.get('image_name'): raise exception.InvalidParameterValue self._config_host_file(d_info,task.node.instance_info.get('fixed_ip_address')) self._make_dhcp() self._nodeset_osimage(d_info,task.node.instance_info.get('image_name')) manager_utils.node_set_boot_device(task, 'pxe', persistent=True) manager_utils.node_power_action(task, states.REBOOT) try: self._wait_for_node_deploy(task) except xcat_exception.xCATDeploymentFailure: LOG.info(_("xcat deployment failed")) return states.ERROR return states.DEPLOYDONE
def _set_failed_state(task, msg): """Set a node's error state and provision state to signal Nova. When deploy steps aren't called by explicitly the conductor, but are the result of callbacks, we need to set the node's state explicitly. This tells Nova to change the instance's status so the user can see their deploy/tear down had an issue and makes debugging/deleting Nova instances easier. """ node = task.node 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)
def _reboot_into_deploy_iso(task, ramdisk_options): """Reboots the node into a given deploy ISO. This method attaches the given deploy ISO as virtual media, prepares the arguments for ramdisk in virtual media floppy, and then reboots the node. :param task: a TaskManager instance containing the node to act on. :param ramdisk_options: the options to be passed to the ramdisk in virtual media floppy. :raises: ImageRefValidationFailed if no image service can handle specified href. :raises: ImageCreationFailed, if it failed while creating the floppy image. :raises: IRMCOperationError, if some operation on iRMC failed. :raises: InvalidParameterValue if the validation of the PowerInterface or ManagementInterface fails. """ d_info = task.node.driver_info deploy_iso_href = d_info['irmc_deploy_iso'] if service_utils.is_image_href_ordinary_file_name(deploy_iso_href): deploy_iso_file = deploy_iso_href else: deploy_iso_file = _get_deploy_iso_name(task.node) deploy_iso_fullpathname = os.path.join( CONF.irmc.remote_image_share_root, deploy_iso_file) images.fetch(task.context, deploy_iso_href, deploy_iso_fullpathname) setup_vmedia_for_boot(task, deploy_iso_file, ramdisk_options) manager_utils.node_set_boot_device(task, boot_devices.CDROM) manager_utils.node_power_action(task, states.REBOOT)
def clean_up_instance(self, task): """Cleans up the boot of instance. This method cleans up the environment that was setup for booting the instance. It ejects virtual media. In case of UEFI iSCSI booting, it cleans up iSCSI target information from the node. :param task: a task from TaskManager. :returns: None :raises: IloOperationError, if some operation on iLO failed. """ LOG.debug("Cleaning up the instance.") manager_utils.node_power_action(task, states.POWER_OFF) disable_secure_boot_if_supported(task) driver_internal_info = task.node.driver_internal_info if (deploy_utils.is_iscsi_boot(task) and task.node.driver_internal_info.get('ilo_uefi_iscsi_boot')): # It will clear iSCSI info from iLO task.driver.management.clear_iscsi_boot_target(task) driver_internal_info.pop('ilo_uefi_iscsi_boot', None) else: _clean_up_boot_iso_for_instance(task.node) driver_internal_info.pop('boot_iso_created_in_web_server', None) ilo_common.cleanup_vmedia_boot(task) task.node.driver_internal_info = driver_internal_info task.node.save()
def set_failed_state(task, msg): """Sets the deploy status as failed with relevant messages. This method sets the deployment as fail with the given message. It sets node's provision_state to DEPLOYFAIL and updates last_error with the given error message. It also powers off the baremetal node. :param task: a TaskManager instance containing the node to act on. :param msg: the message to set in last_error of the node. """ node = task.node try: task.process_event('fail') except exception.InvalidState: msg2 = (_LE('Internal error. Node %(node)s in provision state ' '"%(state)s" could not transition to a failed state.') % {'node': node.uuid, 'state': node.provision_state}) LOG.exception(msg2) try: manager_utils.node_power_action(task, states.POWER_OFF) except Exception: msg2 = (_LE('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.exception(msg2) # NOTE(deva): node_power_action() erases node.last_error # so we need to set it here. node.last_error = msg node.save()
def deploy(self, task): """Start deployment of the task's node'. Fetches instance image, creates a temporary keystone token file, updates the Neutron DHCP port options for next boot, and issues a reboot request to the power driver. This causes the node to boot into the deployment ramdisk and triggers the next phase of PXE-based deployment via VendorPassthru._continue_deploy(). :param task: a TaskManager instance containing the node to act on. :returns: deploy state DEPLOYING. """ _cache_instance_image(task.context, task.node) _check_image_size(task) # TODO(yuriyz): more secure way needed for pass auth token # to deploy ramdisk _create_token_file(task) dhcp_opts = pxe_utils.dhcp_options_for_instance() neutron.update_neutron(task, dhcp_opts) manager_utils.node_set_boot_device(task, 'pxe', persistent=True) manager_utils.node_power_action(task, states.REBOOT) return states.DEPLOYWAIT
def test_node_power_action_invalid_state(self): """Test for exception when changing to an invalid power state.""" node = obj_utils.create_test_node(self.context, uuid=uuidutils.generate_uuid(), driver='fake', power_state=states.POWER_ON) task = task_manager.TaskManager(self.context, node.uuid) with mock.patch.object(self.driver.power, 'get_power_state') as get_power_mock: get_power_mock.return_value = states.POWER_ON self.assertRaises(exception.InvalidParameterValue, conductor_utils.node_power_action, task, "INVALID_POWER_STATE") node.refresh() get_power_mock.assert_called_once_with(mock.ANY) self.assertEqual(states.POWER_ON, node['power_state']) self.assertIsNone(node['target_power_state']) self.assertIsNotNone(node['last_error']) # last_error is cleared when a new transaction happens conductor_utils.node_power_action(task, states.POWER_OFF) node.refresh() self.assertEqual(states.POWER_OFF, node['power_state']) self.assertIsNone(node['target_power_state']) self.assertIsNone(node['last_error'])
def tear_down(self, task): """Tear down a previous deployment on the task's node. :param task: a TaskManager instance. :returns: status of the deploy. One of ironic.common.states. """ manager_utils.node_power_action(task, states.POWER_OFF) return states.DELETED
def deploy(self, task): """Start deployment of the task's node'. Fetches instance image, creates a temporary keystone token file, updates the DHCP port options for next boot, and issues a reboot request to the power driver. This causes the node to boot into the deployment ramdisk and triggers the next phase of PXE-based deployment via VendorPassthru._continue_deploy(). :param task: a TaskManager instance containing the node to act on. :returns: deploy state DEPLOYWAIT. """ iscsi_deploy.cache_instance_image(task.context, task.node) iscsi_deploy.check_image_size(task) # TODO(yuriyz): more secure way needed for pass auth token # to deploy ramdisk _create_token_file(task) dhcp_opts = pxe_utils.dhcp_options_for_instance(task) provider = dhcp_factory.DHCPFactory() provider.update_dhcp(task, dhcp_opts) # NOTE(faizan): Under UEFI boot mode, setting of boot device may differ # between different machines. IPMI does not work for setting boot # devices in UEFI mode for certain machines. # Expected IPMI failure for uefi boot mode. Logging a message to # set the boot device manually and continue with deploy. try: manager_utils.node_set_boot_device(task, 'pxe', persistent=True) except exception.IPMIFailure: if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi': LOG.warning( _LW("ipmitool is unable to set boot device while " "the node is in UEFI boot mode." "Please set the boot device manually.")) else: raise manager_utils.node_power_action(task, states.REBOOT) return states.DEPLOYWAIT
def tear_down(self, task, node): """Tear down a previous deployment. Power off the node. All actual clean-up is done in the clean_up() method which should be called separately. :param task: a TaskManager instance. :param node: the Node to act upon. :returns: deploy state DELETED. """ manager_utils.node_power_action(task, node, states.POWER_OFF) # Remove the internal pxe_deploy_key attribute driver_info = node.driver_info if driver_info.pop('pxe_deploy_key', None): node.driver_info = driver_info node.save(task.context) return states.DELETED
def tear_down(self, task): """Tear down a previous deployment on the task's node. :param task: a TaskManager instance. :returns: status of the deploy. One of ironic.common.states. :raises: NetworkError if the cleaning ports cannot be removed. :raises: InvalidParameterValue when the wrong power state is specified or the wrong driver info is specified for power management. :raises: StorageError when the storage interface attached volumes fail to detach. :raises: other exceptions by the node's power driver if something wrong occurred during the power action. """ manager_utils.node_power_action(task, states.POWER_OFF) task.driver.storage.detach_volumes(task) deploy_utils.tear_down_storage_configuration(task) task.driver.network.unconfigure_tenant_networks(task) return states.DELETED
def set_failed_state(task, msg, collect_logs=True): """Sets the deploy status as failed with relevant messages. This method sets the deployment as fail with the given message. It sets node's provision_state to DEPLOYFAIL and updates last_error with the given error message. It also powers off the baremetal node. :param task: a TaskManager instance containing the node to act on. :param msg: the message to set in logs and last_error of the node. :param collect_logs: Boolean indicating whether to attempt to collect logs from IPA-based ramdisk. Defaults to True. Actual log collection is also affected by CONF.agent.deploy_logs_collect config option. """ node = task.node if (collect_logs and CONF.agent.deploy_logs_collect in ('on_failure', 'always')): driver_utils.collect_ramdisk_logs(node) try: manager_utils.deploying_error_handler(task, msg, msg, clean_up=False) except exception.InvalidState: msg2 = ('Internal error. Node %(node)s in provision state ' '"%(state)s" could not transition to a failed state.' % { 'node': node.uuid, 'state': node.provision_state }) LOG.exception(msg2) if CONF.deploy.power_off_after_deploy_failure: try: manager_utils.node_power_action(task, states.POWER_OFF) except Exception: msg2 = ('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.exception(msg2) # NOTE(tenbrae): node_power_action() erases node.last_error # so we need to set it here. node.last_error = msg node.save()
def boot_into_iso(self, task, **kwargs): """Attaches an ISO image in glance and reboots bare metal. This method accepts an ISO image href (a Glance UUID or an HTTP(S) URL) attaches it as virtual media and then reboots the node. This is useful for debugging purposes. This can be invoked only when the node is in manage state. :param task: A TaskManager object. :param kwargs: The arguments sent with vendor passthru. The expected kwargs are:: 'boot_iso_href': href of the image to be booted. This can be a Glance UUID or an HTTP(S) URL. """ ilo_common.setup_vmedia(task, kwargs['boot_iso_href'], ramdisk_options=None) manager_utils.node_power_action(task, states.REBOOT)
def _start_managed_inspection(task): """Start inspection managed by ironic.""" try: client = _get_client(task.context) endpoint = _get_callback_endpoint(client) params = dict(_parse_kernel_params(), **{'ipa-inspection-callback-url': endpoint}) cond_utils.node_power_action(task, states.POWER_OFF) with cond_utils.power_state_for_network_configuration(task): task.driver.network.add_inspection_network(task) task.driver.boot.prepare_ramdisk(task, ramdisk_params=params) client.start_introspection(task.node.uuid, manage_boot=False) cond_utils.node_power_action(task, states.POWER_ON) except Exception as exc: LOG.exception('Unable to start managed inspection for node %(uuid)s: ' '%(err)s', {'uuid': task.node.uuid, 'err': exc}) error = _('unable to start inspection: %s') % exc _inspection_error_handler(task, error, raise_exc=True)
def test_node_power_action_power_off(self): """Test node_power_action to turn node power off.""" node = obj_utils.create_test_node(self.context, uuid=cmn_utils.generate_uuid(), driver='fake', power_state=states.POWER_ON) task = task_manager.TaskManager(self.context, node.uuid) with mock.patch.object(self.driver.power, 'get_power_state') as get_power_mock: get_power_mock.return_value = states.POWER_ON conductor_utils.node_power_action(task, states.POWER_OFF) node.refresh() get_power_mock.assert_called_once_with(mock.ANY) self.assertEqual(states.POWER_OFF, node['power_state']) self.assertIsNone(node['target_power_state']) self.assertIsNone(node['last_error'])
def unrescue(self, task): """Attempt to move a rescued node back to active state. :param task: a TaskManager instance. :raises: NetworkError if the rescue ports cannot be removed. :raises: InvalidParameterValue when the wrong power state is specified or the wrong driver info is specified for power management. :raises: other exceptions by the node's power driver if something wrong occurred during the power action. :raises: any boot interface's prepare_instance exceptions. :returns: Returns states.ACTIVE """ manager_utils.node_power_action(task, states.POWER_OFF) self.clean_up(task) task.driver.network.configure_tenant_networks(task) task.driver.boot.prepare_instance(task) manager_utils.node_power_action(task, states.POWER_ON) return states.ACTIVE
def reboot_and_finish_deploy(self, task): """Helper method to trigger reboot on the node and finish deploy. This method initiates a reboot on the node. On success, it marks the deploy as complete. On failure, it logs the error and marks deploy as failure. :param task: a TaskManager object containing the node :raises: InstanceDeployFailure, if node reboot failed. """ try: manager_utils.node_power_action(task, states.REBOOT) except Exception as e: msg = (_('Error rebooting node %(node)s. Error: %(error)s') % {'node': task.node.uuid, 'error': e}) self._log_and_raise_deployment_error(task, msg) task.process_event('done') LOG.info(_LI('Deployment to node %s done'), task.node.uuid)
def test_node_power_action_already_being_processed(self): """The target_power_state is expected to be None so it isn't checked in the code. This is what happens if it is not None. (Eg, if a conductor had died during a previous power-off attempt and left the target_power_state set to states.POWER_OFF, and the user is attempting to power-off again.) """ node = obj_utils.create_test_node(self.context, uuid=cmn_utils.generate_uuid(), driver='fake', power_state=states.POWER_ON, target_power_state=states.POWER_OFF) task = task_manager.TaskManager(self.context, node.uuid) conductor_utils.node_power_action(task, states.POWER_OFF) node.refresh() self.assertEqual(states.POWER_OFF, node['power_state']) self.assertEqual(states.NOSTATE, node['target_power_state']) self.assertIsNone(node['last_error'])
def _reboot_to_instance(self, task, **kwargs): node = task.node LOG.debug('Preparing to reboot to instance for node %s', node.uuid) error = self._check_deploy_success(node) if error is not None: # TODO(jimrollenhagen) power off if using neutron dhcp to # align with pxe driver? msg = _('node %(node)s command status errored: %(error)s') % ( {'node': node.uuid, 'error': error}) LOG.error(msg) deploy_utils.set_failed_state(task, msg) return LOG.debug('Rebooting node %s to disk', node.uuid) manager_utils.node_set_boot_device(task, 'disk', persistent=True) manager_utils.node_power_action(task, states.REBOOT) task.process_event('done')
def deploy(self, task, node): """Perform start deployment a node. Creates a temporary keystone token file, updates the Neutron DHCP port options for next boot, and issues a reboot request to the power driver. This causes the node to boot into the deployment ramdisk and triggers the next phase of PXE-based deployment via VendorPassthru._continue_deploy(). :param task: a TaskManager instance. :param node: the Node to act upon. :returns: deploy state DEPLOYING. """ # TODO(yuriyz): more secure way needed for pass auth token # to deploy ramdisk _create_token_file(task, node) _update_neutron(task, node) manager_utils.node_power_action(task, node, states.REBOOT) return states.DEPLOYWAIT
def deploy(self, task): """Start deployment of the task's node. Fetches instance image, creates a temporary keystone token file, updates the DHCP port options for next boot, and issues a reboot request to the power driver. This causes the node to boot into the deployment ramdisk and triggers the next phase of PXE-based deployment via VendorPassthru.pass_deploy_info(). :param task: a TaskManager instance containing the node to act on. :returns: deploy state DEPLOYWAIT. """ node = task.node cache_instance_image(task.context, node) check_image_size(task) manager_utils.node_power_action(task, states.REBOOT) return states.DEPLOYWAIT
def tear_down(self, task): """Tear down a previous deployment on the task's node. Power off the node. All actual clean-up is done in the clean_up() method which should be called separately. :param task: a TaskManager instance containing the node to act on. :returns: deploy state DELETED. :raises: NetworkError if the cleaning ports cannot be removed. :raises: InvalidParameterValue when the wrong state is specified or the wrong driver info is specified. :raises: StorageError when volume detachment fails. :raises: other exceptions by the node's power driver if something wrong occurred during the power action. """ manager_utils.node_power_action(task, states.POWER_OFF) task.driver.storage.detach_volumes(task) deploy_utils.tear_down_storage_configuration(task) task.driver.network.unconfigure_tenant_networks(task) return states.DELETED
def _reboot_into(task, iso, ramdisk_options): """Reboots the node into a given boot ISO. This method attaches the given bootable ISO as virtual media, prepares the arguments for ramdisk in virtual media floppy, and then reboots the node. :param task: a TaskManager instance containing the node to act on. :param iso: a bootable ISO image to attach to. The boot iso should be present in either Glance or in Swift. If present in Glance, it should be of format 'glance:<glance-image-uuid>'. If present in Swift, it should be of format 'swift:<object-name>'. It is assumed that object is present in CONF.ilo.swift_ilo_container. :param ramdisk_options: the options to be passed to the ramdisk in virtual media floppy. :raises: ImageCreationFailed, if it failed while creating the floppy image. :raises: IloOperationError, if some operation on iLO failed. """ ilo_common.setup_vmedia_for_boot(task, iso, ramdisk_options) manager_utils.node_set_boot_device(task, boot_devices.CDROM) manager_utils.node_power_action(task, states.REBOOT)
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. :raises: NetworkError: if the previous cleaning ports cannot be removed or if new cleaning ports cannot be created. :raises: InvalidParameterValue when the wrong power state is specified or the wrong driver info is specified for power management. :raises: StorageError If the storage driver is unable to attach the configured volumes. :raises: other exceptions by the node's power driver if something wrong occurred during the power action. :raises: any boot interface's prepare_ramdisk exceptions. """ node = task.node deploy_utils.populate_storage_driver_internal_info(task) if node.provision_state in [states.ACTIVE, states.ADOPTING]: task.driver.boot.prepare_instance(task) else: if node.provision_state == states.DEPLOYING: # Adding the node to provisioning network so that the dhcp # options get added for the provisioning port. manager_utils.node_power_action(task, states.POWER_OFF) # NOTE(vdrok): in case of rebuild, we have tenant network # already configured, unbind tenant ports if present if task.driver.storage.should_write_image(task): task.driver.network.unconfigure_tenant_networks(task) task.driver.network.add_provisioning_network(task) task.driver.storage.attach_volumes(task) if not task.driver.storage.should_write_image(task): # We have nothing else to do as this is handled in the # backend storage system, and we can return to the caller # as we do not need to boot the agent to deploy. return deploy_opts = deploy_utils.build_agent_options(node) task.driver.boot.prepare_ramdisk(task, deploy_opts)
def test_node_power_action_power_soft_reboot_timeout(self): """Test for soft reboot a node.""" node = obj_utils.create_test_node(self.context, uuid=uuidutils.generate_uuid(), driver='fake_soft_power', power_state=states.POWER_ON) task = task_manager.TaskManager(self.context, node.uuid) with mock.patch.object(self.driver.power, 'get_power_state') as get_power_mock: get_power_mock.return_value = states.POWER_ON conductor_utils.node_power_action(task, states.SOFT_REBOOT, timeout=2) node.refresh() get_power_mock.assert_called_once_with(mock.ANY) self.assertEqual(states.POWER_ON, node['power_state']) self.assertIsNone(node['target_power_state']) self.assertIsNone(node['last_error'])
def prepare(self, task): """Prepare the deployment environment for this node.""" node = task.node # TODO(pas-ha) investigate takeover scenario if node.provision_state == states.DEPLOYING: # adding network-driver dependent provisioning ports fast_track = manager_utils.is_fast_track(task) power_state_to_restore = None if not fast_track: manager_utils.node_power_action(task, states.POWER_OFF) power_state_to_restore = ( manager_utils.power_on_node_if_needed(task)) task.driver.network.add_provisioning_network(task) manager_utils.restore_power_state_if_needed( task, power_state_to_restore) if node.provision_state not in [states.ACTIVE, states.ADOPTING]: node.instance_info = deploy_utils.build_instance_info_for_deploy( task) node.save() boot_opt = deploy_utils.build_agent_options(node) task.driver.boot.prepare_ramdisk(task, boot_opt)
def tear_down(self, task): """Tear down a previous deployment on the task's node. Power off the node. All actual clean-up is done in the clean_up() method which should be called separately. :param task: a TaskManager instance containing the node to act on. :returns: deploy state DELETED. """ manager_utils.node_power_action(task, states.POWER_OFF) try: _update_secure_boot_mode(task, False) # We need to handle IloOperationNotSupported exception so that if # the user has incorrectly specified the Node capability # 'secure_boot' to a node that does not have that capability and # attempted deploy. Handling this exception here, will help the # user to tear down such a Node. except exception.IloOperationNotSupported: LOG.warn(_LW('Secure boot mode is not supported for node %s'), task.node.uuid) return states.DELETED
def deploy(self, task): """Perform a deployment to a node.""" manager_utils.node_power_action(task, states.REBOOT) if CONF.ansible.use_ramdisk_callback: return states.DEPLOYWAIT node = task.node ip_addr = _get_node_ip(task) try: _deploy(task, ip_addr) except Exception as e: error = _('Deploy failed for node %(node)s: ' 'Error: %(exc)s') % { 'node': node.uuid, 'exc': six.text_type(e) } LOG.exception(error) self._set_failed_state(task, error) else: LOG.info(_LI('Deployment to node %s done'), node.uuid) return states.DEPLOYDONE
def prepare_cleaning(self, task): """Boot into the ramdisk to prepare for cleaning. :param task: a TaskManager object containing the node :raises NodeCleaningFailure: if the previous cleaning ports cannot be removed or if new cleaning ports cannot be created :returns: None or states.CLEANWAIT for async prepare. """ node = task.node conductor_steps.set_node_cleaning_steps(task) if not node.driver_internal_info['clean_steps']: # no clean steps configured, nothing to do. return power_state_to_restore = manager_utils.power_on_node_if_needed(task) task.driver.network.add_cleaning_network(task) manager_utils.restore_power_state_if_needed(task, power_state_to_restore) boot_opt = deploy_utils.build_agent_options(node) task.driver.boot.prepare_ramdisk(task, boot_opt) manager_utils.node_power_action(task, states.REBOOT) return states.CLEANWAIT
def deploy(self, task): """Perform a deployment to a node. Perform the necessary work to deploy an image onto the specified node. This method will be called after prepare(), which may have already performed any preparatory steps, such as pre-caching some data for the node. :param task: a TaskManager instance. :returns: status of the deploy. One of ironic.common.states. """ if task.driver.storage.should_write_image(task): manager_utils.node_power_action(task, states.REBOOT) return states.DEPLOYWAIT else: # TODO(TheJulia): At some point, we should de-dupe this code # as it is nearly identical to the iscsi deploy interface. # This is not being done now as it is expected to be # refactored in the near future. manager_utils.node_power_action(task, states.POWER_OFF) task.driver.network.remove_provisioning_network(task) task.driver.network.configure_tenant_networks(task) task.driver.boot.prepare_instance(task) manager_utils.node_power_action(task, states.POWER_ON) LOG.info('Deployment to node %s done', task.node.uuid) return states.DEPLOYDONE
def deploy(self, task): """Start deployment of the task's node. Fetches instance image, updates the DHCP port options for next boot, and issues a reboot request to the power driver. This causes the node to boot into the deployment ramdisk and triggers the next phase of PXE-based deployment via agent heartbeats. :param task: a TaskManager instance containing the node to act on. :returns: deploy state DEPLOYWAIT. """ node = task.node if task.driver.storage.should_write_image(task): deploy_utils.cache_instance_image(task.context, node) check_image_size(task) manager_utils.node_power_action(task, states.REBOOT) return states.DEPLOYWAIT else: # TODO(TheJulia): At some point, we should de-dupe this code # as it is nearly identical to the agent deploy interface. # This is not being done now as it is expected to be # refactored in the near future. manager_utils.node_power_action(task, states.POWER_OFF) task.driver.network.remove_provisioning_network(task) task.driver.network.configure_tenant_networks(task) task.driver.boot.prepare_instance(task) manager_utils.node_power_action(task, states.POWER_ON) return None
def prepare_cleaning(self, task): """Boot into the ramdisk to prepare for cleaning. :param task: a TaskManager object containing the node :raises NodeCleaningFailure: if the previous cleaning ports cannot be removed or if new cleaning ports cannot be created :returns: None or states.CLEANWAIT for async prepare. """ node = task.node use_callback = CONF.ansible.use_ramdisk_callback if use_callback: manager_utils.set_node_cleaning_steps(task) if not node.driver_internal_info['clean_steps']: # no clean steps configured, nothing to do. return task.driver.network.add_cleaning_network(task) boot_opt = deploy_utils.build_agent_options(node) task.driver.boot.prepare_ramdisk(task, boot_opt) manager_utils.node_power_action(task, states.REBOOT) if use_callback: return states.CLEANWAIT ip_addr = _get_node_ip(task) LOG.debug('IP of node %(node)s is %(ip)s', { 'node': node.uuid, 'ip': ip_addr }) driver_internal_info = node.driver_internal_info driver_internal_info['ansible_cleaning_ip'] = ip_addr node.driver_internal_info = driver_internal_info node.save() playbook, user, key = _parse_ansible_driver_info(task.node, action='clean') node_list = [(node.uuid, ip_addr, user, node.extra)] extra_vars = _prepare_extra_vars(node_list) LOG.debug('Waiting ramdisk on node %s for cleaning', node.uuid) _run_playbook(playbook, extra_vars, key, tags=['wait']) LOG.info(_LI('Node %s is ready for cleaning'), node.uuid)
def test_node_power_action_power_on_notify(self, mock_notif): """Test node_power_action to power on node and send notification.""" self.config(notification_level='info') self.config(host='my-host') # Required for exception handling mock_notif.__name__ = 'NodeSetPowerStateNotification' node = obj_utils.create_test_node(self.context, uuid=uuidutils.generate_uuid(), driver='fake', power_state=states.POWER_OFF) task = task_manager.TaskManager(self.context, node.uuid) with mock.patch.object(self.driver.power, 'get_power_state') as get_power_mock: get_power_mock.return_value = states.POWER_OFF conductor_utils.node_power_action(task, states.POWER_ON) node.refresh() get_power_mock.assert_called_once_with(mock.ANY) self.assertEqual(states.POWER_ON, node.power_state) self.assertIsNone(node.target_power_state) self.assertIsNone(node.last_error) # 2 notifications should be sent: 1 .start and 1 .end self.assertEqual(2, mock_notif.call_count) self.assertEqual(2, mock_notif.return_value.emit.call_count) first_notif_args = mock_notif.call_args_list[0][1] second_notif_args = mock_notif.call_args_list[1][1] self.assertNotificationEqual(first_notif_args, 'ironic-conductor', CONF.host, 'baremetal.node.power_set.start', obj_fields.NotificationLevel.INFO) self.assertNotificationEqual(second_notif_args, 'ironic-conductor', CONF.host, 'baremetal.node.power_set.end', obj_fields.NotificationLevel.INFO)
def one_button_secure_erase(self, task): """Erase the whole system securely. The One-button secure erase process resets iLO and deletes all licenses stored there, resets BIOS settings, and deletes all Active Health System (AHS) and warranty data stored on the system. It also erases supported non-volatile storage data and deletes any deployment setting profiles. :param task: a TaskManager instance. :raises: IloError on an error from iLO. """ node = task.node LOG.info("Calling one button secure erase for node %(node)s", {'node': node.uuid}) try: ilo_object = ilo_common.get_ilo_object(node) ilo_object.do_one_button_secure_erase() manager_utils.node_power_action(task, states.REBOOT) node.maintenance = True node.maintenance_reason = ( "One Button Secure erase clean step has begun, it will wipe " "data from drives and any non-volatile/persistent storage, " "reset iLO and delete all licenses stored there, reset BIOS " "settings, delete Active Health System (AHS) and warranty " "data stored in the system and delete any deployment settings " "profiles.") node.save() return states.CLEANWAIT except ilo_error.IloError as ilo_exception: log_msg = ("One button secure erase job failed for node " "%(node)s. Message: '%(message)s'." % { 'node': task.node.uuid, 'message': ilo_exception }) manager_utils.cleaning_error_handler(task, log_msg, errmsg=ilo_exception)
def deploy(self, task): """Start deployment of the task's node. Fetches instance image, updates the DHCP port options for next boot, and issues a reboot request to the power driver. This causes the node to boot into the deployment ramdisk and triggers the next phase of PXE-based deployment via agent heartbeats. :param task: a TaskManager instance containing the node to act on. :returns: deploy state DEPLOYWAIT. """ node = task.node if manager_utils.is_fast_track(task): # NOTE(mgoddard): For fast track we can mostly skip this step and # proceed to the next step (i.e. write_image). LOG.debug('Performing a fast track deployment for %(node)s.', {'node': task.node.uuid}) deploy_utils.cache_instance_image(task.context, node) check_image_size(task) # NOTE(dtantsur): while the node is up and heartbeating, we don't # necessary have the deploy steps cached. Force a refresh here. self.refresh_steps(task, 'deploy') elif task.driver.storage.should_write_image(task): # Standard deploy process deploy_utils.cache_instance_image(task.context, node) check_image_size(task) # Check if the driver has already performed a reboot in a previous # deploy step. if not task.node.driver_internal_info.get('deployment_reboot', False): manager_utils.node_power_action(task, states.REBOOT) info = task.node.driver_internal_info info.pop('deployment_reboot', None) info.pop('deployment_uuids', None) task.node.driver_internal_info = info task.node.save() return states.DEPLOYWAIT
def deploy(self, task): if 'configdrive' in task.node.instance_info: LOG.warning('A configuration drive is present with ' 'in the deployment request of node %(node)s. ' 'The configuration drive will be ignored for ' 'this deployment.', {'node': task.node}) manager_utils.node_power_action(task, states.POWER_OFF) # Tenant neworks must enable connectivity to the boot # location, as reboot() can otherwise be very problematic. # IDEA(TheJulia): Maybe a "trusted environment" mode flag # that we otherwise fail validation on for drivers that # require explicit security postures. task.driver.network.configure_tenant_networks(task) # calling boot.prepare_instance will also set the node # to PXE boot, and update PXE templates accordingly task.driver.boot.prepare_instance(task) # Power-on the instance, with PXE prepared, we're done. manager_utils.node_power_action(task, states.POWER_ON) LOG.info('Deployment setup for node %s done', task.node.uuid) return None
def rescue(self, task): """Boot a rescue ramdisk on the node. :param task: a TaskManager instance. :raises: NetworkError if the tenant ports cannot be removed. :raises: InvalidParameterValue when the wrong power state is specified or the wrong driver info is specified for power management. :raises: other exceptions by the node's power driver if something wrong occurred during the power action. :raises: any boot interface's prepare_ramdisk exceptions. :returns: Returns states.RESCUEWAIT """ manager_utils.node_power_action(task, states.POWER_OFF) task.driver.boot.clean_up_instance(task) task.driver.network.unconfigure_tenant_networks(task) task.driver.network.add_rescuing_network(task) if CONF.agent.manage_agent_boot: ramdisk_opts = deploy_utils.build_agent_options(task.node) # prepare_ramdisk will set the boot device task.driver.boot.prepare_ramdisk(task, ramdisk_opts) manager_utils.node_power_action(task, states.POWER_ON) return states.RESCUEWAIT
def tear_down_inband_cleaning(task, manage_boot=True): """Tears down the environment setup for in-band cleaning. This method does the following: 1. Powers off the bare metal node. 2. If 'manage_boot' parameter is set to true, it also calls the 'clean_up_ramdisk' method of boot interface to clean up the environment that was set for booting agent ramdisk. 3. Deletes the cleaning ports which were setup as part of cleaning. :param task: a TaskManager object containing the node :param manage_boot: If this is set to True, this method calls the 'clean_up_ramdisk' method of boot interface to boot the agent ramdisk. If False, it skips this step. :raises: NetworkError, NodeCleaningFailure if the cleaning ports cannot be removed. """ manager_utils.node_power_action(task, states.POWER_OFF) if manage_boot: task.driver.boot.clean_up_ramdisk(task) task.driver.network.remove_cleaning_network(task)