diff --git a/elements/puppet-stack-config/puppet-stack-config.yaml.template b/elements/puppet-stack-config/puppet-stack-config.yaml.template index 2e9fb5a54..0cbef958f 100644 --- a/elements/puppet-stack-config/puppet-stack-config.yaml.template +++ b/elements/puppet-stack-config/puppet-stack-config.yaml.template @@ -135,7 +135,7 @@ heat::engine::reauthentication_auth_method: 'trusts' heat::engine::trusts_delegated_roles: [] heat::engine::auth_encryption_key: {{UNDERCLOUD_HEAT_ENCRYPTION_KEY}} heat::engine::max_resources_per_stack: -1 -heat::engine::convergence_engine: false +heat::engine::convergence_engine: true # NOTE(trown): We need to give heat engine more workers because we are throwing huge # nested stacks at it for the deploy. By not setting this, we get the heat default, # which is max(#CPUs,4). diff --git a/instack_undercloud/tests/test_undercloud.py b/instack_undercloud/tests/test_undercloud.py index bbbda0176..355a22a65 100644 --- a/instack_undercloud/tests/test_undercloud.py +++ b/instack_undercloud/tests/test_undercloud.py @@ -688,6 +688,8 @@ class TestConfigureSshKeys(base.BaseTestCase): class TestPostConfig(base.BaseTestCase): + @mock.patch('os_client_config.make_client') + @mock.patch('instack_undercloud.undercloud._migrate_to_convergence') @mock.patch('instack_undercloud.undercloud._ensure_node_resource_classes') @mock.patch('instack_undercloud.undercloud._member_role_exists') @mock.patch('ironicclient.client.get_client', autospec=True) @@ -704,7 +706,8 @@ class TestPostConfig(base.BaseTestCase): mock_configure_ssh_keys, mock_get_auth_values, mock_copy_stackrc, mock_delete, mock_mistral_client, mock_swift_client, mock_nova_client, mock_ir_client, - mock_member_role_exists, mock_resource_classes): + mock_member_role_exists, mock_resource_classes, + mock_migrate_to_convergence, mock_make_client): instack_env = { 'UNDERCLOUD_ENDPOINT_MISTRAL_PUBLIC': 'http://192.168.24.1:8989/v2', @@ -725,8 +728,10 @@ class TestPostConfig(base.BaseTestCase): flavors[0].name = 'baremetal' flavors[1].name = 'ceph-storage' mock_instance_nova.flavors.list.return_value = flavors + mock_heat = mock.Mock() + mock_make_client.return_value = mock_heat - undercloud._post_config(instack_env) + undercloud._post_config(instack_env, True) mock_nova_client.assert_called_with( 2, 'aturing', '3nigma', project_name='hut8', auth_url='http://bletchley:5000/') @@ -746,6 +751,7 @@ class TestPostConfig(base.BaseTestCase): mock_resource_classes.assert_called_once_with(mock_instance_ironic) mock_post_config_mistral.assert_called_once_with( instack_env, mock_instance_mistral, mock_instance_swift) + mock_migrate_to_convergence.assert_called_once_with(mock_heat) @mock.patch('instack_undercloud.undercloud._get_auth_values') @mock.patch('instack_undercloud.undercloud._get_session') @@ -1039,6 +1045,28 @@ class TestPostConfig(base.BaseTestCase): '1', [{'path': '/resource_class', 'op': 'add', 'value': 'baremetal'}]) + @mock.patch('instack_undercloud.undercloud._run_command') + def test_migrate_to_convergence(self, mock_run_command): + stacks = [mock.Mock(id='1'), mock.Mock(id='2')] + mock_heat = mock.Mock() + mock_heat.stacks.list.return_value = stacks + undercloud._migrate_to_convergence(mock_heat) + self.assertEqual([mock.call(['sudo', 'heat-manage', + 'migrate_convergence_1', '1'], + name='heat-manage'), + mock.call(['sudo', 'heat-manage', + 'migrate_convergence_1', '2'], + name='heat-manage')], + mock_run_command.mock_calls) + + @mock.patch('instack_undercloud.undercloud._run_command') + def test_migrate_to_convergence_no_stacks(self, mock_run_command): + stacks = [] + mock_heat = mock.Mock() + mock_heat.stacks.list.return_value = stacks + undercloud._migrate_to_convergence(mock_heat) + mock_run_command.assert_not_called() + @mock.patch('instack_undercloud.undercloud._extract_from_stackrc') @mock.patch('instack_undercloud.undercloud._run_command') def test_get_auth_values(self, mock_run, mock_extract): diff --git a/instack_undercloud/undercloud.py b/instack_undercloud/undercloud.py index 37de7cb58..7dbba30d5 100644 --- a/instack_undercloud/undercloud.py +++ b/instack_undercloud/undercloud.py @@ -41,6 +41,7 @@ from mistralclient.api import client as mistralclient import novaclient as nc from novaclient import client as novaclient from novaclient import exceptions +import os_client_config from oslo_config import cfg from oslo_utils import netutils import psutil @@ -1660,7 +1661,22 @@ def _post_config_mistral(instack_env, mistral, swift): _prepare_ssh_environment(mistral) -def _post_config(instack_env): +def _migrate_to_convergence(heat): + """Migrate all active stacks to use the convergence engine + + This appears to be a noop if the stack has already been migrated, so it + should be safe to run unconditionally. + + :param heat: A heat client instance + """ + for stack in heat.stacks.list(): + LOG.info('Migrating stack "%s" to convergence engine', stack.id) + args = ['sudo', 'heat-manage', 'migrate_convergence_1', stack.id] + _run_command(args, name='heat-manage') + LOG.info('Finished migrating stack "%s"', stack.id) + + +def _post_config(instack_env, upgrade): _copy_stackrc() user, password, project, auth_url = _get_auth_values() sess = _get_session() @@ -1699,6 +1715,19 @@ def _post_config(instack_env): _post_config_mistral(instack_env, mistral, swift) _member_role_exists() + # NOTE(bnemec): We are turning on the convergence engine in Queens, so we + # need to migrate all existing stacks on upgrade. This functionality can + # be removed in Rocky as all stacks should have been migrated by then. + if upgrade: + heat = os_client_config.make_client('orchestration', + auth_url=auth_url, + username=user, + password=password, + project_name=project, + project_domain_name='Default', + user_domain_name='Default') + _migrate_to_convergence(heat) + def _handle_upgrade_fact(upgrade=False): """Create an upgrade fact for use in puppet @@ -1779,7 +1808,7 @@ def install(instack_root, upgrade=False): _handle_upgrade_fact(upgrade) _run_instack(instack_env) _run_orc(instack_env) - _post_config(instack_env) + _post_config(instack_env, upgrade) _run_command(['sudo', 'rm', '-f', '/tmp/svc-map-services'], None, 'rm') if upgrade and CONF.enable_validations: # Run post-upgrade validations mistral_url = instack_env['UNDERCLOUD_ENDPOINT_MISTRAL_PUBLIC'] diff --git a/releasenotes/notes/heat-convergence-fea9886b21ff02a5.yaml b/releasenotes/notes/heat-convergence-fea9886b21ff02a5.yaml new file mode 100644 index 000000000..10cf1c6e6 --- /dev/null +++ b/releasenotes/notes/heat-convergence-fea9886b21ff02a5.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + In this release the Heat convergence engine has been enabled on the + undercloud, which allows multiple stack updates to be run at the same time. +upgrade: + - | + On upgrade to this version, any existing overcloud stacks will be converted + to use the Heat convergence engine. The only user-visible impact of this + should be the ability to use Heat convergence features. diff --git a/requirements.txt b/requirements.txt index 0e3854d55..db75c698d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,3 +15,4 @@ netifaces>=0.10.4 # MIT pystache>=0.5.4 # MIT os-refresh-config>=0.1.10 # Apache-2.0 os-apply-config>=0.1.31 # Apache-2.0 +os-client-config>=1.28.0 # Apache-2.0