diff --git a/ironic_staging_drivers/ansible/deploy.py b/ironic_staging_drivers/ansible/deploy.py index d5cdfd2..1877aa3 100644 --- a/ironic_staging_drivers/ansible/deploy.py +++ b/ironic_staging_drivers/ansible/deploy.py @@ -23,6 +23,7 @@ from ironic_lib import utils as irlib_utils from oslo_concurrency import processutils from oslo_config import cfg from oslo_log import log +from oslo_utils import strutils from oslo_utils import units import retrying import six @@ -386,7 +387,10 @@ class AnsibleDeploy(agent_base.HeartbeatMixin, base.DeployInterface): def get_properties(self): """Return the properties of the interface.""" - return COMMON_PROPERTIES + props = COMMON_PROPERTIES.copy() + # NOTE(pas-ha) this is to get the deploy_forces_oob_reboot property + props.update(agent_base.VENDOR_PROPERTIES) + return props def validate(self, task): """Validate the driver-specific Node deployment info.""" @@ -409,7 +413,9 @@ class AnsibleDeploy(agent_base.HeartbeatMixin, base.DeployInterface): def _ansible_deploy(self, task, node_address): """Internal function for deployment to a node.""" - notags = ['wait'] if CONF.ansible.use_ramdisk_callback else [] + notags = ['shutdown'] + if CONF.ansible.use_ramdisk_callback: + notags.append('wait') node = task.node LOG.debug('IP of node %(node)s is %(ip)s', {'node': node.uuid, 'ip': node_address}) @@ -622,21 +628,33 @@ class AnsibleDeploy(agent_base.HeartbeatMixin, base.DeployInterface): return task.driver.power.get_power_state(task) node = task.node + oob_power_off = strutils.bool_from_string( + node.driver_info.get('deploy_forces_oob_reboot', False)) try: - 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}) - # NOTE(pas-ha) flush is a part of deploy playbook - # so if it finished successfully we can safely - # power off the node out-of-band + if not oob_power_off: + try: + node_address = _get_node_ip(task) + playbook, user, key = _parse_ansible_driver_info( + node) + node_list = [(node.uuid, node_address, user, node.extra)] + extra_vars = _prepare_extra_vars(node_list) + _run_playbook(playbook, extra_vars, key, + tags=['shutdown']) + _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': node.uuid, + 'timeout': (wait * (attempts - 1)) / 1000, + 'error': e}) + # NOTE(pas-ha) flush is a part of deploy playbook + # so if it finished successfully we can safely + # power off the node out-of-band + manager_utils.node_power_action(task, states.POWER_OFF) + else: 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) diff --git a/ironic_staging_drivers/ansible/playbooks/deploy.yaml b/ironic_staging_drivers/ansible/playbooks/deploy.yaml index 517b78c..9f75ad4 100644 --- a/ironic_staging_drivers/ansible/playbooks/deploy.yaml +++ b/ironic_staging_drivers/ansible/playbooks/deploy.yaml @@ -9,5 +9,6 @@ - hosts: ironic roles: - - deploy - - shutdown + - role: deploy + - role: shutdown + tags: shutdown diff --git a/ironic_staging_drivers/tests/unit/ansible/test_deploy.py b/ironic_staging_drivers/tests/unit/ansible/test_deploy.py index fd84207..b84d8c7 100644 --- a/ironic_staging_drivers/tests/unit/ansible/test_deploy.py +++ b/ironic_staging_drivers/tests/unit/ansible/test_deploy.py @@ -444,7 +444,8 @@ class TestAnsibleDeploy(db_base.DbTestCase): def test_get_properties(self): self.assertEqual( - set(ansible_deploy.COMMON_PROPERTIES), + set(list(ansible_deploy.COMMON_PROPERTIES) + + ['deploy_forces_oob_reboot']), set(self.driver.get_properties())) @mock.patch.object(deploy_utils, 'check_for_missing_params', @@ -792,7 +793,7 @@ class TestAnsibleDeploy(db_base.DbTestCase): (self.node['uuid'], DRIVER_INTERNAL_INFO['ansible_cleaning_ip'], 'test_u')]}, 'test_k', - notags=['wait']) + notags=['shutdown', 'wait']) @mock.patch.object(ansible_deploy, '_run_playbook', autospec=True) @mock.patch.object(ansible_deploy, '_prepare_extra_vars', autospec=True) @@ -834,14 +835,43 @@ class TestAnsibleDeploy(db_base.DbTestCase): (self.node['uuid'], DRIVER_INTERNAL_INFO['ansible_cleaning_ip'], 'test_u')]}, 'test_k', - notags=['wait', 'parted']) + notags=['shutdown', 'wait', 'parted']) + @mock.patch.object(fake.FakePower, 'get_power_state', + return_value=states.POWER_OFF) + @mock.patch.object(utils, 'node_power_action', autospec=True) + def test_reboot_and_finish_deploy_force_reboot(self, power_action_mock, + get_pow_state_mock): + d_info = self.node.driver_info + d_info['deploy_forces_oob_reboot'] = True + self.node.driver_info = d_info + self.node.save() + self.config(group='ansible', + post_deploy_get_power_state_retry_interval=0) + self.node.provision_state = states.DEPLOYING + self.node.save() + + with task_manager.acquire(self.context, self.node.uuid) as task: + with mock.patch.object(task.driver, 'network') as net_mock: + self.driver.reboot_and_finish_deploy(task) + net_mock.remove_provisioning_network.assert_called_once_with( + task) + net_mock.configure_tenant_networks.assert_called_once_with( + task) + expected_power_calls = [((task, states.POWER_OFF),), + ((task, states.POWER_ON),)] + self.assertEqual(expected_power_calls, + power_action_mock.call_args_list) + get_pow_state_mock.assert_not_called() + + @mock.patch.object(ansible_deploy, '_run_playbook', autospec=True) @mock.patch.object(utils, 'node_power_action', autospec=True) @mock.patch.object(fake.FakePower, 'get_power_state', return_value=states.POWER_ON) def test_reboot_and_finish_deploy_soft_poweroff_retry(self, get_pow_state_mock, - power_action_mock): + power_action_mock, + ansible_mock): self.config(group='ansible', post_deploy_get_power_state_retry_interval=0) self.config(group='ansible', @@ -868,6 +898,8 @@ class TestAnsibleDeploy(db_base.DbTestCase): ((task, states.POWER_ON),)] self.assertEqual(expected_power_calls, power_action_mock.call_args_list) + ansible_mock.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY, + tags=['shutdown']) @mock.patch.object(ansible_deploy, '_get_node_ip_heartbeat', autospec=True, return_value='1.2.3.4') diff --git a/releasenotes/notes/ansible-oob-reboot-970b1358757aa08f.yaml b/releasenotes/notes/ansible-oob-reboot-970b1358757aa08f.yaml new file mode 100644 index 0000000..09b0095 --- /dev/null +++ b/releasenotes/notes/ansible-oob-reboot-970b1358757aa08f.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + ansible-deploy driver now honors ``deploy-forces-oob-reboot`` driver + property