def test_update_neutron(self, mock_gnvi, mock_updo, mock_wait_neutron): opts = pxe_utils.dhcp_options_for_instance() mock_gnvi.return_value = {"port-uuid": "vif-uuid"} with task_manager.acquire(self.context, self.node.uuid) as task: neutron.update_neutron(task, self.node) mock_updo.assertCalleOnceWith("vif-uuid", opts) mock_wait_neutron.assert_called_once_with(task)
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_update_neutron_no_vif_data(self, mock_gnvi, mock_init, mock_wait_neutron): mock_gnvi.return_value = {} with task_manager.acquire(self.context, self.node.uuid) as task: neutron.update_neutron(task, self.node) self.assertFalse(mock_init.called) self.assertFalse(mock_wait_neutron.called)
def test_update_neutron(self, mock_gnvi, mock_updo, mock_wait_neutron): opts = pxe_utils.dhcp_options_for_instance() mock_gnvi.return_value = {'port-uuid': 'vif-uuid'} with task_manager.acquire(self.context, self.node.uuid) as task: neutron.update_neutron(task, self.node) mock_updo.assertCalleOnceWith('vif-uuid', opts) mock_wait_neutron.assert_called_once_with(task)
def test_update_neutron(self, mock_gnvi, mock_updo): opts = tftp.dhcp_options_for_instance(CONF.pxe.pxe_bootfile_name) mock_gnvi.return_value = {'port-uuid': 'vif-uuid'} with task_manager.acquire(self.context, self.node.uuid) as task: neutron.update_neutron(task, self.node) mock_updo.assertCalleOnceWith('vif-uuid', opts)
def test_update_neutron_some_failures(self, mock_gnvi, mock_updo): # confirm update is called twice, one fails, but no exception raised mock_gnvi.return_value = {'p1': 'v1', 'p2': 'v2'} exc = exception.FailedToUpdateDHCPOptOnPort('fake exception') mock_updo.side_effect = [None, exc] with task_manager.acquire(self.context, self.node.uuid) as task: neutron.update_neutron(task, self.node) self.assertEqual(2, mock_updo.call_count)
def test_update_neutron_some_failures(self, mock_gnvi, mock_updo, mock_wait_neutron): # confirm update is called twice, one fails, but no exception raised mock_gnvi.return_value = {"p1": "v1", "p2": "v2"} exc = exception.FailedToUpdateDHCPOptOnPort("fake exception") mock_updo.side_effect = [None, exc] with task_manager.acquire(self.context, self.node.uuid) as task: neutron.update_neutron(task, self.node) self.assertEqual(2, mock_updo.call_count) mock_wait_neutron.assert_called_once_with(task)
def test_update_neutron_some_failures(self, mock_gnvi, mock_updo, mock_wait_neutron): # confirm update is called twice, one fails, but no exception raised mock_gnvi.return_value = {'p1': 'v1', 'p2': 'v2'} exc = exception.FailedToUpdateDHCPOptOnPort('fake exception') mock_updo.side_effect = [None, exc] with task_manager.acquire(self.context, self.node.uuid) as task: neutron.update_neutron(task, self.node) self.assertEqual(2, mock_updo.call_count) mock_wait_neutron.assert_called_once_with(task)
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. """ 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 take_over(self, task): """Take over management of this node from a dead conductor. If conductors' hosts maintain a static relationship to nodes, this method should be implemented by the driver to allow conductors to perform the necessary work during the remapping of nodes to conductors when a conductor joins or leaves the cluster. For example, the PXE driver has an external dependency: Neutron must forward DHCP BOOT requests to a conductor which has prepared the tftpboot environment for the given node. When a conductor goes offline, another conductor must change this setting in Neutron as part of remapping that node's control to itself. This is performed within the `takeover` method. :param task: a TaskManager instance. """ neutron.update_neutron(task, CONF.agent.agent_pxe_bootfile_name)
def take_over(self, task): dhcp_opts = pxe_utils.dhcp_options_for_instance() neutron.update_neutron(task, dhcp_opts)
def take_over(self, task): neutron.update_neutron(task, CONF.pxe.pxe_bootfile_name)
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 _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 test_update_neutron_no_vif_data(self, mock_gnvi, mock_init): mock_gnvi.return_value = {} with task_manager.acquire(self.context, self.node.uuid) as task: neutron.update_neutron(task, self.node) mock_init.assert_not_called()