Files
python-tripleoclient/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py
Harald Jensås 51ad17ba18 Add support for networks data in Standalone
Standalone does not use any of the composable networks by
default. Deploy Standaloen using /dev/null as network data
so that these resources are not included when creating the
plan.

Undercloud uses only the External network for the external
VIP. Deploy the undercloud using the Undercloud specific
network_data_undercloud.yaml, ensures external_from_pool.yaml
is in the plan.

Related-Bug: #1809313
Depends-On: Ib11a134df93e59947168b40bc71fb1da9172d4ac
Change-Id: I102912851a3b9952daaf7c4d5a34a919f527f805
2019-01-11 02:27:03 +00:00

1002 lines
46 KiB
Python

# Copyright 2015 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import fixtures
import mock
import os
import sys
import tempfile
import yaml
from heatclient import exc as hc_exc
from tripleo_common.image import kolla_builder
from tripleoclient import exceptions
from tripleoclient.tests.v1.test_plugin import TestPluginV1
# Load the plugin init module for the plugin list and show commands
from tripleoclient.v1 import tripleo_deploy
# TODO(sbaker) Remove after a tripleo-common release contains this new function
if not hasattr(kolla_builder, 'container_images_prepare_multi'):
setattr(kolla_builder, 'container_images_prepare_multi', mock.Mock())
class FakePluginV1Client(object):
def __init__(self, **kwargs):
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
class TestDeployUndercloud(TestPluginV1):
def setUp(self):
super(TestDeployUndercloud, self).setUp()
# Get the command object to test
self.cmd = tripleo_deploy.Deploy(self.app, None)
tripleo_deploy.Deploy.heat_pid = mock.MagicMock(
return_value=False)
tripleo_deploy.Deploy.tht_render = '/twd/templates'
tripleo_deploy.Deploy.heat_launch = mock.MagicMock(
side_effect=(lambda *x, **y: None))
self.tc = self.app.client_manager.tripleoclient = mock.MagicMock()
self.orc = self.tc.local_orchestration = mock.MagicMock()
self.orc.stacks.create = mock.MagicMock(
return_value={'stack': {'id': 'foo'}})
python_version = sys.version_info[0]
self.ansible_playbook_cmd = "ansible-playbook-%s" % (python_version)
def test_get_roles_file_path(self):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8'], [])
roles_file = self.cmd._get_roles_file_path(parsed_args)
self.assertEqual(roles_file,
'/usr/share/openstack-tripleo-heat-templates/'
'roles_data_undercloud.yaml')
def test_get_roles_file_path_custom_file(self):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8',
'--templates', '/tmp/thtroot',
'--roles-file', 'foobar.yaml'], [])
roles_file = self.cmd._get_roles_file_path(parsed_args)
self.assertEqual(roles_file, 'foobar.yaml')
def test_get_roles_file_path_custom_templates(self):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8',
'--templates', '/tmp/thtroot'], [])
import pprint
pprint.pprint(parsed_args)
roles_file = self.cmd._get_roles_file_path(parsed_args)
self.assertEqual(roles_file,
'/tmp/thtroot/roles_data_undercloud.yaml')
def test_get_networks_file_path(self):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8'], [])
networks_file = self.cmd._get_networks_file_path(parsed_args)
self.assertEqual('/dev/null', networks_file)
def test_get_networks_file_path_custom_file(self):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8',
'--networks-file', 'foobar.yaml'], [])
networks_file = self.cmd._get_networks_file_path(parsed_args)
self.assertEqual('foobar.yaml', networks_file)
def test_get_networks_file_path_custom_templates(self):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8',
'--templates', '/tmp/thtroot'], [])
networks_file = self.cmd._get_networks_file_path(parsed_args)
self.assertEqual('/dev/null', networks_file)
def test_get_plan_env_file_path(self):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8'], [])
plan_env_file = self.cmd._get_plan_env_file_path(parsed_args)
self.assertEqual(plan_env_file,
'/usr/share/openstack-tripleo-heat-templates/'
'plan-environment.yaml')
def test_get_plan_env_file_path_custom_file(self):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8',
'--templates', '/tmp/thtroot',
'--plan-environment-file',
'foobar.yaml'], [])
plan_env_file = self.cmd._get_plan_env_file_path(parsed_args)
self.assertEqual(plan_env_file, 'foobar.yaml')
def test_get_plan_env_file_path_custom_templates(self):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8',
'--templates', '/tmp/thtroot'], [])
plan_env_file = self.cmd._get_plan_env_file_path(parsed_args)
self.assertEqual(plan_env_file,
'/tmp/thtroot/plan-environment.yaml')
@mock.patch('os.path.exists')
@mock.patch('tripleoclient.utils.fetch_roles_file')
def test_get_primary_role_name(self, mock_data, mock_exists):
parsed_args = mock.Mock()
mock_data.return_value = [
{'name': 'Bar'}, {'name': 'Foo', 'tags': ['primary']}
]
self.assertEqual(
self.cmd._get_primary_role_name(parsed_args.roles_file,
parsed_args.templates),
'Foo')
@mock.patch('tripleoclient.utils.fetch_roles_file', return_value=None)
def test_get_primary_role_name_none_defined(self, mock_data):
parsed_args = self.check_parser(self.cmd, [], [])
self.assertEqual(
self.cmd._get_primary_role_name(parsed_args.roles_file,
parsed_args.templates),
'Controller')
@mock.patch('tripleoclient.utils.fetch_roles_file')
def test_get_primary_role_name_no_primary(self, mock_data):
parsed_args = mock.Mock()
mock_data.return_value = [{'name': 'Bar'}, {'name': 'Foo'}]
self.assertEqual(
self.cmd._get_primary_role_name(parsed_args.roles_file,
parsed_args.templates),
'Bar')
@mock.patch('os.path.exists', side_effect=[True, False])
@mock.patch('shutil.copytree')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_create_working_dirs')
def test_populate_templates_dir(self, mock_workingdirs, mock_copy,
mock_exists):
self.cmd.tht_render = '/foo'
self.cmd._populate_templates_dir('/bar')
mock_workingdirs.assert_called_once()
mock_copy.assert_called_once_with('/bar', '/foo', symlinks=True)
@mock.patch('os.path.exists', return_value=False)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_create_working_dirs')
def test_populate_templates_dir_bad_source(self, mock_workingdirs,
mock_exists):
self.cmd.tht_render = '/foo'
self.assertRaises(exceptions.NotFound,
self.cmd._populate_templates_dir, '/foo')
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('os.chmod')
@mock.patch('os.path.exists')
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('subprocess.check_call', autospec=True)
@mock.patch('tripleo_common.utils.passwords.generate_passwords')
@mock.patch('yaml.safe_dump')
def test_update_passwords_env_init(self, mock_dump, mock_pw, mock_cc,
mock_exists, mock_chmod, mock_user):
pw_dict = {"GeneratedPassword": 123}
mock_pw.return_value = pw_dict
mock_exists.return_value = False
mock_open_context = mock.mock_open()
with mock.patch('six.moves.builtins.open', mock_open_context):
self.cmd._update_passwords_env(self.temp_homedir, 'stack')
mock_open_handle = mock_open_context()
mock_dump.assert_called_once_with({'parameter_defaults': pw_dict},
mock_open_handle,
default_flow_style=False)
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('os.chmod')
@mock.patch('os.path.exists')
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('subprocess.check_call', autospec=True)
@mock.patch('tripleo_common.utils.passwords.generate_passwords')
@mock.patch('yaml.safe_dump')
def test_update_passwords_env(self, mock_dump, mock_pw, mock_cc,
mock_exists, mock_chmod, mock_user):
pw_dict = {"GeneratedPassword": 123, "LegacyPass": "override me"}
pw_conf_path = os.path.join(self.temp_homedir,
'undercloud-passwords.conf')
t_pw_conf_path = os.path.join(
self.temp_homedir, 'tripleo-undercloud-passwords.yaml')
mock_pw.return_value = pw_dict
mock_exists.return_value = True
with open(t_pw_conf_path, 'w') as t_pw:
t_pw.write('parameter_defaults: {ExistingKey: xyz, '
'LegacyPass: pick-me-legacy-tht, '
'RpcPassword: pick-me-rpc}\n')
with open(pw_conf_path, 'w') as t_pw:
t_pw.write('[auth]\nundercloud_db_password = ignore-me-mysql\n'
'undercloud_rabbit_password = ignore-me-rabbit\n'
'undercloud_rpc_password = ignore-me-rpc\n'
'undercloud_legacy_pass = ignore-me-legacy\n')
self.cmd._update_passwords_env(self.temp_homedir,
'stack', upgrade=False,
passwords={'ADefault': 456,
'ExistingKey':
'dontupdate'})
expected_dict = {
'parameter_defaults': {'GeneratedPassword': 123,
'LegacyPass': 'pick-me-legacy-tht',
'RpcPassword': 'pick-me-rpc',
'ExistingKey': 'xyz',
'ADefault': 456}}
mock_dump.assert_called_once_with(expected_dict,
mock.ANY,
default_flow_style=False)
# TODO(bogdando) drop once we have proper oslo.privsep
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('os.chmod')
@mock.patch('os.path.exists')
# TODO(bogdando) drop once we have proper oslo.privsep
@mock.patch('subprocess.check_call', autospec=True)
@mock.patch('tripleo_common.utils.passwords.generate_passwords')
@mock.patch('yaml.safe_dump')
def test_update_passwords_env_upgrade(self, mock_dump, mock_pw, mock_cc,
mock_exists, mock_chmod, mock_user):
pw_dict = {"GeneratedPassword": 123, "LegacyPass": "override me"}
pw_conf_path = os.path.join(self.temp_homedir,
'undercloud-passwords.conf')
t_pw_conf_path = os.path.join(
self.temp_homedir, 'tripleo-undercloud-passwords.yaml')
mock_pw.return_value = pw_dict
def mock_file_exists(file_name):
return not file_name.startswith('/etc/keystone')
mock_exists.side_effect = mock_file_exists
with open(t_pw_conf_path, 'w') as t_pw:
t_pw.write('parameter_defaults: {ExistingKey: xyz, '
'LegacyPass: override-me-legacy, '
'RpcPassword: override-me-rpc}\n')
with open(pw_conf_path, 'w') as t_pw:
t_pw.write('[auth]\nundercloud_db_password = pick-me-mysql\n'
'undercloud_rabbit_password = pick-me-rabbit\n'
'undercloud_rpc_password = pick-me-rpc\n'
'undercloud_legacy_pass = pick-me-legacy-instack\n')
self.cmd._update_passwords_env(self.temp_homedir,
'stack', upgrade=True,
passwords={'ADefault': 456,
'ExistingKey':
'dontupdate'})
expected_dict = {
'parameter_defaults': {'GeneratedPassword': 123,
'ExistingKey': 'xyz',
'MysqlRootPassword': 'pick-me-mysql',
'RpcPassword': 'pick-me-rpc',
'RabbitPassword': 'pick-me-rabbit',
'LegacyPass': 'pick-me-legacy-instack',
'ADefault': 456}}
mock_dump.assert_called_once_with(expected_dict,
mock.ANY,
default_flow_style=False)
@mock.patch('heatclient.common.template_utils.'
'process_environment_and_files', return_value=({}, {}),
autospec=True)
@mock.patch('heatclient.common.template_utils.'
'get_template_contents', return_value=({}, {}),
autospec=True)
@mock.patch('heatclient.common.environment_format.'
'parse', autospec=True, return_value=dict())
@mock.patch('heatclient.common.template_format.'
'parse', autospec=True, return_value=dict())
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_setup_heat_environments', autospec=True)
@mock.patch('tripleo_common.image.kolla_builder.'
'container_images_prepare_multi')
def test_deploy_tripleo_heat_templates_redir(self,
mock_cipm,
mock_setup_heat_envs,
mock_hc_templ_parse,
mock_hc_env_parse,
mock_hc_get_templ_cont,
mock_hc_process):
with tempfile.NamedTemporaryFile(delete=False) as roles_file:
self.addCleanup(os.unlink, roles_file.name)
mock_cipm.return_value = {}
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8',
'--templates', '/tmp/thtroot',
'--roles-file', roles_file.name], [])
mock_setup_heat_envs.return_value = [
'./inside.yaml', '/tmp/thtroot/abs.yaml',
'/tmp/thtroot/puppet/foo.yaml',
'/tmp/thtroot/environments/myenv.yaml',
'/tmp/thtroot42/notouch.yaml',
'../outside.yaml']
self.cmd._deploy_tripleo_heat_templates(self.orc, parsed_args)
mock_hc_process.assert_has_calls([
mock.call(env_path='./inside.yaml'),
mock.call(env_path='/twd/templates/abs.yaml'),
mock.call(env_path='/twd/templates/puppet/foo.yaml'),
mock.call(env_path='/twd/templates/environments/myenv.yaml'),
mock.call(env_path='/tmp/thtroot42/notouch.yaml'),
mock.call(env_path='../outside.yaml')])
@mock.patch('tripleoclient.utils.rel_or_abs_path')
@mock.patch('heatclient.common.template_utils.'
'process_environment_and_files', return_value=({}, {}),
autospec=True)
@mock.patch('heatclient.common.template_utils.'
'get_template_contents', return_value=({}, {}),
autospec=True)
@mock.patch('heatclient.common.environment_format.'
'parse', autospec=True, return_value=dict())
@mock.patch('heatclient.common.template_format.'
'parse', autospec=True, return_value=dict())
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_setup_heat_environments', autospec=True)
@mock.patch('yaml.safe_dump', autospec=True)
@mock.patch('yaml.safe_load', autospec=True)
@mock.patch('six.moves.builtins.open')
@mock.patch('tempfile.NamedTemporaryFile', autospec=True)
@mock.patch('tripleo_common.image.kolla_builder.'
'container_images_prepare_multi')
def test_deploy_tripleo_heat_templates_rewrite(self,
mock_cipm,
mock_temp, mock_open,
mock_yaml_load,
mock_yaml_dump,
mock_setup_heat_envs,
mock_hc_templ_parse,
mock_hc_env_parse,
mock_hc_get_templ_cont,
mock_hc_process,
mock_norm_path):
def hc_process(*args, **kwargs):
if 'abs.yaml' in kwargs['env_path']:
raise hc_exc.CommandError
else:
return ({}, {})
mock_cipm.return_value = {}
mock_hc_process.side_effect = hc_process
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8',
'--templates', '/tmp/thtroot'], [])
rewritten_env = {'resource_registry': {
'OS::Foo::Bar': '/twd/outside.yaml',
'OS::Foo::Baz': '/twd/templates/inside.yaml',
'OS::Foo::Qux': '/twd/templates/abs.yaml',
'OS::Foo::Quux': '/tmp/thtroot42/notouch.yaml',
'OS::Foo::Corge': '/twd/templates/puppet/foo.yaml'
}
}
myenv = {'resource_registry': {
'OS::Foo::Bar': '../outside.yaml',
'OS::Foo::Baz': './inside.yaml',
'OS::Foo::Qux': '/tmp/thtroot/abs.yaml',
'OS::Foo::Quux': '/tmp/thtroot42/notouch.yaml',
'OS::Foo::Corge': '/tmp/thtroot/puppet/foo.yaml'
}
}
mock_yaml_load.return_value = myenv
mock_setup_heat_envs.return_value = [
'./inside.yaml', '/tmp/thtroot/abs.yaml',
'/tmp/thtroot/puppet/foo.yaml',
'/tmp/thtroot/environments/myenv.yaml',
'../outside.yaml']
self.cmd._deploy_tripleo_heat_templates(self.orc, parsed_args)
mock_yaml_dump.assert_has_calls([mock.call(rewritten_env,
default_flow_style=False)])
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_populate_templates_dir')
@mock.patch('tripleoclient.utils.fetch_roles_file')
@mock.patch('tripleoclient.utils.rel_or_abs_path')
@mock.patch('heatclient.common.template_utils.'
'process_environment_and_files', return_value=({}, {}),
autospec=True)
@mock.patch('heatclient.common.template_utils.'
'get_template_contents', return_value=({}, {}),
autospec=True)
@mock.patch('heatclient.common.environment_format.'
'parse', autospec=True, return_value=dict())
@mock.patch('heatclient.common.template_format.'
'parse', autospec=True, return_value=dict())
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_setup_heat_environments', autospec=True)
@mock.patch('yaml.safe_dump', autospec=True)
@mock.patch('yaml.safe_load', autospec=True)
@mock.patch('six.moves.builtins.open')
@mock.patch('tempfile.NamedTemporaryFile', autospec=True)
@mock.patch('tripleo_common.image.kolla_builder.'
'container_images_prepare_multi')
def test_deploy_tripleo_heat_templates_remove(self,
mock_cipm,
mock_temp, mock_open,
mock_yaml_load,
mock_yaml_dump,
mock_setup_heat_envs,
mock_hc_templ_parse,
mock_hc_env_parse,
mock_hc_get_templ_cont,
mock_hc_process,
mock_norm_path,
mock_fetch_roles,
mock_populate):
def hc_process(*args, **kwargs):
if 'myenv.yaml' in kwargs['env_path']:
env = {
'resource_registry': {
'OS::TripleO::Services::Foo': 'OS::Heat::None'}}
return ({}, env)
else:
return ({}, {})
mock_fetch_roles.return_value = [
{'name': 'Bar', 'ServicesDefault': [
'OS::TripleO::Services::Foo', 'OS::TripleO::Services::Bar']},
{'name': 'Foo', 'tags': ['primary']}
]
def set_tht(templates):
self.cmd.tht_render = "tht_from"
mock_populate.side_effect = set_tht
mock_cipm.return_value = {}
mock_hc_process.side_effect = hc_process
parsed_args = self.check_parser(self.cmd,
['--templates', '/tmp/thtroot'], [])
rewritten_role = [
{'name': 'Bar', 'ServicesDefault': ['OS::TripleO::Services::Bar']},
{'name': 'Foo', 'tags': ['primary']}
]
myenv = {'resource_registry': {
'OS::Foo::Bar': '../outside.yaml',
'OS::Foo::Baz': './inside.yaml',
'OS::Foo::Qux': '/tmp/thtroot/abs.yaml',
'OS::Foo::Quux': '/tmp/thtroot42/notouch.yaml',
'OS::Foo::Corge': '/tmp/thtroot/puppet/foo.yaml'
}
}
mock_yaml_load.return_value = myenv
mock_setup_heat_envs.return_value = [
'./inside.yaml', '/tmp/thtroot/abs.yaml',
'/tmp/thtroot/puppet/foo.yaml',
'/tmp/thtroot/environments/myenv.yaml',
'../outside.yaml']
self.cmd._deploy_tripleo_heat_templates(self.orc, parsed_args)
mock_yaml_dump.assert_has_calls([mock.call(rewritten_role)])
@mock.patch('shutil.copy')
@mock.patch('os.path.exists', return_value=False)
def test_normalize_user_templates(self, mock_exists, mock_copy):
user_tht_root = '/userroot'
tht_root = '/thtroot'
env_files = [
'/home/basic.yaml',
'/home/dir/dir.yaml',
'home/relative.yaml',
'file.yaml',
'~/tilde.yaml',
'../../../dots.yaml',
'/userroot/template.yaml',
'/userroot/tht/tht.yaml',
]
expected = [
'/thtroot/basic.yaml',
'/thtroot/dir.yaml',
'/thtroot/relative.yaml',
'/thtroot/file.yaml',
'/thtroot/tilde.yaml',
'/thtroot/dots.yaml',
'/thtroot/template.yaml',
'/thtroot/tht/tht.yaml'
]
results = self.cmd._normalize_user_templates(user_tht_root,
tht_root,
env_files)
self.assertEqual(expected, results)
self.assertEqual(mock_copy.call_count, 6)
@mock.patch('os.path.exists', return_value=True)
def test_normalize_user_templates_exists(self, mock_exists):
user_tht_root = '/userroot'
tht_root = '/thtroot'
env_files = ['/home/basic.yaml']
self.assertRaises(exceptions.DeploymentError,
self.cmd._normalize_user_templates,
user_tht_root,
tht_root,
env_files)
@mock.patch('os.path.exists', return_value=True)
def test_normalize_user_templates_trailing_slash(self, mock_exists):
user_tht_root = '/userroot/'
tht_root = '/thtroot'
env_files = ['/userroot/basic.yaml']
expected = ['/thtroot/basic.yaml']
results = self.cmd._normalize_user_templates(user_tht_root,
tht_root,
env_files)
self.assertEqual(expected, results)
@mock.patch('time.time', return_value=123)
@mock.patch('yaml.safe_load', return_value={}, autospec=True)
@mock.patch('yaml.safe_dump', autospec=True)
@mock.patch('os.path.isfile', return_value=True)
@mock.patch('six.moves.builtins.open')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_process_hieradata_overrides', autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_update_passwords_env', autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_normalize_user_templates', return_value=[], autospec=True)
@mock.patch('tripleoclient.utils.rel_or_abs_path', return_value={},
autospec=True)
@mock.patch('tripleoclient.utils.run_command_and_log', return_value=0,
autospec=True)
def test_setup_heat_environments_dropin(
self, mock_run, mock_paths, mock_norm, mock_update_pass_env,
mock_process_hiera, mock_open, mock_os, mock_yaml_dump,
mock_yaml_load, mock_time):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8',
'--templates', 'tht_from',
'--output-dir', 'tht_to'], [])
dropin = 'tht_from/standalone-stack-vstate-dropin.yaml'
self.cmd.output_dir = 'tht_to'
self.cmd.tht_render = 'tht_from'
self.cmd.stack_action = 'UPDATE'
environment = self.cmd._setup_heat_environments(
parsed_args.roles_file, parsed_args.networks_file, parsed_args)
self.assertIn(dropin, environment)
mock_open.assert_has_calls([mock.call(dropin, 'w')])
# unpack the dump yaml calls to verify if the produced stack update
# dropin matches our expectations
found_dropin = False
found_identifier = False
for call in mock_yaml_dump.call_args_list:
args, kwargs = call
for a in args:
if isinstance(a, mock.mock.MagicMock):
continue
if a.get('parameter_defaults', {}).get('StackAction', None):
self.assertTrue(
a['parameter_defaults']['StackAction'] == 'UPDATE')
found_dropin = True
if a.get('parameter_defaults', {}).get('DeployIdentifier',
None):
self.assertTrue(
a['parameter_defaults']['DeployIdentifier'] == 123)
found_identifier = True
self.assertTrue(found_dropin)
self.assertTrue(found_identifier)
@mock.patch('heatclient.common.template_utils.'
'process_environment_and_files', return_value=({}, {}),
autospec=True)
@mock.patch('heatclient.common.template_utils.'
'get_template_contents', return_value=({}, {}),
autospec=True)
@mock.patch('tripleoclient.utils.'
'process_multiple_environments', autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_process_hieradata_overrides', return_value='hiera_or.yaml',
autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_update_passwords_env', autospec=True)
@mock.patch('tripleoclient.utils.'
'run_command_and_log', autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_get_primary_role_name', autospec=True)
def test_setup_heat_environments_default_plan_env(
self, mock_prim, mock_run, mock_update_pass_env,
mock_process_hiera, mock_process_multiple_environments,
mock_hc_get_templ_cont, mock_hc_process):
tmpdir = self.useFixture(fixtures.TempDir()).path
tht_from = os.path.join(tmpdir, 'tht-from')
os.mkdir(tht_from)
plan_env_path = os.path.join(tht_from, 'plan-environment.yaml')
with open(plan_env_path, mode='w') as plan_file:
yaml.dump({'environments': [{'path': 'env.yaml'}]}, plan_file)
self.assertTrue(os.path.exists(plan_env_path))
self._setup_heat_environments(tmpdir, tht_from, plan_env_path,
mock_update_pass_env, mock_run)
@mock.patch('heatclient.common.template_utils.'
'process_environment_and_files', return_value=({}, {}),
autospec=True)
@mock.patch('heatclient.common.template_utils.'
'get_template_contents', return_value=({}, {}),
autospec=True)
@mock.patch('tripleoclient.utils.'
'process_multiple_environments', autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_process_hieradata_overrides', return_value='hiera_or.yaml',
autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_update_passwords_env', autospec=True)
@mock.patch('tripleoclient.utils.'
'run_command_and_log', autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_get_primary_role_name', autospec=True)
def test_setup_heat_environments_non_default_plan_env(
self, mock_prim, mock_run, mock_update_pass_env,
mock_process_hiera, mock_process_multiple_environments,
mock_hc_get_templ_cont, mock_hc_process):
tmpdir = self.useFixture(fixtures.TempDir()).path
tht_from = os.path.join(tmpdir, 'tht-from')
os.mkdir(tht_from)
default_plan_env_path = os.path.join(tht_from, 'plan-environment.yaml')
with open(default_plan_env_path, mode='w') as plan_file:
yaml.dump({'environments': [{'path': 'env.yaml'}]}, plan_file)
plan_env_path = os.path.join(tmpdir, 'plan-environment.yaml')
with open(plan_env_path, mode='w') as plan_file:
yaml.dump({'environments': [{'path': 'notenv.yaml'}]}, plan_file)
self.assertTrue(os.path.exists(plan_env_path))
with open(os.path.join(tht_from, 'notenv.yaml'),
mode='w') as env_file:
yaml.dump({}, env_file)
cmd_extra = ['-p', plan_env_path]
self._setup_heat_environments(tmpdir, tht_from, plan_env_path,
mock_update_pass_env, mock_run,
cmd_extra, 'notenv.yaml')
def _setup_heat_environments(self, tmpdir, tht_from, plan_env_path,
mock_update_pass_env, mock_run,
extra_cmd=None, plan_env_env=None):
cmd_extra = extra_cmd or []
plan_env_env_name = plan_env_env or 'env.yaml'
tht_outside = os.path.join(tmpdir, 'tht-outside')
os.mkdir(tht_outside)
tht_to = os.path.join(tmpdir, 'tht-to')
os.mkdir(tht_to)
with open(os.path.join(tht_from, 'env.yaml'),
mode='w') as env_file:
yaml.dump({}, env_file)
with open(os.path.join(tht_from, 'foo.yaml'),
mode='w') as env_file:
yaml.dump({}, env_file)
with open(os.path.join(tht_outside, 'outside.yaml'),
mode='w') as env_file:
yaml.dump({}, env_file)
tht_render = os.path.join(tht_to, 'tripleo-heat-installer-templates')
mock_update_pass_env.return_value = os.path.join(
tht_render, 'passwords.yaml')
mock_run.return_value = 0
original_abs = os.path.abspath
# Stub abspath for default plan and envs to return the tht_render base
def abs_path_stub(*args, **kwargs):
if 'plan-environment.yaml' in args:
return plan_env_path
elif 'notenv.yaml' in args:
return os.path.join(tht_render, 'notenv.yaml')
elif 'env.yaml' in args:
return os.path.join(tht_render, 'env.yaml')
else:
return original_abs(*args, **kwargs)
# logic handled in _standalone_deploy
self.cmd.output_dir = tht_to
# Note we don't create tht_render as _populate_templates_dir creates it
self.cmd.tht_render = tht_render
self.cmd._populate_templates_dir(tht_from)
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1/8',
'--templates', tht_from,
'--output-dir', tht_to,
'--hieradata-override',
'legacy.yaml',
'-e',
os.path.join(tht_from, 'foo.yaml'),
'-e',
os.path.join(tht_outside,
'outside.yaml'),
] + cmd_extra, [])
expected_env = [
os.path.join(tht_render, plan_env_env_name),
os.path.join(tht_render, 'passwords.yaml'),
os.path.join(tht_render,
'environments/config-download-environment.yaml'),
os.path.join(tht_render,
'environments/deployed-server-noop-ctlplane.yaml'),
os.path.join(tht_render,
'tripleoclient-hosts-portmaps.yaml'),
'hiera_or.yaml',
os.path.join(tht_render, 'standalone-stack-vstate-dropin.yaml'),
os.path.join(tht_render, 'foo.yaml'),
os.path.join(tht_render, 'outside.yaml')]
with mock.patch('os.path.abspath', side_effect=abs_path_stub):
with mock.patch('os.path.isfile'):
environment = self.cmd._setup_heat_environments(
parsed_args.roles_file, parsed_args.networks_file,
parsed_args)
self.assertEqual(expected_env, environment)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_create_working_dirs', autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.TripleoInventory',
autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_launch_heat', autospec=True)
@mock.patch('tripleo_common.utils.config.Config',
autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.sys.stdout.flush')
@mock.patch('os.path.join', return_value='/twd/inventory.yaml')
def test_download_ansible_playbooks(self, mock_join, mock_flush,
mock_stack_config, mock_launch_heat,
mock_importInv, createdir_mock):
fake_output_dir = '/twd'
extra_vars = {'Undercloud': {
'ansible_connection': 'local',
'ansible_python_interpreter': sys.executable}}
mock_inventory = mock.Mock()
mock_importInv.return_value = mock_inventory
self.cmd.output_dir = fake_output_dir
self.cmd._download_ansible_playbooks(mock_launch_heat,
'undercloud',
'Undercloud')
self.assertEqual(mock_flush.call_count, 2)
mock_inventory.write_static_inventory.assert_called_once_with(
fake_output_dir + '/inventory.yaml', extra_vars)
@mock.patch('tripleoclient.utils.'
'run_command_and_log', autospec=True)
@mock.patch('os.chdir')
@mock.patch('os.execvp')
def test_launch_ansible_deploy(self, mock_execvp, mock_chdir, mock_run):
self.cmd._launch_ansible_deploy('/tmp')
mock_chdir.assert_called_once()
mock_run.assert_called_once_with(self.cmd.log, [
self.ansible_playbook_cmd, '-i', '/tmp/inventory.yaml',
'deploy_steps_playbook.yaml'])
@mock.patch('tripleo_common.image.kolla_builder.'
'container_images_prepare_multi')
def test_prepare_container_images(self, mock_cipm):
env = {'parameter_defaults': {}}
mock_cipm.return_value = {'FooImage': 'foo/bar:baz'}
self.cmd._prepare_container_images(env, [{'name': 'Compute'}])
mock_cipm.assert_called_once_with(
env,
[{'name': 'Compute'}],
dry_run=True,
)
self.assertEqual(
{
'parameter_defaults': {
'FooImage': 'foo/bar:baz'
}
},
env
)
@mock.patch('tripleo_common.actions.ansible.'
'write_default_ansible_cfg')
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('os.chmod')
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('subprocess.check_call', autospec=True)
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('os.mkdir')
@mock.patch('six.moves.builtins.open')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_populate_templates_dir')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_create_install_artifact', return_value='/tmp/foo.tar.bzip2')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_launch_ansible_deploy', return_value=0)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_cleanup_working_dirs')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_create_working_dirs')
@mock.patch('tripleoclient.utils.wait_api_port_ready',
autospec=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_deploy_tripleo_heat_templates', autospec=True,
return_value='undercloud, 0')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_download_ansible_playbooks', autospec=True,
return_value='/foo')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_launch_heat')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_kill_heat')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_configure_puppet')
@mock.patch('os.geteuid', return_value=0)
@mock.patch('os.environ', return_value='CREATE_COMPLETE')
@mock.patch('tripleoclient.v1.tripleo_deploy.'
'event_utils.poll_for_events',
return_value=('CREATE_COMPLETE', 0))
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_set_default_plan')
def test_take_action_standalone(self, mock_def_plan, mock_poll,
mock_environ, mock_geteuid, mock_puppet,
mock_killheat, mock_launchheat,
mock_download, mock_tht,
mock_wait_for_port, mock_createdirs,
mock_cleanupdirs, mock_launchansible,
mock_tarball, mock_templates_dir,
mock_open, mock_os, mock_user, mock_cc,
mock_chmod, mock_ac):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone-role', 'Undercloud',
# TODO(cjeanner) drop once we have
# proper oslo.privsep
'--deployment-user', 'stack',
'-e', '/tmp/thtroot/puppet/foo.yaml',
'-e', '/tmp/thtroot//docker/bar.yaml',
'-e', '/tmp/thtroot42/notouch.yaml',
'-e', '~/custom.yaml',
'-e', 'something.yaml',
'-e', '../../../outside.yaml',
'--standalone'], [])
fake_orchestration = mock_launchheat(parsed_args)
self.cmd.take_action(parsed_args)
mock_createdirs.assert_called_once()
mock_puppet.assert_called_once()
mock_launchheat.assert_called_with(parsed_args)
mock_tht.assert_called_once_with(self.cmd, fake_orchestration,
parsed_args)
mock_download.assert_called_with(self.cmd, fake_orchestration,
'undercloud', 'Undercloud',
sys.executable)
mock_launchansible.assert_called_once()
mock_tarball.assert_called_once()
mock_cleanupdirs.assert_called_once()
self.assertEqual(mock_killheat.call_count, 2)
def test_take_action(self):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my'], [])
self.assertRaises(exceptions.DeploymentError,
self.cmd.take_action, parsed_args)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy._standalone_deploy',
return_value=1)
def test_take_action_failure(self, mock_deploy):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone'], [])
self.assertRaises(exceptions.DeploymentError,
self.cmd.take_action, parsed_args)
@mock.patch('os.path.isfile', return_value=False)
def test_set_stack_action_default_create(self, mock_isfile):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone'], [])
self.cmd._set_stack_action(parsed_args)
self.assertEqual('CREATE', self.cmd.stack_action)
@mock.patch('os.path.isfile', return_value=True)
def test_set_stack_action_default_update(self, mock_isfile):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone'], [])
self.cmd._set_stack_action(parsed_args)
self.assertEqual('UPDATE', self.cmd.stack_action)
@mock.patch('os.path.isfile', return_value=False)
def test_set_stack_action_force_update(self, mock_isfile):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone',
'--force-stack-update'], [])
self.cmd._set_stack_action(parsed_args)
self.assertEqual('UPDATE', self.cmd.stack_action)
@mock.patch('os.path.isfile', return_value=True)
def test_set_stack_action_force_create(self, mock_isfile):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone',
'--force-stack-create'], [])
self.cmd._set_stack_action(parsed_args)
self.assertEqual('CREATE', self.cmd.stack_action)
@mock.patch('os.path.isfile', return_value=True)
def test_set_stack_action_mutually_exclusive(self, mock_isfile):
self.assertRaises(
SystemExit,
self.check_parser,
self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
'--stack', 'undercloud',
'--output-dir', '/my',
'--standalone',
'--force-stack-create',
'--force-stack-update'], [])