diff --git a/tripleoclient/command.py b/tripleoclient/command.py index ec640b994..76f25353b 100644 --- a/tripleoclient/command.py +++ b/tripleoclient/command.py @@ -18,6 +18,8 @@ import logging from osc_lib.command import command from osc_lib import exceptions as oscexc +from tripleo_common.utils import config + from tripleoclient import exceptions from tripleoclient import utils @@ -36,6 +38,38 @@ class Command(command.Command): self.log.exception("Exception occured while running the command") raise + @staticmethod + def get_ansible_key_and_dir(no_workflow, stack, orchestration): + """Return the ansible directory and key path. + + :param no_workflow: Enable or disable the mistral workflow code path. + :type no_workflow: Boolean + + :oaram stack: Name of a given stack to run against. + :type stack: String + + :param orchestration: Orchestration client object. + :type orchestration: Object + + :returns: Tuple + """ + + if no_workflow: + key = utils.get_key(stack=stack) + stack_config = config.Config(orchestration) + with utils.TempDirs(cleanup=False, chdir=False) as tmp: + stack_config.write_config( + stack_config.fetch_config(stack), + stack, + tmp + ) + return key, tmp + else: + # Assumes execution will take place from within a mistral + # container. + key = '.ssh/tripleo-admin-rsa' + return key, None + class Lister(Command, command.Lister): pass diff --git a/tripleoclient/tests/fakes.py b/tripleoclient/tests/fakes.py index e0cc6d42e..6867784d6 100644 --- a/tripleoclient/tests/fakes.py +++ b/tripleoclient/tests/fakes.py @@ -16,6 +16,10 @@ import mock import sys +from osc_lib.tests import utils + +from tripleoclient import plugin + AUTH_TOKEN = "foobar" AUTH_URL = "http://0.0.0.0" @@ -36,6 +40,11 @@ class FakeApp(object): class FakeStackObject(object): stack_name = 'undercloud' + outputs = [] + + @staticmethod + def get(*args, **kwargs): + pass class FakeClientManager(object): @@ -81,6 +90,8 @@ class FakeClientWrapper(object): def __init__(self): self.ws = FakeWebSocket() + self.object_store = FakeObjectClient() + self._instance = mock.Mock() def messaging_websocket(self): return self.ws @@ -125,6 +136,60 @@ class FakeInstanceData(object): _data = {'token': {}} +class FakeObjectClient(object): + + def __init__(self): + self._instance = mock.Mock() + self.put_object = mock.Mock() + + def get_object(self, *args): + return [None, "fake"] + + def get_container(self, *args): + return [None, [{"name": "fake"}]] + + +class FakePlaybookExecution(utils.TestCommand): + + def setUp(self, ansible_mock=True): + super(FakePlaybookExecution, self).setUp() + + self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") + self.app.client_manager.baremetal = mock.Mock() + self.app.client_manager.compute = mock.Mock() + self.app.client_manager.identity = mock.Mock() + self.app.client_manager.image = mock.Mock() + self.app.client_manager.network = mock.Mock() + tc = self.app.client_manager.tripleoclient = FakeClientWrapper() + self.app.client_manager.workflow_engine = mock.Mock() + stack = self.app.client_manager.orchestration = mock.Mock() + stack.stacks.get.return_value = FakeStackObject + tc.create_mistral_context = plugin.ClientWrapper( + instance=FakeInstanceData + ).create_mistral_context + + # NOTE(cloudnull): When mistral is gone this should be removed. + workflow = execution = mock.Mock() + execution.id = "IDID" + workflow.executions.create.return_value = execution + self.app.client_manager.workflow_engine = workflow + + if ansible_mock: + self.gcn = mock.patch( + 'tripleo_common.utils.config.Config', + autospec=True + ) + self.gcn.start() + self.addCleanup(self.gcn.stop) + + self.mkdirs = mock.patch( + 'os.makedirs', + autospec=True + ) + self.mkdirs.start() + self.addCleanup(self.mkdirs.stop) + + def fake_ansible_runner_run_return(rc=0): return 'Test Status', rc diff --git a/tripleoclient/tests/v1/overcloud_deploy/fakes.py b/tripleoclient/tests/v1/overcloud_deploy/fakes.py index 9a0c616a9..33cd79bd1 100644 --- a/tripleoclient/tests/v1/overcloud_deploy/fakes.py +++ b/tripleoclient/tests/v1/overcloud_deploy/fakes.py @@ -14,7 +14,6 @@ # import mock -from osc_lib.tests import utils from tripleoclient.tests import fakes @@ -105,43 +104,7 @@ def create_env(**kwargs): return env -class FakeClientWrapper(object): - - def __init__(self): - self._instance = mock.Mock() - self.object_store = FakeObjectClient() - - def messaging_websocket(self): - return fakes.FakeWebSocket() - - -class FakeObjectClient(object): - - def __init__(self): - self._instance = mock.Mock() - self.put_object = mock.Mock() - - def get_object(self, *args): - return [None, "fake"] - - def get_container(self, *args): - return [None, [{"name": "fake"}]] - - -class TestDeployOvercloud(utils.TestCommand): +class TestDeployOvercloud(fakes.FakePlaybookExecution): def setUp(self): - super(TestDeployOvercloud, self).setUp() - - self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") - self.app.client_manager.baremetal = mock.Mock() - self.app.client_manager.compute = mock.Mock() - self.app.client_manager.identity = mock.Mock() - self.app.client_manager.image = mock.Mock() - self.app.client_manager.network = mock.Mock() - self.app.client_manager.orchestration = mock.Mock() - workflow = execution = mock.Mock() - execution.id = "IDID" - workflow.executions.create.return_value = execution - self.app.client_manager.workflow_engine = workflow - self.app.client_manager.tripleoclient = FakeClientWrapper() + super(TestDeployOvercloud, self).setUp(ansible_mock=False) diff --git a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py index dd74b2d4a..33fe4a29b 100644 --- a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py +++ b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py @@ -877,9 +877,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): parsed_args) self.assertIn('/tmp/notthere', str(error)) - @mock.patch('tripleoclient.tests.v1.overcloud_deploy.fakes.' - 'FakeObjectClient.get_object', autospec=True) - def test_validate_args_missing_environment_files(self, mock_obj): + def test_validate_args_missing_environment_files(self): arglist = ['--templates', '-e', 'nonexistent.yaml'] verifylist = [ @@ -887,8 +885,6 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): ('environment_files', ['nonexistent.yaml']), ] - mock_obj.side_effect = ObjectClientException(mock.Mock( - '/fake/path not found')) parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises(oscexc.CommandError, @@ -1482,8 +1478,6 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.cmd.take_action(parsed_args) self.assertTrue(fixture.mock_get_hosts_and_enable_ssh_admin.called) self.assertTrue(fixture.mock_config_download.called) - self.assertEqual('ansible.cfg', - fixture.mock_config_download.call_args[0][8]) mock_copy.assert_called_once() @mock.patch('tripleoclient.utils.copy_clouds_yaml') @@ -1514,7 +1508,6 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.cmd.take_action(parsed_args) fixture.mock_config_download.assert_called() - self.assertEqual(240*60, fixture.mock_config_download.call_args[0][9]) mock_copy.assert_called_once() def test_download_missing_files_from_plan(self): diff --git a/tripleoclient/tests/v1/overcloud_external_update/fakes.py b/tripleoclient/tests/v1/overcloud_external_update/fakes.py index e079ac837..c37da4304 100644 --- a/tripleoclient/tests/v1/overcloud_external_update/fakes.py +++ b/tripleoclient/tests/v1/overcloud_external_update/fakes.py @@ -13,38 +13,10 @@ # under the License. # -import mock -from osc_lib.tests import utils - from tripleoclient.tests import fakes -class FakeClientWrapper(object): - - def __init__(self): - self._instance = mock.Mock() - self.object_store = FakeObjectClient() - - def messaging_websocket(self): - return fakes.FakeWebSocket() - - -class FakeObjectClient(object): - - def __init__(self): - self._instance = mock.Mock() - self.put_object = mock.Mock() - - def get_object(self, *args): - return - - -class TestOvercloudExternalUpdateRun(utils.TestCommand): +class TestOvercloudExternalUpdateRun(fakes.FakePlaybookExecution): def setUp(self): super(TestOvercloudExternalUpdateRun, self).setUp() - - self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") - self.app.client_manager.tripleoclient = FakeClientWrapper() - self.app.client_manager.workflow_engine = mock.Mock() - self.app.client_manager.orchestration = mock.Mock() diff --git a/tripleoclient/tests/v1/overcloud_external_update/test_overcloud_external_update.py b/tripleoclient/tests/v1/overcloud_external_update/test_overcloud_external_update.py index 7856c42d4..ae006c19a 100644 --- a/tripleoclient/tests/v1/overcloud_external_update/test_overcloud_external_update.py +++ b/tripleoclient/tests/v1/overcloud_external_update/test_overcloud_external_update.py @@ -15,6 +15,7 @@ import mock +from tripleoclient.tests import fakes as ooofakes from tripleoclient.tests.v1.overcloud_external_update import fakes from tripleoclient.v1 import overcloud_external_update @@ -34,13 +35,23 @@ class TestOvercloudExternalUpdateRun(fakes.TestOvercloudExternalUpdateRun): self.mock_uuid4 = uuid4_patcher.start() self.addCleanup(self.mock_uuid4.stop) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch( + 'ansible_runner.runner_config.RunnerConfig', + autospec=True, + return_value=ooofakes.FakeRunnerConfig() + ) + @mock.patch( + 'ansible_runner.Runner.run', + return_value=ooofakes.fake_ansible_runner_run_return() + ) + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') @mock.patch('six.moves.builtins.open') def test_update_with_user_and_tags(self, mock_open, mock_execute, - mock_expanduser, update_ansible): + mock_expanduser, update_ansible, + mock_run, mock_run_prepare): mock_expanduser.return_value = '/home/fake/' argslist = ['--ssh-user', 'tripleo-admin', '--tags', 'ceph'] @@ -54,25 +65,35 @@ class TestOvercloudExternalUpdateRun(fakes.TestOvercloudExternalUpdateRun): mock_exists.return_value = True self.cmd.take_action(parsed_args) update_ansible.assert_called_once_with( - self.app.client_manager, - container='overcloud', - nodes='all', - inventory_file=mock_open().__enter__().read(), playbook='external_update_steps_playbook.yaml', - node_user='tripleo-admin', + inventory=mock.ANY, + workdir=mock.ANY, + ssh_user='tripleo-admin', + key='/var/lib/mistral/overcloud/ssh_private_key', + module_path='/usr/share/ansible-modules', + limit_hosts='all', tags='ceph', skip_tags='', - verbosity=0, - extra_vars={} + extra_vars={'ansible_become': True} ) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch( + 'ansible_runner.runner_config.RunnerConfig', + autospec=True, + return_value=ooofakes.FakeRunnerConfig() + ) + @mock.patch( + 'ansible_runner.Runner.run', + return_value=ooofakes.fake_ansible_runner_run_return() + ) + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') @mock.patch('six.moves.builtins.open') def test_update_with_user_and_extra_vars(self, mock_open, mock_execute, - mock_expanduser, update_ansible): + mock_expanduser, update_ansible, + mock_run, mock_run_prepare): mock_expanduser.return_value = '/home/fake/' argslist = ['--ssh-user', 'tripleo-admin', '--extra-vars', 'key1=val1', @@ -87,14 +108,18 @@ class TestOvercloudExternalUpdateRun(fakes.TestOvercloudExternalUpdateRun): mock_exists.return_value = True self.cmd.take_action(parsed_args) update_ansible.assert_called_once_with( - self.app.client_manager, - container='overcloud', - nodes='all', - inventory_file=mock_open().__enter__().read(), playbook='external_update_steps_playbook.yaml', - node_user='tripleo-admin', + inventory=mock.ANY, + workdir=mock.ANY, + ssh_user='tripleo-admin', + key='/var/lib/mistral/overcloud/ssh_private_key', + module_path='/usr/share/ansible-modules', + limit_hosts='all', tags='', skip_tags='', - verbosity=0, - extra_vars={'key1': 'val1', 'key2': 'val2'} + extra_vars={ + 'key1': 'val1', + 'key2': 'val2', + 'ansible_become': True + } ) diff --git a/tripleoclient/tests/v1/overcloud_external_upgrade/fakes.py b/tripleoclient/tests/v1/overcloud_external_upgrade/fakes.py index 22ef36766..9aaf9f534 100644 --- a/tripleoclient/tests/v1/overcloud_external_upgrade/fakes.py +++ b/tripleoclient/tests/v1/overcloud_external_upgrade/fakes.py @@ -13,38 +13,10 @@ # under the License. # -import mock -from osc_lib.tests import utils - from tripleoclient.tests import fakes -class FakeClientWrapper(object): - - def __init__(self): - self._instance = mock.Mock() - self.object_store = FakeObjectClient() - - def messaging_websocket(self): - return fakes.FakeWebSocket() - - -class FakeObjectClient(object): - - def __init__(self): - self._instance = mock.Mock() - self.put_object = mock.Mock() - - def get_object(self, *args): - return - - -class TestOvercloudExternalUpgradeRun(utils.TestCommand): +class TestOvercloudExternalUpgradeRun(fakes.FakePlaybookExecution): def setUp(self): super(TestOvercloudExternalUpgradeRun, self).setUp() - - self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") - self.app.client_manager.tripleoclient = FakeClientWrapper() - self.app.client_manager.workflow_engine = mock.Mock() - self.app.client_manager.orchestration = mock.Mock() diff --git a/tripleoclient/tests/v1/overcloud_external_upgrade/test_overcloud_external_upgrade.py b/tripleoclient/tests/v1/overcloud_external_upgrade/test_overcloud_external_upgrade.py index 6c01a2901..bb287d73e 100644 --- a/tripleoclient/tests/v1/overcloud_external_upgrade/test_overcloud_external_upgrade.py +++ b/tripleoclient/tests/v1/overcloud_external_upgrade/test_overcloud_external_upgrade.py @@ -15,6 +15,7 @@ import mock +from tripleoclient.tests import fakes as ooofakes from tripleoclient.tests.v1.overcloud_external_upgrade import fakes from tripleoclient.v1 import overcloud_external_upgrade @@ -34,13 +35,23 @@ class TestOvercloudExternalUpgradeRun(fakes.TestOvercloudExternalUpgradeRun): self.mock_uuid4 = uuid4_patcher.start() self.addCleanup(self.mock_uuid4.stop) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch( + 'ansible_runner.runner_config.RunnerConfig', + autospec=True, + return_value=ooofakes.FakeRunnerConfig() + ) + @mock.patch( + 'ansible_runner.Runner.run', + return_value=ooofakes.fake_ansible_runner_run_return() + ) + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') @mock.patch('six.moves.builtins.open') def test_upgrade_with_user_and_tags(self, mock_open, mock_execute, - mock_expanduser, update_ansible): + mock_expanduser, update_ansible, + mock_run, mock_run_prepare): mock_expanduser.return_value = '/home/fake/' argslist = ['--ssh-user', 'tripleo-admin', '--tags', 'ceph'] @@ -54,25 +65,35 @@ class TestOvercloudExternalUpgradeRun(fakes.TestOvercloudExternalUpgradeRun): mock_exists.return_value = True self.cmd.take_action(parsed_args) update_ansible.assert_called_once_with( - self.app.client_manager, - container='overcloud', - nodes='all', - inventory_file=mock_open().__enter__().read(), playbook='external_upgrade_steps_playbook.yaml', - node_user='tripleo-admin', + inventory=mock.ANY, + workdir=mock.ANY, + ssh_user='tripleo-admin', + key='/var/lib/mistral/overcloud/ssh_private_key', + module_path='/usr/share/ansible-modules', + limit_hosts='all', tags='ceph', skip_tags='', - verbosity=0, - extra_vars={} + extra_vars={'ansible_become': True} ) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch( + 'ansible_runner.runner_config.RunnerConfig', + autospec=True, + return_value=ooofakes.FakeRunnerConfig() + ) + @mock.patch( + 'ansible_runner.Runner.run', + return_value=ooofakes.fake_ansible_runner_run_return() + ) + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') @mock.patch('six.moves.builtins.open') def test_upgrade_with_user_and_extra_vars(self, mock_open, mock_execute, - mock_expanduser, update_ansible): + mock_expanduser, update_ansible, + mock_run, mock_run_prepare): mock_expanduser.return_value = '/home/fake/' argslist = ['--ssh-user', 'tripleo-admin', '--extra-vars', 'key1=val1', @@ -87,14 +108,18 @@ class TestOvercloudExternalUpgradeRun(fakes.TestOvercloudExternalUpgradeRun): mock_exists.return_value = True self.cmd.take_action(parsed_args) update_ansible.assert_called_once_with( - self.app.client_manager, - container='overcloud', - nodes='all', - inventory_file=mock_open().__enter__().read(), playbook='external_upgrade_steps_playbook.yaml', - node_user='tripleo-admin', + inventory=mock.ANY, + workdir=mock.ANY, + ssh_user='tripleo-admin', + key='/var/lib/mistral/overcloud/ssh_private_key', + module_path='/usr/share/ansible-modules', + limit_hosts='all', tags='', skip_tags='', - verbosity=0, - extra_vars={'key1': 'val1', 'key2': 'val2'} + extra_vars={ + 'key1': 'val1', + 'key2': 'val2', + 'ansible_become': True + } ) diff --git a/tripleoclient/tests/v1/overcloud_ffwd_upgrade/fakes.py b/tripleoclient/tests/v1/overcloud_ffwd_upgrade/fakes.py index 4d543ccd0..b5ff7d8bc 100644 --- a/tripleoclient/tests/v1/overcloud_ffwd_upgrade/fakes.py +++ b/tripleoclient/tests/v1/overcloud_ffwd_upgrade/fakes.py @@ -13,78 +13,22 @@ # under the License. # -import mock -from osc_lib.tests import utils - -from tripleoclient import plugin from tripleoclient.tests import fakes -class FakeClientWrapper(object): - - def __init__(self): - self._instance = mock.Mock() - self.object_store = FakeObjectClient() - - def messaging_websocket(self, queue="tripleo"): - return fakes.FakeWebSocket() - - -class FakeObjectClient(object): - - def __init__(self): - self._instance = mock.Mock() - self.put_object = mock.Mock() - - def get_object(self, *args): - return - - -class TestFFWDUpgradePrepare(utils.TestCommand): +class TestFFWDUpgradePrepare(fakes.FakePlaybookExecution): def setUp(self): super(TestFFWDUpgradePrepare, self).setUp() - self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") - self.app.client_manager.baremetal = mock.Mock() - self.app.client_manager.orchestration = mock.Mock() - workflow = execution = mock.Mock() - execution.id = "IDID" - workflow.executions.create.return_value = execution - self.app.client_manager.workflow_engine = workflow - tc = self.app.client_manager.tripleoclient = FakeClientWrapper() - tc.create_mistral_context = plugin.ClientWrapper( - instance=fakes.FakeInstanceData - ).create_mistral_context - -class TestFFWDUpgradeRun(utils.TestCommand): +class TestFFWDUpgradeRun(fakes.FakePlaybookExecution): def setUp(self): super(TestFFWDUpgradeRun, self).setUp() - self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") - self.app.client_manager.workflow_engine = mock.Mock() - self.app.client_manager.orchestration = mock.Mock() - tc = self.app.client_manager.tripleoclient = FakeClientWrapper() - tc.create_mistral_context = plugin.ClientWrapper( - instance=fakes.FakeInstanceData - ).create_mistral_context - -class TestFFWDUpgradeConverge(utils.TestCommand): +class TestFFWDUpgradeConverge(fakes.FakePlaybookExecution): def setUp(self): super(TestFFWDUpgradeConverge, self).setUp() - - self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") - self.app.client_manager.baremetal = mock.Mock() - self.app.client_manager.orchestration = mock.Mock() - workflow = execution = mock.Mock() - execution.id = "IDID" - workflow.executions.create.return_value = execution - self.app.client_manager.workflow_engine = workflow - tc = self.app.client_manager.tripleoclient = FakeClientWrapper() - tc.create_mistral_context = plugin.ClientWrapper( - instance=fakes.FakeInstanceData - ).create_mistral_context diff --git a/tripleoclient/tests/v1/overcloud_ffwd_upgrade/test_overcloud_ffwd_upgrade.py b/tripleoclient/tests/v1/overcloud_ffwd_upgrade/test_overcloud_ffwd_upgrade.py index 65a0991a7..b9558f8f5 100644 --- a/tripleoclient/tests/v1/overcloud_ffwd_upgrade/test_overcloud_ffwd_upgrade.py +++ b/tripleoclient/tests/v1/overcloud_ffwd_upgrade/test_overcloud_ffwd_upgrade.py @@ -155,7 +155,7 @@ class TestFFWDUpgradeRun(fakes.TestFFWDUpgradeRun): self.mock_uuid4 = uuid4_patcher.start() self.addCleanup(self.mock_uuid4.stop) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') @@ -171,19 +171,19 @@ class TestFFWDUpgradeRun(fakes.TestFFWDUpgradeRun): mock_exists.return_value = True self.cmd.take_action(parsed_args) upgrade_ansible.assert_called_once_with( - self.app.client_manager, - container='overcloud', - inventory_file=mock_open().__enter__().read(), - nodes='', - playbook=constants.FFWD_UPGRADE_PLAYBOOK, - node_user='heat-admin', + playbook='fast_forward_upgrade_playbook.yaml', + inventory=mock.ANY, + workdir=mock.ANY, + ssh_user='heat-admin', + key='/var/lib/mistral/overcloud/ssh_private_key', + module_path='/usr/share/ansible-modules', + limit_hosts='', tags='', skip_tags='', - verbosity=0, - extra_vars=None + extra_vars={'ansible_become': True} ) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') @@ -199,19 +199,19 @@ class TestFFWDUpgradeRun(fakes.TestFFWDUpgradeRun): mock_exists.return_value = True self.cmd.take_action(parsed_args) upgrade_ansible.assert_called_once_with( - self.app.client_manager, - container='overcloud', - inventory_file=mock_open().__enter__().read(), - nodes='', - playbook=constants.FFWD_UPGRADE_PLAYBOOK, - node_user='my-user', + playbook='fast_forward_upgrade_playbook.yaml', + inventory=mock.ANY, + workdir=mock.ANY, + ssh_user='my-user', + key='/var/lib/mistral/overcloud/ssh_private_key', + module_path='/usr/share/ansible-modules', + limit_hosts='', tags='', skip_tags='', - verbosity=0, - extra_vars=None + extra_vars={'ansible_become': True} ) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') diff --git a/tripleoclient/tests/v1/overcloud_node/test_overcloud_node.py b/tripleoclient/tests/v1/overcloud_node/test_overcloud_node.py index c6a8c8d17..0e71d2e57 100644 --- a/tripleoclient/tests/v1/overcloud_node/test_overcloud_node.py +++ b/tripleoclient/tests/v1/overcloud_node/test_overcloud_node.py @@ -51,10 +51,31 @@ class TestDeleteNode(fakes.TestDeleteNode): tc.create_mistral_context = plugin.ClientWrapper( instance=ooofakes.FakeInstanceData ).create_mistral_context + self.gcn = mock.patch( + 'tripleo_common.actions.config.DownloadConfigAction', + autospec=True + ) + self.gcn.start() + self.addCleanup(self.gcn.stop) + self.ansible = mock.patch( + 'tripleo_common.actions.ansible.AnsibleGenerateInventoryAction', + autospec=True + ) + self.ansible.start() + self.addCleanup(self.ansible.stop) + config_mock = mock.patch( + 'tripleo_common.actions.config.GetOvercloudConfig', + autospec=True + ) + config_mock.start() + self.addCleanup(config_mock.stop) self.workflow = self.app.client_manager.workflow_engine self.stack_name = self.app.client_manager.orchestration.stacks.get - self.stack_name.return_value = mock.Mock(stack_name="overcloud") + stack = self.stack_name.return_value = mock.Mock( + stack_name="overcloud" + ) + stack.output_show.return_value = {'output': {'output_value': []}} execution = mock.Mock() execution.id = "IDID" self.workflow.executions.create.return_value = execution @@ -67,11 +88,19 @@ class TestDeleteNode(fakes.TestDeleteNode): delete_node.return_value = None self.addCleanup(delete_node.stop) + wait_stack = mock.patch( + 'tripleoclient.utils.wait_for_stack_ready', + autospec=True + ) + wait_stack.start() + wait_stack.return_value = None + self.addCleanup(wait_stack.stop) + # TODO(someone): This test does not pass with autospec=True, it should # probably be fixed so that it can pass with that. - @mock.patch("heatclient.common.event_utils.poll_for_events") - def test_node_delete(self, mock_poll): - mock_poll.return_value = ("CREATE_IN_PROGRESS", "MESSAGE") + @mock.patch('tripleoclient.utils.run_ansible_playbook', + autospec=True) + def test_node_delete(self, mock_playbook): argslist = ['instance1', 'instance2', '--templates', '--stack', 'overcast', '--timeout', '90', '--yes'] verifylist = [ @@ -79,14 +108,6 @@ class TestDeleteNode(fakes.TestDeleteNode): ('nodes', ['instance1', 'instance2']) ] parsed_args = self.check_parser(self.cmd, argslist, verifylist) - self.websocket.wait_for_messages.return_value = iter([{ - "execution_id": "IDID", - "status": "SUCCESS", - "message": "Success.", - }]) - - self.stack_name.return_value = mock.Mock(stack_name="overcast") - self.cmd.take_action(parsed_args) @mock.patch('tripleoclient.utils.prompt_user_for_confirmation', @@ -122,9 +143,9 @@ class TestDeleteNode(fakes.TestDeleteNode): # Verify self.workflow.executions.create.assert_not_called() - @mock.patch("heatclient.common.event_utils.poll_for_events") - def test_node_delete_without_stack(self, mock_poll): - mock_poll.return_value = ("CREATE_IN_PROGRESS", "MESSAGE") + @mock.patch('tripleoclient.utils.run_ansible_playbook', + autospec=True) + def test_node_delete_without_stack(self, mock_playbook): arglist = ['instance1', '--yes'] verifylist = [ @@ -132,49 +153,17 @@ class TestDeleteNode(fakes.TestDeleteNode): ('nodes', ['instance1']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.websocket.wait_for_messages.return_value = iter([{ - "execution_id": "IDID", - "status": "SUCCESS", - "message": "Success.", - }]) - self.cmd.take_action(parsed_args) - def test_node_delete_wrong_instance(self): - - argslist = ['wrong_instance', '--templates', - '--stack', 'overcloud', '--yes'] - verifylist = [ - ('stack', 'overcloud'), - ('nodes', ['wrong_instance']), - ] - parsed_args = self.check_parser(self.cmd, argslist, verifylist) - - self.websocket.wait_for_messages.return_value = iter([{ - "status": "FAILED", - "execution_id": "IDID", - "message": """Failed to run action ERROR: Couldn't find \ - following instances in stack overcloud: wrong_instance""" - }]) - - # Verify - self.assertRaises(exceptions.DeploymentError, - self.cmd.take_action, parsed_args) - - @mock.patch("heatclient.common.event_utils.poll_for_events") + @mock.patch('tripleoclient.utils.run_ansible_playbook', + autospec=True) @mock.patch('tripleoclient.workflows.baremetal.expand_roles', autospec=True) @mock.patch('tripleoclient.workflows.baremetal.undeploy_roles', autospec=True) def test_node_delete_baremetal_deployment(self, mock_undeploy_roles, - mock_expand_roles, mock_poll): - mock_poll.return_value = ("CREATE_IN_PROGRESS", "MESSAGE") - self.websocket.wait_for_messages.return_value = iter([{ - "execution_id": "IDID", - "status": "SUCCESS", - "message": "Success.", - }]) + mock_expand_roles, + mock_playbook): bm_yaml = [{ 'name': 'Compute', 'count': 5, @@ -219,7 +208,6 @@ class TestDeleteNode(fakes.TestDeleteNode): expand_to_translate ] - self.stack_name.return_value = mock.Mock(stack_name="overcast") res_list = self.app.client_manager.orchestration.resources.list res_list.return_value = [ mock.Mock( diff --git a/tripleoclient/tests/v1/overcloud_update/fakes.py b/tripleoclient/tests/v1/overcloud_update/fakes.py index db0c0cf68..131273cce 100644 --- a/tripleoclient/tests/v1/overcloud_update/fakes.py +++ b/tripleoclient/tests/v1/overcloud_update/fakes.py @@ -13,75 +13,22 @@ # under the License. # -import mock -from osc_lib.tests import utils - -from tripleoclient import plugin from tripleoclient.tests import fakes -class FakeClientWrapper(object): - - def __init__(self): - self._instance = mock.Mock() - self.object_store = FakeObjectClient() - - def messaging_websocket(self): - return fakes.FakeWebSocket() - - -class FakeObjectClient(object): - - def __init__(self): - self._instance = mock.Mock() - self.put_object = mock.Mock() - - def get_object(self, *args): - return - - -class TestOvercloudUpdatePrepare(utils.TestCommand): +class TestOvercloudUpdatePrepare(fakes.FakePlaybookExecution): def setUp(self): super(TestOvercloudUpdatePrepare, self).setUp() - self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") - self.app.client_manager.baremetal = mock.Mock() - self.app.client_manager.orchestration = mock.Mock() - workflow = execution = mock.Mock() - execution.id = "IDID" - workflow.executions.create.return_value = execution - self.app.client_manager.workflow_engine = workflow - tc = self.app.client_manager.tripleoclient = FakeClientWrapper() - tc.create_mistral_context = plugin.ClientWrapper( - instance=fakes.FakeInstanceData - ).create_mistral_context - -class TestOvercloudUpdateRun(utils.TestCommand): +class TestOvercloudUpdateRun(fakes.FakePlaybookExecution): def setUp(self): super(TestOvercloudUpdateRun, self).setUp() - self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") - self.app.client_manager.workflow_engine = mock.Mock() - self.app.client_manager.orchestration = mock.Mock() - tc = self.app.client_manager.tripleoclient = FakeClientWrapper() - tc.create_mistral_context = plugin.ClientWrapper( - instance=fakes.FakeInstanceData - ).create_mistral_context - -class TestOvercloudUpdateConverge(utils.TestCommand): +class TestOvercloudUpdateConverge(fakes.FakePlaybookExecution): def setUp(self): super(TestOvercloudUpdateConverge, self).setUp() - - self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") - self.app.client_manager.baremetal = mock.Mock() - self.app.client_manager.orchestration = mock.Mock() - self.app.client_manager.workflow_engine = mock.Mock() - tc = self.app.client_manager.tripleoclient = FakeClientWrapper() - tc.create_mistral_context = plugin.ClientWrapper( - instance=fakes.FakeInstanceData - ).create_mistral_context diff --git a/tripleoclient/tests/v1/overcloud_update/test_overcloud_update.py b/tripleoclient/tests/v1/overcloud_update/test_overcloud_update.py index b7bb60017..4a91d5b04 100644 --- a/tripleoclient/tests/v1/overcloud_update/test_overcloud_update.py +++ b/tripleoclient/tests/v1/overcloud_update/test_overcloud_update.py @@ -130,75 +130,7 @@ class TestOvercloudUpdateRun(fakes.TestOvercloudUpdateRun): self.mock_uuid4 = uuid4_patcher.start() self.addCleanup(self.mock_uuid4.stop) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', - autospec=True) - @mock.patch('os.path.expanduser') - @mock.patch('oslo_concurrency.processutils.execute') - @mock.patch('six.moves.builtins.open') - def test_update_with_playbook_and_user(self, mock_open, mock_execute, - mock_expanduser, update_ansible): - mock_expanduser.return_value = '/home/fake/' - argslist = ['--limit', 'Compute', - '--playbook', 'fake-playbook.yaml', - '--ssh-user', 'tripleo-admin'] - verifylist = [ - ('limit', 'Compute'), - ('static_inventory', None), - ('playbook', 'fake-playbook.yaml'), - ('ssh_user', 'tripleo-admin') - ] - - parsed_args = self.check_parser(self.cmd, argslist, verifylist) - with mock.patch('os.path.exists') as mock_exists: - mock_exists.return_value = True - self.cmd.take_action(parsed_args) - update_ansible.assert_called_once_with( - self.app.client_manager, - container='overcloud', - nodes='Compute', - inventory_file=mock_open().__enter__().read(), - playbook='fake-playbook.yaml', - node_user='tripleo-admin', - tags='', - skip_tags='', - verbosity=0, - extra_vars=None - ) - - @mock.patch('tripleoclient.workflows.package_update.update_ansible', - autospec=True) - @mock.patch('os.path.expanduser') - @mock.patch('oslo_concurrency.processutils.execute') - @mock.patch('six.moves.builtins.open') - def test_update_limit_with_all_playbooks(self, mock_open, mock_execute, - mock_expanduser, update_ansible): - mock_expanduser.return_value = '/home/fake/' - argslist = ['--limit', 'Compute', '--playbook', 'all'] - verifylist = [ - ('limit', 'Compute'), - ('static_inventory', None), - ('playbook', 'all') - ] - - parsed_args = self.check_parser(self.cmd, argslist, verifylist) - with mock.patch('os.path.exists') as mock_exists: - mock_exists.return_value = True - self.cmd.take_action(parsed_args) - for book in constants.MINOR_UPDATE_PLAYBOOKS: - update_ansible.assert_any_call( - self.app.client_manager, - container='overcloud', - nodes='Compute', - inventory_file=mock_open().__enter__().read(), - playbook=book, - node_user='tripleo-admin', - tags='', - skip_tags='', - verbosity=0, - extra_vars=None - ) - - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') diff --git a/tripleoclient/tests/v1/overcloud_upgrade/fakes.py b/tripleoclient/tests/v1/overcloud_upgrade/fakes.py index 173b9fc4f..0ec28847d 100644 --- a/tripleoclient/tests/v1/overcloud_upgrade/fakes.py +++ b/tripleoclient/tests/v1/overcloud_upgrade/fakes.py @@ -13,57 +13,16 @@ # under the License. # -import mock -from osc_lib.tests import utils - -from tripleoclient import plugin from tripleoclient.tests import fakes -class FakeClientWrapper(object): - - def __init__(self): - self._instance = mock.Mock() - self.object_store = FakeObjectClient() - - def messaging_websocket(self): - return fakes.FakeWebSocket() - - -class FakeObjectClient(object): - - def __init__(self): - self._instance = mock.Mock() - self.put_object = mock.Mock() - - def get_object(self, *args): - return - - -class TestOvercloudUpgradePrepare(utils.TestCommand): +class TestOvercloudUpgradePrepare(fakes.FakePlaybookExecution): def setUp(self): super(TestOvercloudUpgradePrepare, self).setUp() - self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") - self.app.client_manager.baremetal = mock.Mock() - self.app.client_manager.orchestration = mock.Mock() - tc = self.app.client_manager.tripleoclient = FakeClientWrapper() - tc.create_mistral_context = plugin.ClientWrapper( - instance=fakes.FakeInstanceData - ).create_mistral_context - workflow = execution = mock.Mock() - execution.id = "IDID" - workflow.executions.create.return_value = execution - self.app.client_manager.workflow_engine = workflow - -class TestOvercloudUpgradeRun(utils.TestCommand): +class TestOvercloudUpgradeRun(fakes.FakePlaybookExecution): def setUp(self): super(TestOvercloudUpgradeRun, self).setUp() - - self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN") - self.app.client_manager.tripleoclient = FakeClientWrapper() - self.app.client_manager.workflow_engine = mock.Mock() - self.app.client_manager.orchestration = mock.Mock() diff --git a/tripleoclient/tests/v1/overcloud_upgrade/test_overcloud_upgrade.py b/tripleoclient/tests/v1/overcloud_upgrade/test_overcloud_upgrade.py index 110729b64..e0d4cfd99 100644 --- a/tripleoclient/tests/v1/overcloud_upgrade/test_overcloud_upgrade.py +++ b/tripleoclient/tests/v1/overcloud_upgrade/test_overcloud_upgrade.py @@ -19,6 +19,7 @@ from osc_lib import exceptions as oscexc from osc_lib.tests.utils import ParserException from tripleoclient import constants from tripleoclient import exceptions +from tripleoclient.tests import fakes as ooofakes from tripleoclient.tests.v1.overcloud_upgrade import fakes from tripleoclient.v1 import overcloud_upgrade @@ -173,13 +174,23 @@ class TestOvercloudUpgradeRun(fakes.TestOvercloudUpgradeRun): self.mock_uuid4 = uuid4_patcher.start() self.addCleanup(self.mock_uuid4.stop) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch( + 'ansible_runner.runner_config.RunnerConfig', + autospec=True, + return_value=ooofakes.FakeRunnerConfig() + ) + @mock.patch( + 'ansible_runner.Runner.run', + return_value=ooofakes.fake_ansible_runner_run_return() + ) + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') @mock.patch('six.moves.builtins.open') def test_upgrade_limit_with_playbook_and_user( - self, mock_open, mock_execute, mock_expanduser, upgrade_ansible): + self, mock_open, mock_execute, mock_expanduser, upgrade_ansible, + mock_run, mock_run_prepare): mock_expanduser.return_value = '/home/fake/' argslist = ['--limit', 'Compute, Controller', '--playbook', 'fake-playbook.yaml', @@ -195,95 +206,35 @@ class TestOvercloudUpgradeRun(fakes.TestOvercloudUpgradeRun): mock_exists.return_value = True self.cmd.take_action(parsed_args) upgrade_ansible.assert_called_once_with( - self.app.client_manager, - container='overcloud', - nodes='Compute, Controller', - inventory_file=mock_open().__enter__().read(), playbook='fake-playbook.yaml', - node_user='tripleo-admin', + inventory=mock.ANY, + workdir=mock.ANY, + ssh_user='tripleo-admin', + key='/var/lib/mistral/overcloud/ssh_private_key', + module_path='/usr/share/ansible-modules', + limit_hosts='Compute, Controller', tags='', skip_tags='', - verbosity=0, - extra_vars=None + extra_vars={'ansible_become': True} ) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', - autospec=True) - @mock.patch('os.path.expanduser') - @mock.patch('oslo_concurrency.processutils.execute') - @mock.patch('six.moves.builtins.open') - def test_upgrade_limit_all_playbooks_skip_validation( - self, mock_open, mock_execute, mock_expanduser, upgrade_ansible): - mock_expanduser.return_value = '/home/fake/' - argslist = ['--limit', 'Compute', '--playbook', 'all', - '--skip-tags', 'validation'] - verifylist = [ - ('limit', 'Compute'), - ('static_inventory', None), - ('playbook', 'all'), - ('skip_tags', 'validation') - ] - - parsed_args = self.check_parser(self.cmd, argslist, verifylist) - with mock.patch('os.path.exists') as mock_exists: - mock_exists.return_value = True - self.cmd.take_action(parsed_args) - for book in constants.MAJOR_UPGRADE_PLAYBOOKS: - upgrade_ansible.assert_any_call( - self.app.client_manager, - container='overcloud', - nodes='Compute', - inventory_file=mock_open().__enter__().read(), - playbook=book, - node_user='tripleo-admin', - tags='', - skip_tags='validation', - verbosity=0, - extra_vars=None - ) - - @mock.patch('tripleoclient.workflows.package_update.update_ansible', - autospec=True) - @mock.patch('os.path.expanduser') - @mock.patch('oslo_concurrency.processutils.execute') - @mock.patch('six.moves.builtins.open') - def test_upgrade_limit_all_playbooks_only_validation( - self, mock_open, mock_execute, mock_expanduser, upgrade_ansible): - mock_expanduser.return_value = '/home/fake/' - argslist = ['--limit', 'Compute', '--playbook', 'all', - '--tags', 'validation'] - verifylist = [ - ('limit', 'Compute'), - ('static_inventory', None), - ('playbook', 'all'), - ('tags', 'validation') - ] - - parsed_args = self.check_parser(self.cmd, argslist, verifylist) - with mock.patch('os.path.exists') as mock_exists: - mock_exists.return_value = True - self.cmd.take_action(parsed_args) - for book in constants.MAJOR_UPGRADE_PLAYBOOKS: - upgrade_ansible.assert_any_call( - self.app.client_manager, - container='overcloud', - nodes='Compute', - inventory_file=mock_open().__enter__().read(), - playbook=book, - node_user='tripleo-admin', - tags='validation', - skip_tags='', - verbosity=0, - extra_vars=None - ) - - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch( + 'ansible_runner.runner_config.RunnerConfig', + autospec=True, + return_value=ooofakes.FakeRunnerConfig() + ) + @mock.patch( + 'ansible_runner.Runner.run', + return_value=ooofakes.fake_ansible_runner_run_return() + ) + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') @mock.patch('six.moves.builtins.open') def test_upgrade_nodes_with_playbook_no_skip_tags( - self, mock_open, mock_execute, mock_expanduser, upgrade_ansible): + self, mock_open, mock_execute, mock_expanduser, upgrade_ansible, + mock_run, mock_run_prepare): mock_expanduser.return_value = '/home/fake/' argslist = ['--limit', 'compute-0, compute-1', '--playbook', 'fake-playbook.yaml', ] @@ -298,87 +249,19 @@ class TestOvercloudUpgradeRun(fakes.TestOvercloudUpgradeRun): mock_exists.return_value = True self.cmd.take_action(parsed_args) upgrade_ansible.assert_called_once_with( - self.app.client_manager, - container='overcloud', - nodes='compute-0, compute-1', - inventory_file=mock_open().__enter__().read(), playbook='fake-playbook.yaml', - node_user='tripleo-admin', + inventory=mock.ANY, + workdir=mock.ANY, + ssh_user='tripleo-admin', + key='/var/lib/mistral/overcloud/ssh_private_key', + module_path='/usr/share/ansible-modules', + limit_hosts='compute-0, compute-1', tags='', skip_tags='', - verbosity=0, - extra_vars=None + extra_vars={'ansible_become': True} ) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', - autospec=True) - @mock.patch('os.path.expanduser') - @mock.patch('oslo_concurrency.processutils.execute') - @mock.patch('six.moves.builtins.open') - def test_upgrade_node_all_playbooks_skip_tags_default( - self, mock_open, mock_execute, mock_expanduser, upgrade_ansible): - mock_expanduser.return_value = '/home/fake/' - argslist = ['--limit', 'swift-1', '--playbook', 'all'] - verifylist = [ - ('limit', 'swift-1'), - ('static_inventory', None), - ('playbook', 'all'), - ] - - parsed_args = self.check_parser(self.cmd, argslist, verifylist) - with mock.patch('os.path.exists') as mock_exists: - mock_exists.return_value = True - self.cmd.take_action(parsed_args) - for book in constants.MAJOR_UPGRADE_PLAYBOOKS: - upgrade_ansible.assert_any_call( - self.app.client_manager, - container='overcloud', - nodes='swift-1', - inventory_file=mock_open().__enter__().read(), - playbook=book, - node_user='tripleo-admin', - tags='', - skip_tags='', - verbosity=0, - extra_vars=None - ) - - @mock.patch('tripleoclient.workflows.package_update.update_ansible', - autospec=True) - @mock.patch('os.path.expanduser') - @mock.patch('oslo_concurrency.processutils.execute') - @mock.patch('six.moves.builtins.open') - def test_upgrade_node_all_playbooks_skip_tags_all_supported( - self, mock_open, mock_execute, mock_expanduser, upgrade_ansible): - mock_expanduser.return_value = '/home/fake/' - argslist = ['--limit', 'swift-1', '--playbook', 'all', - '--skip-tags', 'pre-upgrade,validation'] - verifylist = [ - ('limit', 'swift-1'), - ('static_inventory', None), - ('playbook', 'all'), - ('skip_tags', 'pre-upgrade,validation') - ] - - parsed_args = self.check_parser(self.cmd, argslist, verifylist) - with mock.patch('os.path.exists') as mock_exists: - mock_exists.return_value = True - self.cmd.take_action(parsed_args) - for book in constants.MAJOR_UPGRADE_PLAYBOOKS: - upgrade_ansible.assert_any_call( - self.app.client_manager, - container='overcloud', - nodes='swift-1', - inventory_file=mock_open().__enter__().read(), - playbook=book, - node_user='tripleo-admin', - tags='', - skip_tags='pre-upgrade,validation', - verbosity=0, - extra_vars=None - ) - - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') @@ -391,7 +274,7 @@ class TestOvercloudUpgradeRun(fakes.TestOvercloudUpgradeRun): self.assertRaises(ParserException, lambda: self.check_parser( self.cmd, argslist, verifylist)) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') @@ -414,7 +297,7 @@ class TestOvercloudUpgradeRun(fakes.TestOvercloudUpgradeRun): self.assertRaises(exceptions.InvalidConfiguration, lambda: self.cmd.take_action(parsed_args)) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') @@ -437,7 +320,7 @@ class TestOvercloudUpgradeRun(fakes.TestOvercloudUpgradeRun): self.assertRaises(exceptions.InvalidConfiguration, lambda: self.cmd.take_action(parsed_args)) - @mock.patch('tripleoclient.workflows.package_update.update_ansible', + @mock.patch('tripleoclient.utils.run_ansible_playbook', autospec=True) @mock.patch('os.path.expanduser') @mock.patch('oslo_concurrency.processutils.execute') diff --git a/tripleoclient/tests/workflows/test_deployment.py b/tripleoclient/tests/workflows/test_deployment.py index 7b5890941..180e3606d 100644 --- a/tripleoclient/tests/workflows/test_deployment.py +++ b/tripleoclient/tests/workflows/test_deployment.py @@ -16,7 +16,8 @@ import mock from osc_lib.tests import utils -from tripleoclient import exceptions +from tripleoclient import plugin +from tripleoclient.tests.fakes import FakeInstanceData from tripleoclient.tests.fakes import FakeStackObject from tripleoclient.workflows import deployment @@ -32,8 +33,28 @@ class TestDeploymentWorkflows(utils.TestCommand): self.websocket.__enter__ = lambda s: self.websocket self.websocket.__exit__ = lambda s, *exc: None self.tripleoclient.messaging_websocket.return_value = self.websocket - self.app.client_manager.tripleoclient = self.tripleoclient - + tc = self.app.client_manager.tripleoclient = self.tripleoclient + tc.create_mistral_context = plugin.ClientWrapper( + instance=FakeInstanceData + ).create_mistral_context + self.gcn = mock.patch( + 'tripleo_common.actions.config.DownloadConfigAction', + autospec=True + ) + self.gcn.start() + self.addCleanup(self.gcn.stop) + self.ansible = mock.patch( + 'tripleo_common.actions.ansible.AnsibleGenerateInventoryAction', + autospec=True + ) + self.ansible.start() + self.addCleanup(self.ansible.stop) + config_mock = mock.patch( + 'tripleo_common.actions.config.GetOvercloudConfig', + autospec=True + ) + config_mock.start() + self.addCleanup(config_mock.stop) self.message_success = iter([{ "execution": {"id": "IDID"}, "status": "SUCCESS", @@ -100,6 +121,7 @@ class TestDeploymentWorkflows(utils.TestCommand): self, mock_role_net_ip_map, mock_blacklisted_ip_addresses): stack = mock.Mock() + stack.output_show.return_value = [] mock_role_net_ip_map.return_value = { 'Controller': { 'ctlplane': ['1.1.1.1', '2.2.2.2', '3.3.3.3'], @@ -126,29 +148,15 @@ class TestDeploymentWorkflows(utils.TestCommand): expected = ['4.4.4.4', '6.6.6.6', '11.11.11.11'] self.assertEqual(sorted(expected), sorted(ips)) - def test_config_download_already_in_progress( - self): - log = mock.Mock() - stack = mock.Mock() - stack.stack_name = 'stacktest' - clients = mock.Mock() - mock_execution = mock.Mock() - mock_execution.input = '{"plan_name": "stacktest"}' - mock_return = mock.Mock(return_value=[mock_execution]) - clients.workflow_engine.executions.find = mock_return - - self.assertRaises(exceptions.ConfigDownloadInProgress, - deployment.config_download, - log, clients, stack, 'templates', 'ssh_user', - 'ssh_key', 'ssh_networks', 'output_dir', False, - 'timeout') - + @mock.patch('tripleoclient.utils.run_ansible_playbook', + autospec=True) @mock.patch('tripleoclient.workflows.deployment.base') def test_config_download_already_in_progress_for_diff_stack( - self, mock_base): + self, mock_base, mock_playbook): log = mock.Mock() stack = mock.Mock() stack.stack_name = 'stacktest' + stack.output_show.return_value = {'output': {'output_value': []}} clients = mock.Mock() mock_execution = mock.Mock() mock_execution.input = '{"plan_name": "someotherstack"}' diff --git a/tripleoclient/utils.py b/tripleoclient/utils.py index d936dfcb1..bb780be4e 100644 --- a/tripleoclient/utils.py +++ b/tripleoclient/utils.py @@ -60,9 +60,12 @@ from heatclient import exc as hc_exc from six.moves.urllib import error as url_error from six.moves.urllib import request +from tripleo_common.actions import config + from tripleoclient import constants from tripleoclient import exceptions + LOG = logging.getLogger(__name__ + ".utils") @@ -209,7 +212,7 @@ def run_ansible_playbook(playbook, inventory, workdir, playbook_dir=None, plan='overcloud', gathering_policy='smart', extra_env_variables=None, parallel_run=False, callback_whitelist=None, ansible_cfg=None, - ansible_timeout=30): + ansible_timeout=30, reproduce_command=False): """Simple wrapper for ansible-playbook. :param playbook: Playbook filename. @@ -286,6 +289,13 @@ def run_ansible_playbook(playbook, inventory, workdir, playbook_dir=None, :param ansible_timeout: Timeout for ansible connections. :type ansible_timeout: int + + :param reproduce_command: Enable or disable option to reproduce ansible + commands upon failure. This option will produce + a bash script that can reproduce a failing + playbook command which is helpful for debugging + and retry purposes. + :type reproduce_command: Boolean """ def _playbook_check(play): @@ -331,23 +341,7 @@ def run_ansible_playbook(playbook, inventory, workdir, playbook_dir=None, 'fact_cache' ) makedirs(ansible_fact_path) - extravars = { - 'ansible_ssh_extra_args': ( - '-o UserKnownHostsFile={} ' - '-o StrictHostKeyChecking=no ' - '-o ControlMaster=auto ' - '-o ControlPersist=30m ' - '-o ServerAliveInterval=64 ' - '-o ServerAliveCountMax=1024 ' - '-o Compression=no ' - '-o TCPKeepAlive=yes ' - '-o VerifyHostKeyDNS=no ' - '-o ForwardX11=no ' - '-o ForwardAgent=yes ' - '-o PreferredAuthentications=publickey ' - '-T' - ).format(os.devnull) - } + extravars = dict() if extra_vars: if isinstance(extra_vars, dict): extravars.update(extra_vars) @@ -363,6 +357,21 @@ def run_ansible_playbook(playbook, inventory, workdir, playbook_dir=None, callback_whitelist = ','.join([callback_whitelist, 'profile_tasks']) env = os.environ.copy() + env['ANSIBLE_SSH_ARGS'] = ( + '-o UserKnownHostsFile={} ' + '-o StrictHostKeyChecking=no ' + '-o ControlMaster=auto ' + '-o ControlPersist=30m ' + '-o ServerAliveInterval=64 ' + '-o ServerAliveCountMax=1024 ' + '-o Compression=no ' + '-o TCPKeepAlive=yes ' + '-o VerifyHostKeyDNS=no ' + '-o ForwardX11=no ' + '-o ForwardAgent=yes ' + '-o PreferredAuthentications=publickey ' + '-T' + ).format(os.devnull) env['ANSIBLE_DISPLAY_FAILED_STDERR'] = True env['ANSIBLE_FORKS'] = 36 env['ANSIBLE_TIMEOUT'] = ansible_timeout @@ -483,6 +492,8 @@ def run_ansible_playbook(playbook, inventory, workdir, playbook_dir=None, else: env.update(extra_env_variables) + command_path = None + with TempDirs(chdir=False) as ansible_artifact_path: if 'ANSIBLE_CONFIG' not in env and not ansible_cfg: ansible_cfg = os.path.join(ansible_artifact_path, 'ansible.cfg') @@ -533,6 +544,21 @@ def run_ansible_playbook(playbook, inventory, workdir, playbook_dir=None, runner_config.env['ANSIBLE_STDOUT_CALLBACK'] = \ r_opts['envvars']['ANSIBLE_STDOUT_CALLBACK'] runner = ansible_runner.Runner(config=runner_config) + + if reproduce_command: + command_path = os.path.join( + playbook_dir, + "ansible-playbook-command.sh" + ) + with open(command_path, 'w') as f: + f.write('#!/usr/bin/env bash\n') + f.write('echo -e "Exporting environment variables"\n') + for key, value in r_opts['envvars'].items(): + f.write('export {}="{}"\n'.format(key, value)) + f.write('echo -e "Running Ansible command"\n') + f.write('{} $@\n'.format(' '.join(runner_config.command))) + os.chmod(command_path, 0o750) + status, rc = runner.run() if rc != 0: @@ -545,6 +571,14 @@ def run_ansible_playbook(playbook, inventory, workdir, playbook_dir=None, rc ) ) + if command_path: + err_msg += ( + ', To rerun the failed command manually execute the' + ' following script: {}'.format( + command_path + ) + ) + if not quiet: LOG.error(err_msg) raise ansible_runner.AnsibleRunnerException(err_msg) @@ -1337,6 +1371,51 @@ def load_environment_directories(directories): return environments +def get_config(clients, container): + """Get cloud config. + + :param clients: Application client object. + :type clients: Object + + :param container: Container name to pull from. + :type container: String. + """ + + context = clients.tripleoclient.create_mistral_context() + config_action = config.GetOvercloudConfig(container=container) + config_action.run(context=context) + + +def get_key(stack): + """Returns the private key from the local file system. + + Searches for and returns the stack private key. If the key is inaccessible + for any reason, the process will fall back to using the users key. If no + key is found, this method will return None. + + :params stack: name of the stack to use + :type stack: String + + :returns: String || None + """ + + stack_dir = os.path.join(constants.DEFAULT_WORK_DIR, stack) + stack_key_file = os.path.join(stack_dir, 'ssh_private_key') + user_dir = os.path.join(os.path.expanduser("~"), '.ssh') + user_key_file = os.path.join(user_dir, 'id_rsa_tripleo') + legacy_dir = os.path.join(constants.DEFAULT_WORK_DIR, '.ssh') + legacy_key_file = os.path.join(legacy_dir, 'tripleo-admin-rsa') + for key_file in [stack_key_file, user_key_file, legacy_key_file]: + try: + if os.path.exists(key_file): + with open(key_file): + return key_file + except IOError: + pass + else: + return + + def get_tripleo_ansible_inventory(inventory_file=None, ssh_user='tripleo-admin', stack='overcloud', @@ -1352,7 +1431,7 @@ def get_tripleo_ansible_inventory(inventory_file=None, '--ansible_ssh_user', ssh_user, '--undercloud-connection', undercloud_connection, '--undercloud-key-file', - '/var/lib/mistral/.ssh/tripleo-admin-rsa', + get_key(stack=stack), '--static-yaml-inventory', inventory_file) except processutils.ProcessExecutionError as e: message = _("Failed to generate inventory: %s") % str(e) @@ -1483,7 +1562,8 @@ def run_update_ansible_action(log, clients, stack, nodes, inventory, module_path='/usr/share/ansible-modules', limit_hosts=nodes, tags=tags, - skip_tags=skip_tags) + skip_tags=skip_tags, + extra_vars=extra_vars) def parse_extra_vars(extra_var_strings): diff --git a/tripleoclient/v1/overcloud_deploy.py b/tripleoclient/v1/overcloud_deploy.py index 27d74c03b..eeb1acb61 100644 --- a/tripleoclient/v1/overcloud_deploy.py +++ b/tripleoclient/v1/overcloud_deploy.py @@ -675,7 +675,7 @@ class DeployOvercloud(command.Command): parser.add_argument( '--overcloud-ssh-key', default=os.path.join( - os.path.expanduser('~'), '.ssh', 'id_rsa'), + os.path.expanduser('~'), '.ssh', 'id_rsa_tripleo'), help=_('Key path for ssh access to overcloud nodes.') ) parser.add_argument( @@ -972,16 +972,17 @@ class DeployOvercloud(command.Command): parsed_args.deployment_python_interpreter deployment.config_download( - self.log, self.clients, stack, - parsed_args.templates, parsed_args.overcloud_ssh_user, - parsed_args.overcloud_ssh_key, + self.log, + self.clients, + stack, parsed_args.overcloud_ssh_network, parsed_args.output_dir, parsed_args.override_ansible_cfg, timeout, verbosity=(self.app_args.verbose_level - 1), deployment_options=deployment_options, - in_flight_validations=parsed_args.inflight) + in_flight_validations=parsed_args.inflight + ) except Exception: deployment.set_deployment_status( clients=self.clients, diff --git a/tripleoclient/v1/overcloud_external_update.py b/tripleoclient/v1/overcloud_external_update.py index 7c0cc5d67..102643343 100644 --- a/tripleoclient/v1/overcloud_external_update.py +++ b/tripleoclient/v1/overcloud_external_update.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. # + from oslo_config import cfg from oslo_log import log as logging @@ -86,10 +87,8 @@ class ExternalUpdateRun(command.Command): default=[]) parser.add_argument('--no-workflow', dest='no_workflow', action='store_true', - default=False, - help=_('Run ansible-playbook directly via ' - 'system command instead of running Ansible' - 'via the TripleO mistral workflows.') + default=True, + help=_('This option no longer has any effect.') ) return parser @@ -101,19 +100,20 @@ class ExternalUpdateRun(command.Command): verbosity = self.app_args.verbose_level - 1 stack = parsed_args.stack - ansible_dir = None - key = package_update.get_key(stack=stack) - # Disable mistral - if parsed_args.no_workflow: - ansible_dir = oooutils.download_ansible_playbooks(orchestration, - stack) + key, ansible_dir = self.get_ansible_key_and_dir( + no_workflow=parsed_args.no_workflow, + stack=stack, + orchestration=orchestration + ) # Run ansible: inventory = oooutils.get_tripleo_ansible_inventory( - parsed_args.static_inventory, parsed_args.ssh_user, stack) + parsed_args.static_inventory, parsed_args.ssh_user, stack, + return_inventory_file_path=True) limit_hosts = 'all' playbook = 'all' extra_vars = oooutils.parse_extra_vars(parsed_args.extra_vars) + extra_vars['ansible_become'] = True oooutils.run_update_ansible_action( self.log, clients, stack, limit_hosts, inventory, playbook, diff --git a/tripleoclient/v1/overcloud_external_upgrade.py b/tripleoclient/v1/overcloud_external_upgrade.py index 32998b8aa..0d3d5078d 100644 --- a/tripleoclient/v1/overcloud_external_upgrade.py +++ b/tripleoclient/v1/overcloud_external_upgrade.py @@ -86,10 +86,8 @@ class ExternalUpgradeRun(command.Command): default=[]) parser.add_argument('--no-workflow', dest='no_workflow', action='store_true', - default=False, - help=_('Run ansible-playbook directly via ' - 'system command instead of running Ansible' - 'via the TripleO mistral workflows.') + default=True, + help=_('This option no longer has any effect.') ) return parser @@ -101,20 +99,20 @@ class ExternalUpgradeRun(command.Command): verbosity = self.app_args.verbose_level - 1 stack = parsed_args.stack - ansible_dir = None - key = package_update.get_key(stack=stack) - # Disable mistral - if parsed_args.no_workflow: - ansible_dir = oooutils.download_ansible_playbooks(orchestration, - stack) + key, ansible_dir = self.get_ansible_key_and_dir( + no_workflow=parsed_args.no_workflow, + stack=stack, + orchestration=orchestration + ) # Run ansible: inventory = oooutils.get_tripleo_ansible_inventory( - parsed_args.static_inventory, parsed_args.ssh_user, stack) + parsed_args.static_inventory, parsed_args.ssh_user, stack, + return_inventory_file_path=True) limit_hosts = 'all' playbook = 'all' extra_vars = oooutils.parse_extra_vars(parsed_args.extra_vars) - + extra_vars['ansible_become'] = True oooutils.run_update_ansible_action( self.log, clients, stack, limit_hosts, inventory, playbook, constants.EXTERNAL_UPGRADE_PLAYBOOKS, parsed_args.ssh_user, diff --git a/tripleoclient/v1/overcloud_ffwd_upgrade.py b/tripleoclient/v1/overcloud_ffwd_upgrade.py index cf6590fa6..34c07e434 100644 --- a/tripleoclient/v1/overcloud_ffwd_upgrade.py +++ b/tripleoclient/v1/overcloud_ffwd_upgrade.py @@ -83,7 +83,7 @@ class FFWDUpgradePrepare(DeployOvercloud): super(FFWDUpgradePrepare, self).take_action(parsed_args) package_update.update(clients, container=stack_name) - package_update.get_config(clients, container=stack_name) + oooutils.get_config(clients, container=stack_name) overcloudrcs = deployment.create_overcloudrc( clients, container=stack_name) @@ -145,10 +145,8 @@ class FFWDUpgradeRun(command.Command): ) parser.add_argument('--no-workflow', dest='no_workflow', action='store_true', - default=False, - help=_('Run ansible-playbook directly via ' - 'system command instead of running Ansible' - 'via the TripleO mistral workflows.') + default=True, + help=_('This option no longer has any effect.') ) return parser @@ -160,24 +158,26 @@ class FFWDUpgradeRun(command.Command): orchestration = clients.orchestration stack = parsed_args.stack - ansible_dir = None - key = package_update.get_key(stack=stack) - # Disable mistral - if parsed_args.no_workflow: - ansible_dir = oooutils.download_ansible_playbooks(orchestration, - stack) + key, ansible_dir = self.get_ansible_key_and_dir( + no_workflow=parsed_args.no_workflow, + stack=stack, + orchestration=orchestration + ) # Run ansible: inventory = oooutils.get_tripleo_ansible_inventory( inventory_file=parsed_args.static_inventory, - ssh_user=parsed_args.ssh_user, stack=parsed_args.stack) + ssh_user=parsed_args.ssh_user, stack=parsed_args.stack, + return_inventory_file_path=True) # Don't expost limit_hosts. We need this on the whole overcloud. limit_hosts = '' + extra_vars = {'ansible_become': True} oooutils.run_update_ansible_action( self.log, clients, parsed_args.stack, limit_hosts, inventory, constants.FFWD_UPGRADE_PLAYBOOK, [], parsed_args.ssh_user, (None if parsed_args.no_workflow else package_update), - verbosity=verbosity, workdir=ansible_dir, priv_key=key) + verbosity=verbosity, workdir=ansible_dir, priv_key=key, + extra_vars=extra_vars) class FFWDUpgradeConverge(DeployOvercloud): diff --git a/tripleoclient/v1/overcloud_node.py b/tripleoclient/v1/overcloud_node.py index 290c61c49..466a4dff8 100644 --- a/tripleoclient/v1/overcloud_node.py +++ b/tripleoclient/v1/overcloud_node.py @@ -198,10 +198,11 @@ class DeleteNode(command.Command): .format(stack=stack.stack_name, nodes=nodes_text)) scale.scale_down( - clients, - stack.stack_name, - nodes, - parsed_args.timeout + log=self.log, + clients=clients, + stack=stack, + nodes=nodes, + timeout=parsed_args.timeout ) if parsed_args.baremetal_deployment: diff --git a/tripleoclient/v1/overcloud_update.py b/tripleoclient/v1/overcloud_update.py index aa5b1bab3..7a2ed0d40 100644 --- a/tripleoclient/v1/overcloud_update.py +++ b/tripleoclient/v1/overcloud_update.py @@ -72,7 +72,7 @@ class UpdatePrepare(DeployOvercloud): super(UpdatePrepare, self).take_action(parsed_args) package_update.update(clients, container=stack_name) - package_update.get_config(clients, container=stack_name) + oooutils.get_config(clients, container=stack_name) self.log.info("Update init on stack {0} complete.".format( parsed_args.stack)) @@ -131,10 +131,8 @@ class UpdateRun(command.Command): ) parser.add_argument('--no-workflow', dest='no_workflow', action='store_true', - default=False, - help=_('Run ansible-playbook directly via ' - 'system command instead of running Ansible' - 'via the TripleO mistral workflows.') + default=True, + help=_('This option no longer has any effect.') ) return parser @@ -146,19 +144,20 @@ class UpdateRun(command.Command): verbosity = self.app_args.verbose_level - 1 stack = parsed_args.stack - ansible_dir = None - key = package_update.get_key(stack=stack) - # Disable mistral - if parsed_args.no_workflow: - ansible_dir = oooutils.download_ansible_playbooks(orchestration, - stack) + key, ansible_dir = self.get_ansible_key_and_dir( + no_workflow=parsed_args.no_workflow, + stack=stack, + orchestration=orchestration + ) # Run ansible: limit_hosts = parsed_args.limit playbook = parsed_args.playbook inventory = oooutils.get_tripleo_ansible_inventory( - parsed_args.static_inventory, parsed_args.ssh_user, stack) + parsed_args.static_inventory, parsed_args.ssh_user, stack, + return_inventory_file_path=True) + extra_vars = {'ansible_become': True} oooutils.run_update_ansible_action(self.log, clients, stack, limit_hosts, inventory, playbook, constants.MINOR_UPDATE_PLAYBOOKS, @@ -167,7 +166,8 @@ class UpdateRun(command.Command): else package_update), verbosity=verbosity, workdir=ansible_dir, - priv_key=key) + priv_key=key, + extra_vars=extra_vars) class UpdateConverge(DeployOvercloud): diff --git a/tripleoclient/v1/overcloud_upgrade.py b/tripleoclient/v1/overcloud_upgrade.py index a48c2d72d..07d9feaf7 100644 --- a/tripleoclient/v1/overcloud_upgrade.py +++ b/tripleoclient/v1/overcloud_upgrade.py @@ -73,7 +73,7 @@ class UpgradePrepare(DeployOvercloud): constants.UPGRADE_PREPARE_ENV) super(UpgradePrepare, self).take_action(parsed_args) package_update.update(clients, container=stack_name) - package_update.get_config(clients, container=stack_name) + oooutils.get_config(clients, container=stack_name) overcloudrcs = deployment.create_overcloudrc( clients, container=stack_name) @@ -179,10 +179,8 @@ class UpgradeRun(command.Command): default='overcloud')) parser.add_argument('--no-workflow', dest='no_workflow', action='store_true', - default=False, - help=_('Run ansible-playbook directly via ' - 'system command instead of running Ansible' - 'via the TripleO mistral workflows.') + default=True, + help=_('This option no longer has any effect.') ) return parser @@ -204,20 +202,21 @@ class UpgradeRun(command.Command): orchestration = clients.orchestration stack = parsed_args.stack - ansible_dir = None - key = package_update.get_key(stack=stack) - # Disable mistral - if parsed_args.no_workflow: - ansible_dir = oooutils.download_ansible_playbooks(orchestration, - stack) + key, ansible_dir = self.get_ansible_key_and_dir( + no_workflow=parsed_args.no_workflow, + stack=stack, + orchestration=orchestration + ) # Run ansible: limit_hosts = parsed_args.limit playbook = parsed_args.playbook inventory = oooutils.get_tripleo_ansible_inventory( - parsed_args.static_inventory, parsed_args.ssh_user, stack) + parsed_args.static_inventory, parsed_args.ssh_user, stack, + return_inventory_file_path=True) skip_tags = self._validate_skip_tags(parsed_args.skip_tags) + extra_vars = {'ansible_become': True} oooutils.run_update_ansible_action(self.log, clients, stack, limit_hosts, inventory, playbook, constants.MAJOR_UPGRADE_PLAYBOOKS, @@ -228,7 +227,8 @@ class UpgradeRun(command.Command): skip_tags, verbosity, workdir=ansible_dir, - priv_key=key) + priv_key=key, + extra_vars=extra_vars) playbooks = (constants.MAJOR_UPGRADE_PLAYBOOKS if playbook == 'all' else playbook) diff --git a/tripleoclient/workflows/deployment.py b/tripleoclient/workflows/deployment.py index b5ad7827f..b32ba7b73 100644 --- a/tripleoclient/workflows/deployment.py +++ b/tripleoclient/workflows/deployment.py @@ -12,18 +12,20 @@ from __future__ import print_function import copy +import getpass import os import pprint import time -import yaml from heatclient.common import event_utils from openstackclient import shell +from tripleo_common.actions import ansible from tripleo_common.actions import config from tripleo_common.actions import deployment from tripleo_common.actions import swifthelper from tripleoclient.constants import ANSIBLE_TRIPLEO_PLAYBOOKS +from tripleoclient.constants import DEFAULT_WORK_DIR from tripleoclient import exceptions from tripleoclient import utils @@ -251,70 +253,209 @@ def enable_ssh_admin(stack, hosts, ssh_user, ssh_key, timeout): print("Enabling ssh admin - COMPLETE.") -def config_download(log, clients, stack, templates, - ssh_user, ssh_key, ssh_network, - output_dir, override_ansible_cfg, timeout, verbosity=0, - deployment_options={}, - in_flight_validations=False): - workflow_client = clients.workflow_engine - tripleoclients = clients.tripleoclient +def config_download(log, clients, stack, ssh_network=None, + output_dir=None, override_ansible_cfg=None, + timeout=None, verbosity=0, deployment_options=None, + in_flight_validations=False, + ansible_playbook_name='deploy_steps_playbook.yaml', + limit_list=None): + """Run config download. - if in_flight_validations: - skip_tags = '' - else: + :param log: Logging object + :type log: Object + + :param clients: openstack clients + :type clients: Object + + :param stack: Heat Stack object + :type stack: Object + + :param ssh_network: Network named used to access the overcloud. + :type ssh_network: String + + :param override_ansible_cfg: Ansible configuration file location. + :type override_ansible_cfg: String + + :param timeout: Ansible connection timeout. If None, the effective + default will be set to 30 at playbook runtime. + :type timeout: Integer + + :param verbosity: Ansible verbosity level. + :type verbosity: Integer + + :param deployment_options: Additional deployment options. + :type deployment_options: Dictionary + + :param in_flight_validations: Enable or Disable inflight validations. + :type in_flight_validations: Boolean + + :param ansible_playbook_name: Name of the playbook to execute. + :type ansible_playbook_name: String + + :param limit_list: List of hosts to limit the current playbook to. + :type limit_list: List + """ + + def _log_and_print(message, logger, level='info', print_msg=True): + """Print and log a given message. + + :param message: Message to print and log. + :type message: String + + :param log: Logging object + :type log: Object + + :param level: Log level. + :type level: String + + :param print_msg: Print messages to stdout. + :type print_msg: Boolean + """ + + if print_msg: + print(message) + + log = getattr(logger, level) + log(message) + + if not output_dir: + output_dir = DEFAULT_WORK_DIR + + if not deployment_options: + deployment_options = dict() + + if not in_flight_validations: skip_tags = 'opendev-validation' + else: + skip_tags = None - workflow_input = { - 'verbosity': verbosity, - 'plan_name': stack.stack_name, - 'ssh_network': ssh_network, - 'config_download_timeout': timeout, - 'deployment_options': deployment_options, - 'skip_tags': skip_tags - } - if output_dir: - workflow_input.update(dict(work_dir=output_dir)) - if override_ansible_cfg: - with open(override_ansible_cfg) as cfg: - override_ansible_cfg_contents = cfg.read() - workflow_input.update( - dict(override_ansible_cfg=override_ansible_cfg_contents)) + if not timeout: + timeout = 30 - workflow_name = 'tripleo.deployment.v1.config_download_deploy' + # NOTE(cloudnull): List of hosts to limit the current playbook execution + # The list is later converted into an ansible compatible + # string. Storing hosts in list format will ensure all + # entries are consistent. + if not limit_list: + limit_list = list() - # Check to see if any existing executions for the same stack are already in - # progress. - log.info("Checking for existing executions of config_download for " - "%s" % stack.stack_name) - for execution in workflow_client.executions.find( - workflow_name=workflow_name, - state='RUNNING'): - - try: - exec_input = yaml.safe_load(execution.input) - except yaml.YAMLError as ye: - log.error("YAML error loading input for execution %s: %s" % - (execution.id, str(ye))) - raise - - if exec_input.get('plan_name', 'overcloud') == stack.stack_name: - raise exceptions.ConfigDownloadInProgress(execution.id, - stack.stack_name) - - with tripleoclients.messaging_websocket() as ws: - execution = base.start_workflow( - workflow_client, - workflow_name, - workflow_input=workflow_input + with utils.TempDirs() as tmp: + utils.run_ansible_playbook( + playbook='cli-grant-local-access.yaml', + inventory='localhost,', + workdir=tmp, + playbook_dir=ANSIBLE_TRIPLEO_PLAYBOOKS, + extra_vars={ + 'access_path': output_dir, + 'execution_user': getpass.getuser() + } ) - for payload in base.wait_for_messages(workflow_client, ws, execution): - print(payload['message']) + stack_work_dir = os.path.join(output_dir, stack.stack_name) + context = clients.tripleoclient.create_mistral_context() + _log_and_print( + message='Checking for blacklisted hosts from stack: {}'.format( + stack.stack_name + ), + logger=log, + print_msg=(verbosity == 0) + ) + blacklist_show = stack.output_show('BlacklistedHostnames') + blacklist_stack_output = blacklist_show.get('output', dict()) + blacklist_stack_output_value = blacklist_stack_output.get('output_value') + if blacklist_stack_output_value: + limit_list.extend( + ['!{}'.format(i) for i in blacklist_stack_output_value if i] + ) + _log_and_print( + message='Retrieving configuration for stack: {}'.format( + stack.stack_name + ), + logger=log, + print_msg=(verbosity == 0) + ) + utils.get_config(clients, container=stack.stack_name) + _log_and_print( + message='Downloading configuration for stack: {}'.format( + stack.stack_name + ), + logger=log, + print_msg=(verbosity == 0) + ) + download = config.DownloadConfigAction( + work_dir=stack_work_dir, + container_config='{}-config'.format(stack.stack_name) + ) + work_dir = download.run(context=context) + _log_and_print( + message='Retrieving keyfile for stack: {}'.format( + stack.stack_name + ), + logger=log, + print_msg=(verbosity == 0) + ) + key_file = utils.get_key(stack=stack.stack_name) + _log_and_print( + message='Generating information for stack: {}'.format( + stack.stack_name + ), + logger=log, + print_msg=(verbosity == 0) + ) + inventory_kwargs = { + 'ansible_ssh_user': 'tripleo-admin', + 'work_dir': work_dir, + 'plan_name': stack.stack_name, + 'undercloud_key_file': key_file + } + if ssh_network: + inventory_kwargs['ssh_network'] = ssh_network + python_interpreter = deployment_options.get('ansible_python_interpreter') + if python_interpreter: + inventory_kwargs['ansible_python_interpreter'] = python_interpreter + inventory = ansible.AnsibleGenerateInventoryAction(**inventory_kwargs) + inventory_path = inventory.run(context=context) + _log_and_print( + message='Executing deployment playbook for stack: {}'.format( + stack.stack_name + ), + logger=log, + print_msg=(verbosity == 0) + ) - if payload['status'] == 'SUCCESS': - print("Overcloud configuration completed.") - else: - raise exceptions.DeploymentError("Overcloud configuration failed.") + # NOTE(cloudnull): Join the limit_list into an ansible compatible string. + # If it is an empty, the object will be reset to None. + limit_hosts = ':'.join(limit_list) + + with utils.TempDirs() as tmp: + utils.run_ansible_playbook( + playbook=os.path.join( + stack_work_dir, + ansible_playbook_name + ), + inventory=inventory_path, + workdir=tmp, + playbook_dir=work_dir, + skip_tags=skip_tags, + ansible_cfg=override_ansible_cfg, + verbosity=verbosity, + ssh_user='tripleo-admin', + key=key_file, + limit_hosts=limit_hosts, + ansible_timeout=timeout, + reproduce_command=True, + extra_env_variables={ + 'ANSIBLE_BECOME': True, + } + ) + + _log_and_print( + message='Overcloud configuration completed for stack: {}'.format( + stack.stack_name + ), + logger=log, + print_msg=(verbosity == 0) + ) def config_download_export(clients, plan, config_type): diff --git a/tripleoclient/workflows/package_update.py b/tripleoclient/workflows/package_update.py index 9595ab284..b11583afb 100644 --- a/tripleoclient/workflows/package_update.py +++ b/tripleoclient/workflows/package_update.py @@ -11,20 +11,17 @@ # under the License. from __future__ import print_function -import os import pprint import time - from heatclient.common import event_utils from openstackclient import shell -from tripleo_common.actions import config -from tripleoclient import constants from tripleoclient import exceptions from tripleoclient import utils from tripleoclient.workflows import base + _WORKFLOW_TIMEOUT = 120 * 60 # 2h @@ -67,49 +64,6 @@ def update(clients, **workflow_input): raise exceptions.DeploymentError("Heat Stack update failed.") -def get_config(clients, container): - """Get cloud config. - - :param clients: Application client object. - :type clients: Object - - :param container: Container name to pull from. - :type container: String. - """ - - context = clients.tripleoclient.create_mistral_context() - config_action = config.GetOvercloudConfig(container=container) - config_action.run(context=context) - - -def get_key(stack): - """Returns the private key from the local file system. - - Searches for and returns the stack private key. If the key is inaccessible - for any reason, the process will fall back to using the users key. If no - key is found, this method will return None. - - :params stack: name of the stack to use - :type stack: String - - :returns: String || None - """ - - stack_dir = os.path.join(constants.DEFAULT_WORK_DIR, stack) - stack_key_file = os.path.join(stack_dir, 'ssh_private_key') - user_dir = os.path.join(os.path.expanduser("~"), '.ssh') - user_key_file = os.path.join(user_dir, 'id_rsa_tripleo') - for key_file in [stack_key_file, user_key_file]: - try: - if os.path.exists(key_file): - with open(key_file): - return key_file - except IOError: - pass - else: - return - - def update_ansible(clients, **workflow_input): workflow_client = clients.workflow_engine tripleoclients = clients.tripleoclient diff --git a/tripleoclient/workflows/scale.py b/tripleoclient/workflows/scale.py index 5f889f02b..27133e96b 100644 --- a/tripleoclient/workflows/scale.py +++ b/tripleoclient/workflows/scale.py @@ -16,65 +16,58 @@ from __future__ import print_function from tripleo_common.actions import scale -from tripleoclient import exceptions from tripleoclient import utils -from tripleoclient.workflows import base +from tripleoclient.workflows import deployment -def ansible_tear_down(clients, **workflow_input): - - workflow_client = clients.workflow_engine - tripleoclients = clients.tripleoclient - workflow_input['playbook_name'] = 'scale_playbook.yaml' - - with tripleoclients.messaging_websocket() as ws: - execution = base.start_workflow( - workflow_client, - 'tripleo.deployment.v1.config_download_deploy', - workflow_input=workflow_input - ) - - for payload in base.wait_for_messages(workflow_client, ws, execution): - print(payload['message']) - - if payload['status'] == 'SUCCESS': - print("Scale-down configuration completed.") - else: - raise exceptions.DeploymentError("Scale-down configuration failed.") - - -def scale_down(clients, plan_name, nodes, timeout=None): +def scale_down(log, clients, stack, nodes, timeout=None): """Unprovision and deletes overcloud nodes from a heat stack. + :param log: Logging object + :type log: Object + :param clients: Application client object. :type clients: Object + :param stack: Heat Stack object + :type stack: Object + + :param nodes: List of nodes to delete. If the node UUID is used the + UUID will be used to lookup the node name before being + passed through to the cleanup playbook. + :type nodes: List + :param timeout: Timeout to use when deleting nodes. If timeout is None it will be set to 240. :type timeout: Integer - - :param plan: Plan name. - :type plan: String - - :param nodes: List of nodes to delete. - :type nodes: List """ - workflow_input = { - "plan_name": plan_name, - "nodes": nodes, - } - - ansible_tear_down(clients, **workflow_input) - if not timeout: timeout = 240 + limit_list = list() + for node in nodes: + try: + _node = clients.compute.servers.get(node) + limit_list.append(_node.name) + except Exception: + limit_list.append(node) + + deployment.config_download( + log=log, + clients=clients, + stack=stack, + timeout=timeout, + ansible_playbook_name='scale_playbook.yaml', + limit_list=limit_list + ) + + print('Running scale down') context = clients.tripleoclient.create_mistral_context() scale_down_action = scale.ScaleDownAction(nodes=nodes, timeout=timeout) scale_down_action.run(context=context) utils.wait_for_stack_ready( orchestration_client=clients.orchestration, - stack_name=plan_name, + stack_name=stack.stack_name, action='UPDATE' )