diff --git a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py index 7b2a354e1..2b2868dbe 100644 --- a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py +++ b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py @@ -1517,7 +1517,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): 'heat-admin', None, 'ctlplane', None, None, deployment_options={}, deployment_timeout=26880, # 451 - 3 = 448m time left - in_flight_validations=False, timeout=42, verbosity=1)], + in_flight_validations=False, limit_hosts=None, + skip_tags=None, tags=None, timeout=42, verbosity=1)], fixture.mock_config_download.mock_calls) fixture.mock_config_download.assert_called() mock_copy.assert_called_once() @@ -1571,7 +1572,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): 'heat-admin', None, 'ctlplane', None, None, deployment_options={}, deployment_timeout=14400, # 240 * 60 - in_flight_validations=False, timeout=42, verbosity=1)], + in_flight_validations=False, limit_hosts=None, + skip_tags=None, tags=None, timeout=42, verbosity=1)], fixture.mock_config_download.mock_calls) mock_copy.assert_called_once() 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 5bd730dc0..4a59ab067 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 @@ -56,7 +56,7 @@ class TestOvercloudExternalUpdateRun(fakes.TestOvercloudExternalUpdateRun): update_ansible.assert_called_once_with( self.app.client_manager, container='overcloud', - nodes='all', + nodes=None, inventory_file=mock_open().__enter__().read(), playbook='external_update_steps_playbook.yaml', node_user='tripleo-admin', @@ -89,7 +89,7 @@ class TestOvercloudExternalUpdateRun(fakes.TestOvercloudExternalUpdateRun): update_ansible.assert_called_once_with( self.app.client_manager, container='overcloud', - nodes='all', + nodes=None, inventory_file=mock_open().__enter__().read(), playbook='external_update_steps_playbook.yaml', node_user='tripleo-admin', 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 abe0d3a4b..31404e31c 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 @@ -56,7 +56,7 @@ class TestOvercloudExternalUpgradeRun(fakes.TestOvercloudExternalUpgradeRun): update_ansible.assert_called_once_with( self.app.client_manager, container='overcloud', - nodes='all', + nodes=None, inventory_file=mock_open().__enter__().read(), playbook='external_upgrade_steps_playbook.yaml', node_user='tripleo-admin', @@ -89,7 +89,7 @@ class TestOvercloudExternalUpgradeRun(fakes.TestOvercloudExternalUpgradeRun): update_ansible.assert_called_once_with( self.app.client_manager, container='overcloud', - nodes='all', + nodes=None, inventory_file=mock_open().__enter__().read(), playbook='external_upgrade_steps_playbook.yaml', node_user='tripleo-admin', 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 aef150018..ae00f8a44 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 @@ -166,11 +166,11 @@ class TestFFWDUpgradeRun(fakes.TestFFWDUpgradeRun): self.app.client_manager, container='overcloud', inventory_file=mock_open().__enter__().read(), - nodes='', + nodes=None, playbook=constants.FFWD_UPGRADE_PLAYBOOK, node_user='heat-admin', - tags='', - skip_tags='', + tags=None, + skip_tags=None, verbosity=1, extra_vars=None ) @@ -194,11 +194,11 @@ class TestFFWDUpgradeRun(fakes.TestFFWDUpgradeRun): self.app.client_manager, container='overcloud', inventory_file=mock_open().__enter__().read(), - nodes='', + nodes=None, playbook=constants.FFWD_UPGRADE_PLAYBOOK, node_user='my-user', - tags='', - skip_tags='', + tags=None, + skip_tags=None, verbosity=1, extra_vars=None ) @@ -211,11 +211,24 @@ class TestFFWDUpgradeRun(fakes.TestFFWDUpgradeRun): def test_upgrade_no_nodes_or_roles(self, mock_open, mock_execute, mock_expanduser, upgrade_ansible): mock_expanduser.return_value = '/home/fake/' - argslist = ["--nodes", "controller-1", "--roles", "foo", "--yes"] + argslist = ["--limit", "controller-1", "--roles", "foo", "--yes"] verifylist = [] self.assertRaises(ParserException, lambda: self.check_parser( self.cmd, argslist, verifylist)) + @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(self, mock_open, mock_execute, + mock_expanduser, upgrade_ansible): + mock_expanduser.return_value = '/home/fake/' + argslist = ['--limit', 'controller1,controller2,controll3'] + verifylist = [('limit', 'controller1,controller2,controll3')] + self.assertRaises(ParserException, lambda: self.check_parser( + self.cmd, argslist, verifylist)) + class TestFFWDUpgradeConverge(fakes.TestFFWDUpgradeConverge): diff --git a/tripleoclient/tests/v1/overcloud_update/test_overcloud_update.py b/tripleoclient/tests/v1/overcloud_update/test_overcloud_update.py index 5866e90cf..69e94f6a7 100644 --- a/tripleoclient/tests/v1/overcloud_update/test_overcloud_update.py +++ b/tripleoclient/tests/v1/overcloud_update/test_overcloud_update.py @@ -153,8 +153,8 @@ class TestOvercloudUpdateRun(fakes.TestOvercloudUpdateRun): inventory_file=mock_open().__enter__().read(), playbook='fake-playbook.yaml', node_user='tripleo-admin', - tags='', - skip_tags='', + tags=None, + skip_tags=None, verbosity=1, extra_vars=None ) @@ -186,8 +186,8 @@ class TestOvercloudUpdateRun(fakes.TestOvercloudUpdateRun): inventory_file=mock_open().__enter__().read(), playbook=book, node_user='tripleo-admin', - tags='', - skip_tags='', + tags=None, + skip_tags=None, verbosity=1, extra_vars=None ) diff --git a/tripleoclient/tests/v1/overcloud_upgrade/test_overcloud_upgrade.py b/tripleoclient/tests/v1/overcloud_upgrade/test_overcloud_upgrade.py index a3dcfe9d7..96688c7a6 100644 --- a/tripleoclient/tests/v1/overcloud_upgrade/test_overcloud_upgrade.py +++ b/tripleoclient/tests/v1/overcloud_upgrade/test_overcloud_upgrade.py @@ -189,7 +189,7 @@ class TestOvercloudUpgradeRun(fakes.TestOvercloudUpgradeRun): upgrade_ansible.assert_called_once_with( self.app.client_manager, container='overcloud', - nodes='Compute, Controller', + nodes='Compute:Controller', inventory_file=mock_open().__enter__().read(), playbook='fake-playbook.yaml', node_user='tripleo-admin', @@ -277,10 +277,10 @@ class TestOvercloudUpgradeRun(fakes.TestOvercloudUpgradeRun): def test_upgrade_nodes_with_playbook_no_skip_tags( self, mock_open, mock_execute, mock_expanduser, upgrade_ansible): mock_expanduser.return_value = '/home/fake/' - argslist = ['--limit', 'compute-0, compute-1', + argslist = ['--limit', 'compute-0,compute-1', '--playbook', 'fake-playbook.yaml', ] verifylist = [ - ('limit', 'compute-0, compute-1'), + ('limit', 'compute-0,compute-1'), ('static_inventory', None), ('playbook', 'fake-playbook.yaml'), ] @@ -292,7 +292,7 @@ class TestOvercloudUpgradeRun(fakes.TestOvercloudUpgradeRun): upgrade_ansible.assert_called_once_with( self.app.client_manager, container='overcloud', - nodes='compute-0, compute-1', + nodes='compute-0:compute-1', inventory_file=mock_open().__enter__().read(), playbook='fake-playbook.yaml', node_user='tripleo-admin', diff --git a/tripleoclient/utils.py b/tripleoclient/utils.py index 876dfc39e..e36bbd9b8 100644 --- a/tripleoclient/utils.py +++ b/tripleoclient/utils.py @@ -35,6 +35,7 @@ import json import netaddr import os import os.path +import re import simplejson import six import socket @@ -77,8 +78,8 @@ def run_ansible_playbook(logger, key=None, module_path=None, limit_hosts=None, - tags=None, skip_tags=None, + tags=None, verbosity=1, extra_vars=None, plan='overcloud', @@ -1212,7 +1213,7 @@ def process_multiple_environments(created_env_files, tht_root, def run_update_ansible_action(log, clients, stack, nodes, inventory, playbook, all_playbooks, ssh_user, - action=None, tags='', skip_tags='', + action=None, skip_tags=None, tags=None, verbosity='1', extra_vars=None, workdir='', priv_key=''): @@ -1225,7 +1226,7 @@ def run_update_ansible_action(log, clients, stack, nodes, inventory, action.update_ansible(clients, container=stack, nodes=nodes, inventory_file=inventory, playbook=book, node_user=ssh_user, - tags=tags, skip_tags=skip_tags, + skip_tags=skip_tags, tags=tags, verbosity=verbosity, extra_vars=extra_vars) else: run_ansible_playbook(logger=LOG, @@ -1235,8 +1236,9 @@ def run_update_ansible_action(log, clients, stack, nodes, inventory, ssh_user=ssh_user, key=ssh_private_key(workdir, priv_key), module_path='/usr/share/ansible-modules', - limit_hosts=nodes, tags=tags, - skip_tags=skip_tags) + limit_hosts=nodes, + skip_tags=skip_tags, + tags=tags) def ssh_private_key(workdir, key): @@ -2102,3 +2104,20 @@ def copy_clouds_yaml(user): ' with sudo') % {'user': user, 'dir': clouds_config_dir} LOG.error(msg) raise exceptions.DeploymentError(msg) + + +def playbook_limit_parse(limit_nodes): + """Return a parsed string for limits. + + This will sanitize user inputs so that we guarantee what is provided is + expected to be functional. If limit_nodes is None, this function will + return None. + + + :returns: String + """ + + if not limit_nodes: + return limit_nodes + + return ':'.join([i.strip() for i in re.split(',| |:', limit_nodes) if i]) diff --git a/tripleoclient/v1/overcloud_deploy.py b/tripleoclient/v1/overcloud_deploy.py index e8706a857..5e324b190 100644 --- a/tripleoclient/v1/overcloud_deploy.py +++ b/tripleoclient/v1/overcloud_deploy.py @@ -903,6 +903,29 @@ class DeployOvercloud(command.Command): metavar='', help=_('Configuration file describing the ' 'baremetal deployment')) + parser.add_argument( + '--limit', + action='store', + default=None, + help=_("A string that identifies a single node or comma-separated" + "list of nodes the config-download Ansible playbook " + "execution will be limited to. For example: --limit" + " \"compute-0,compute-1,compute-5\".") + ) + parser.add_argument( + '--tags', + action='store', + default=None, + help=_('A list of tags to use when running the config-download' + ' ansible-playbook command.') + ) + parser.add_argument( + '--skip-tags', + action='store', + default=None, + help=_('A list of tags to skip when running the' + ' config-download ansible-playbook command.') + ) return parser def take_action(self, parsed_args): @@ -997,7 +1020,12 @@ class DeployOvercloud(command.Command): verbosity=self.app_args.verbose_level, deployment_options=deployment_options, in_flight_validations=parsed_args.inflight, - deployment_timeout=timeout + deployment_timeout=timeout, + tags=parsed_args.tags, + skip_tags=parsed_args.skip_tags, + limit_hosts=utils.playbook_limit_parse( + limit_nodes=parsed_args.limit + ) ) except Exception: deployment.set_deployment_status( diff --git a/tripleoclient/v1/overcloud_external_update.py b/tripleoclient/v1/overcloud_external_update.py index f748bb1fe..c731c3146 100644 --- a/tripleoclient/v1/overcloud_external_update.py +++ b/tripleoclient/v1/overcloud_external_update.py @@ -91,6 +91,15 @@ class ExternalUpdateRun(command.Command): 'system command instead of running Ansible' 'via the TripleO mistral workflows.') ) + parser.add_argument( + '--limit', + action='store', + default=None, + help=_("A string that identifies a single node or comma-separated" + "list of nodes the config-download Ansible playbook " + "execution will be limited to. For example: --limit" + " \"compute-0,compute-1,compute-5\".") + ) return parser @@ -112,7 +121,8 @@ class ExternalUpdateRun(command.Command): # Run ansible: inventory = oooutils.get_tripleo_ansible_inventory( parsed_args.static_inventory, parsed_args.ssh_user, stack) - limit_hosts = 'all' + limit_hosts = oooutils.playbook_limit_parse( + limit_nodes=parsed_args.limit) playbook = 'all' extra_vars = oooutils.parse_extra_vars(parsed_args.extra_vars) diff --git a/tripleoclient/v1/overcloud_external_upgrade.py b/tripleoclient/v1/overcloud_external_upgrade.py index 49629b41b..3da54b870 100644 --- a/tripleoclient/v1/overcloud_external_upgrade.py +++ b/tripleoclient/v1/overcloud_external_upgrade.py @@ -91,6 +91,15 @@ class ExternalUpgradeRun(command.Command): 'system command instead of running Ansible' 'via the TripleO mistral workflows.') ) + parser.add_argument( + '--limit', + action='store', + default=None, + help=_("A string that identifies a single node or comma-separated" + "list of nodes the config-download Ansible playbook " + "execution will be limited to. For example: --limit" + " \"compute-0,compute-1,compute-5\".") + ) return parser @@ -112,7 +121,8 @@ class ExternalUpgradeRun(command.Command): # Run ansible: inventory = oooutils.get_tripleo_ansible_inventory( parsed_args.static_inventory, parsed_args.ssh_user, stack) - limit_hosts = 'all' + limit_hosts = oooutils.playbook_limit_parse( + limit_nodes=parsed_args.limit) playbook = 'all' extra_vars = oooutils.parse_extra_vars(parsed_args.extra_vars) diff --git a/tripleoclient/v1/overcloud_ffwd_upgrade.py b/tripleoclient/v1/overcloud_ffwd_upgrade.py index f1f31b692..6dc0d1f2a 100644 --- a/tripleoclient/v1/overcloud_ffwd_upgrade.py +++ b/tripleoclient/v1/overcloud_ffwd_upgrade.py @@ -171,7 +171,7 @@ class FFWDUpgradeRun(command.Command): inventory_file=parsed_args.static_inventory, ssh_user=parsed_args.ssh_user, stack=parsed_args.stack) # Don't expost limit_hosts. We need this on the whole overcloud. - limit_hosts = '' + limit_hosts = None oooutils.run_update_ansible_action( self.log, clients, parsed_args.stack, limit_hosts, inventory, constants.FFWD_UPGRADE_PLAYBOOK, [], parsed_args.ssh_user, diff --git a/tripleoclient/v1/overcloud_update.py b/tripleoclient/v1/overcloud_update.py index 8fb1ddc1f..02f054a14 100644 --- a/tripleoclient/v1/overcloud_update.py +++ b/tripleoclient/v1/overcloud_update.py @@ -85,11 +85,13 @@ class UpdateRun(command.Command): def get_parser(self, prog_name): parser = super(UpdateRun, self).get_parser(prog_name) parser.add_argument( - '--limit', action='store', required=True, help=_( - "A string that identifies a single node or comma-separated" - " list of nodes to be upgraded in parallel in this upgrade" - " run invocation. For example: --limit \"compute-0," - " compute-1, compute-5\".") + '--limit', + action='store', + required=True, + help=_("A string that identifies a single node or comma-separated" + "list of nodes the config-download Ansible playbook " + "execution will be limited to. For example: --limit" + " \"compute-0,compute-1,compute-5\".") ) parser.add_argument('--playbook', action="store", @@ -136,7 +138,20 @@ class UpdateRun(command.Command): 'system command instead of running Ansible' 'via the TripleO mistral workflows.') ) - + parser.add_argument( + '--tags', + action='store', + default=None, + help=_('A list of tags to use when running the config-download' + ' ansible-playbook command.') + ) + parser.add_argument( + '--skip-tags', + action='store', + default=None, + help=_('A list of tags to skip when running the' + ' config-download ansible-playbook command.') + ) return parser def take_action(self, parsed_args): @@ -155,20 +170,26 @@ class UpdateRun(command.Command): key = package_update.get_key(clients) # 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) - oooutils.run_update_ansible_action(self.log, clients, stack, - limit_hosts, inventory, playbook, - constants.MINOR_UPDATE_PLAYBOOKS, - parsed_args.ssh_user, - (None if parsed_args.no_workflow - else package_update), - verbosity=verbosity, - workdir=ansible_dir, - priv_key=key) + limit_hosts = oooutils.playbook_limit_parse( + limit_nodes=parsed_args.limit) + oooutils.run_update_ansible_action( + self.log, + clients, + stack, + limit_hosts, + inventory, + playbook, + constants.MINOR_UPDATE_PLAYBOOKS, + parsed_args.ssh_user, + (None if parsed_args.no_workflow else package_update), + skip_tags=parsed_args.skip_tags, + tags=parsed_args.tags, + verbosity=verbosity, + workdir=ansible_dir, + priv_key=key) class UpdateConverge(DeployOvercloud): diff --git a/tripleoclient/v1/overcloud_upgrade.py b/tripleoclient/v1/overcloud_upgrade.py index 5862fb065..121c65853 100644 --- a/tripleoclient/v1/overcloud_upgrade.py +++ b/tripleoclient/v1/overcloud_upgrade.py @@ -108,11 +108,13 @@ class UpgradeRun(command.Command): def get_parser(self, prog_name): parser = super(UpgradeRun, self).get_parser(prog_name) parser.add_argument( - '--limit', action='store', required=True, help=_( - "A string that identifies a single node or comma-separated" - "list of nodes to be upgraded in parallel in this upgrade" - " run invocation. For example: --limit \"compute-0," - " compute-1, compute-5\".") + '--limit', + action='store', + required=True, + help=_("A string that identifies a single node or comma-separated" + "list of nodes the config-download Ansible playbook " + "execution will be limited to. For example: --limit" + " \"compute-0,compute-1,compute-5\".") ) parser.add_argument('--playbook', action="store", @@ -211,23 +213,27 @@ class UpgradeRun(command.Command): key = package_update.get_key(clients) # 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) skip_tags = self._validate_skip_tags(parsed_args.skip_tags) - oooutils.run_update_ansible_action(self.log, clients, stack, - limit_hosts, inventory, playbook, - constants.MAJOR_UPGRADE_PLAYBOOKS, - parsed_args.ssh_user, - (None if parsed_args.no_workflow - else package_update), - parsed_args.tags, - skip_tags, - verbosity, - workdir=ansible_dir, - priv_key=key) + limit_hosts = oooutils.playbook_limit_parse( + limit_nodes=parsed_args.limit) + oooutils.run_update_ansible_action( + self.log, + clients, + stack, + limit_hosts, + inventory, + playbook, + constants.MAJOR_UPGRADE_PLAYBOOKS, + parsed_args.ssh_user, + (None if parsed_args.no_workflow else package_update), + skip_tags, + parsed_args.tags, + verbosity, + workdir=ansible_dir, + priv_key=key) playbooks = (constants.MAJOR_UPGRADE_PLAYBOOKS if playbook == 'all' else playbook) diff --git a/tripleoclient/workflows/deployment.py b/tripleoclient/workflows/deployment.py index eca78f720..10a4ec88e 100644 --- a/tripleoclient/workflows/deployment.py +++ b/tripleoclient/workflows/deployment.py @@ -318,17 +318,23 @@ def enable_ssh_admin(log, clients, plan_name, hosts, ssh_user, ssh_key, print("Enabling ssh admin - COMPLETE.") -def config_download(log, clients, stack, templates, ssh_user, ssh_key, - ssh_network, output_dir, override_ansible_cfg, timeout=600, - verbosity=1, deployment_options={}, - in_flight_validations=False, deployment_timeout=None): +def config_download(log, clients, stack, templates, + ssh_user, ssh_key, ssh_network, + output_dir, override_ansible_cfg, timeout, verbosity=1, + deployment_options={}, + in_flight_validations=False, + deployment_timeout=None, + skip_tags=None, + tags=None, + limit_hosts=None): workflow_client = clients.workflow_engine tripleoclients = clients.tripleoclient - if in_flight_validations: - skip_tags = '' - else: - skip_tags = 'opendev-validation' + if not in_flight_validations: + if skip_tags: + skip_tags = 'opendev-validation,{}'.format(skip_tags) + else: + skip_tags = 'opendev-validation' workflow_input = { 'verbosity': verbosity, @@ -337,7 +343,9 @@ def config_download(log, clients, stack, templates, ssh_user, ssh_key, 'connection_timeout': timeout, 'config_download_timeout': deployment_timeout, 'deployment_options': deployment_options, - 'skip_tags': skip_tags + 'skip_tags': skip_tags, + 'tags': tags, + 'limit_hosts': utils.playbook_limit_parse(limit_hosts) } if output_dir: workflow_input.update(dict(work_dir=output_dir))