Replace ansible shell with python runner

This change replaces all of the ansible shell commands with the
python library, ansible-runner. This library is supported by
upstream ansible, is approved by the openstack foundation, is
supported in global requirements, and provides a better, more
programatic interface into running ansible playbooks.

All tests that interacted with the old shell commands have been
updated to now test using the library.

Change-Id: I8db50da826e2fbc074f4e7986d6fd00f6d488648
Signed-off-by: Kevin Carter <kecarter@redhat.com>
This commit is contained in:
Kevin Carter 2019-07-19 21:45:52 -05:00
parent 29b00170b3
commit bcc9c66747
No known key found for this signature in database
GPG Key ID: CE94BD890A47B20A
25 changed files with 789 additions and 926 deletions

View File

@ -1,5 +1,6 @@
alembic==0.8.10
amqp==2.1.1
ansible-runner===1.4.4
aodhclient==0.9.0
appdirs==1.3.0
asn1crypto==0.23.0

View File

@ -19,3 +19,4 @@ websocket-client>=0.44.0 # LGPLv2+
tripleo-common>=11.3.1 # Apache-2.0
cryptography>=2.1 # BSD/Apache-2.0
futures>=3.0.0;python_version=='2.7' or python_version=='2.6' # BSD
ansible-runner>=1.4.4 # Apache 2.0

View File

@ -102,16 +102,33 @@ CTLPLANE_INSPECTION_IPRANGE_DEFAULT = '192.168.24.100,192.168.24.120'
CTLPLANE_GATEWAY_DEFAULT = '192.168.24.1'
CTLPLANE_DNS_NAMESERVERS_DEFAULT = []
# Ansible parameters used for the actions being
# executed during tripleo deploy/upgrade.
# Ansible parameters used for the actions being executed during tripleo
# deploy/upgrade. Used as kwargs in the `utils.run_ansible_playbook`
# function. A playbook entry is either a string representing the name of
# one the playbook or a list of playbooks to execute. The lookup
# will search for the playbook in the work directory path.
DEPLOY_ANSIBLE_ACTIONS = {
'deploy': 'deploy_steps_playbook.yaml',
'upgrade': 'upgrade_steps_playbook.yaml --skip-tags '
'validation',
'post-upgrade': 'post_upgrade_steps_playbook.yaml '
'--skip-tags validation',
'online-upgrade': 'external_upgrade_steps_playbook.yaml '
'--tags online_upgrade',
'deploy': {
'playbook': 'deploy_steps_playbook.yaml'
},
'upgrade': {
'playbook': 'upgrade_steps_playbook.yaml',
'skip_tags': 'validation'
},
'post-upgrade': {
'playbook': 'post_upgrade_steps_playbook.yaml',
'skip_tags': 'validation'
},
'online-upgrade': {
'playbook': 'external_upgrade_steps_playbook.yaml',
'tags': 'online_upgrade'
},
'preflight-deploy': {
'playbook': 'undercloud-disk-space.yaml'
},
'preflight-upgrade': {
'playbook': 'undercloud-disk-space-pre-upgrade.yaml'
},
}
# Key-value pair of deprecated service and its warning message

View File

@ -80,3 +80,8 @@ class FakeClientWrapper(object):
def messaging_websocket(self):
return self.ws
def fake_ansible_runner_run_return(rc=0):
return 'Test Status', rc

View File

@ -38,9 +38,13 @@ import yaml
from tripleoclient import exceptions
from tripleoclient import utils
from tripleoclient.tests import fakes
from six.moves.configparser import ConfigParser
from six.moves.urllib import error as url_error
from ansible_runner import Runner
class TestRunAnsiblePlaybook(TestCase):
def setUp(self):
@ -48,329 +52,130 @@ class TestRunAnsiblePlaybook(TestCase):
self.addCleanup(self.unlink_patch.stop)
self.unlink_patch.start()
self.mock_log = mock.Mock('logging.getLogger')
python_version = sys.version_info[0]
self.ansible_playbook_cmd = "ansible-playbook-%s" % (python_version)
self.ansible_playbook_cmd = "ansible-playbook"
@mock.patch('os.path.exists', return_value=False)
@mock.patch('tripleoclient.utils.run_command_and_log')
def test_no_playbook(self, mock_run, mock_exists):
self.assertRaises(RuntimeError,
self.assertRaises(
RuntimeError,
utils.run_ansible_playbook,
self.mock_log,
'/tmp',
'non-existing.yaml',
'localhost,'
'localhost,',
'/tmp'
)
mock_exists.assert_called_once_with('/tmp/non-existing.yaml')
mock_exists.assert_called_with('/tmp/non-existing.yaml')
mock_run.assert_not_called()
@mock.patch('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
@mock.patch('os.path.exists', return_value=True)
@mock.patch('tripleoclient.utils.run_command_and_log')
def test_subprocess_error(self, mock_run, mock_exists, mock_mkstemp):
mock_process = mock.Mock()
mock_process.returncode = 1
mock_process.stdout.read.side_effect = ["Error\n"]
mock_run.return_value = mock_process
env = os.environ.copy()
env['ANSIBLE_LIBRARY'] = \
('/root/.ansible/plugins/modules:'
'/usr/share/ansible/plugins/modules:'
'/usr/share/openstack-tripleo-validations/library')
env['ANSIBLE_LOOKUP_PLUGINS'] = \
('root/.ansible/plugins/lookup:'
'/usr/share/ansible/plugins/lookup:'
'/usr/share/openstack-tripleo-validations/lookup_plugins')
env['ANSIBLE_CALLBACK_PLUGINS'] = \
('~/.ansible/plugins/callback:'
'/usr/share/ansible/plugins/callback:'
'/usr/share/openstack-tripleo-validations/callback_plugins')
env['ANSIBLE_ROLES_PATH'] = \
('/root/.ansible/roles:'
'/usr/share/ansible/roles:'
'/etc/ansible/roles:'
'/usr/share/openstack-tripleo-validations/roles')
env['ANSIBLE_CONFIG'] = '/tmp/fooBar.cfg'
env['ANSIBLE_HOST_KEY_CHECKING'] = 'False'
env['ANSIBLE_LOG_PATH'] = '/tmp/ansible.log'
env['TRIPLEO_PLAN_NAME'] = 'overcloud'
self.assertRaises(RuntimeError,
@mock.patch('os.makedirs')
@mock.patch.object(
Runner,
'run',
return_value=fakes.fake_ansible_runner_run_return(rc=1)
)
def test_subprocess_error(self, mock_run, mock_mkdirs, mock_exists,
mock_mkstemp):
self.assertRaises(
RuntimeError,
utils.run_ansible_playbook,
self.mock_log,
'/tmp',
'existing.yaml',
'localhost,'
)
mock_run.assert_called_once_with(self.mock_log,
[self.ansible_playbook_cmd,
'-u', 'root',
'-i', 'localhost,', '-v',
'-c', 'smart',
'/tmp/existing.yaml'],
env=env, retcode_only=False)
@mock.patch('os.path.isabs')
@mock.patch('os.path.exists', return_value=False)
@mock.patch('tripleoclient.utils.run_command_and_log')
def test_non_existing_config(self, mock_run, mock_exists, mock_isabs):
self.assertRaises(RuntimeError,
utils.run_ansible_playbook, self.mock_log,
'/tmp', 'existing.yaml', 'localhost,',
'/home/foo', '/tmp/foo.cfg'
)
mock_exists.assert_called_with('/tmp/foo.cfg')
mock_isabs.assert_called_with('/tmp/foo.cfg')
mock_run.assert_not_called()
@mock.patch('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
@mock.patch('os.path.exists', return_value=True)
@mock.patch('tripleoclient.utils.run_command_and_log')
def test_run_success_default(self, mock_run, mock_exists, mock_mkstemp):
mock_process = mock.Mock()
mock_process.returncode = 0
mock_run.return_value = mock_process
retcode, output = utils.run_ansible_playbook(
self.mock_log, '/tmp', 'existing.yaml', 'localhost,')
self.assertEqual(retcode, 0)
mock_exists.assert_called_once_with('/tmp/existing.yaml')
env = os.environ.copy()
env['ANSIBLE_LIBRARY'] = \
('/root/.ansible/plugins/modules:'
'/usr/share/ansible/plugins/modules:'
'/usr/share/openstack-tripleo-validations/library')
env['ANSIBLE_LOOKUP_PLUGINS'] = \
('root/.ansible/plugins/lookup:'
'/usr/share/ansible/plugins/lookup:'
'/usr/share/openstack-tripleo-validations/lookup_plugins')
env['ANSIBLE_CALLBACK_PLUGINS'] = \
('~/.ansible/plugins/callback:'
'/usr/share/ansible/plugins/callback:'
'/usr/share/openstack-tripleo-validations/callback_plugins')
env['ANSIBLE_ROLES_PATH'] = \
('/root/.ansible/roles:'
'/usr/share/ansible/roles:'
'/etc/ansible/roles:'
'/usr/share/openstack-tripleo-validations/roles')
env['ANSIBLE_CONFIG'] = '/tmp/fooBar.cfg'
env['ANSIBLE_HOST_KEY_CHECKING'] = 'False'
env['ANSIBLE_LOG_PATH'] = '/tmp/ansible.log'
env['TRIPLEO_PLAN_NAME'] = 'overcloud'
mock_run.assert_called_once_with(self.mock_log,
[self.ansible_playbook_cmd,
'-u', 'root',
'-i', 'localhost,', '-v',
'-c', 'smart',
'/tmp/existing.yaml'],
env=env, retcode_only=False)
@mock.patch('os.path.isabs')
@mock.patch('os.path.exists', return_value=True)
@mock.patch('tripleoclient.utils.run_command_and_log')
def test_run_success_ansible_cfg(self, mock_run, mock_exists, mock_isabs):
mock_process = mock.Mock()
mock_process.returncode = 0
mock_run.return_value = mock_process
retcode, output = utils.run_ansible_playbook(
self.mock_log,
'/tmp',
'existing.yaml',
'localhost,',
ansible_config='/tmp/foo.cfg')
self.assertEqual(retcode, 0)
mock_isabs.assert_called_once_with('/tmp/foo.cfg')
exist_calls = [mock.call('/tmp/foo.cfg'),
mock.call('/tmp/existing.yaml')]
mock_exists.assert_has_calls(exist_calls, any_order=False)
env = os.environ.copy()
env['ANSIBLE_LIBRARY'] = \
('/root/.ansible/plugins/modules:'
'/usr/share/ansible/plugins/modules:'
'/usr/share/openstack-tripleo-validations/library')
env['ANSIBLE_LOOKUP_PLUGINS'] = \
('root/.ansible/plugins/lookup:'
'/usr/share/ansible/plugins/lookup:'
'/usr/share/openstack-tripleo-validations/lookup_plugins')
env['ANSIBLE_CALLBACK_PLUGINS'] = \
('~/.ansible/plugins/callback:'
'/usr/share/ansible/plugins/callback:'
'/usr/share/openstack-tripleo-validations/callback_plugins')
env['ANSIBLE_ROLES_PATH'] = \
('/root/.ansible/roles:'
'/usr/share/ansible/roles:'
'/etc/ansible/roles:'
'/usr/share/openstack-tripleo-validations/roles')
env['ANSIBLE_CONFIG'] = '/tmp/foo.cfg'
env['ANSIBLE_HOST_KEY_CHECKING'] = 'False'
env['ANSIBLE_LOG_PATH'] = '/tmp/ansible.log'
env['TRIPLEO_PLAN_NAME'] = 'overcloud'
mock_run.assert_called_once_with(self.mock_log,
[self.ansible_playbook_cmd,
'-u', 'root',
'-i', 'localhost,', '-v',
'-c', 'smart',
'/tmp/existing.yaml'],
env=env, retcode_only=False)
'/tmp'
)
@mock.patch('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
@mock.patch('os.path.exists', return_value=True)
@mock.patch('tripleoclient.utils.run_command_and_log')
def test_run_success_connection_local(self, mock_run, mock_exists,
mok_mkstemp):
mock_process = mock.Mock()
mock_process.returncode = 0
mock_run.return_value = mock_process
@mock.patch('os.makedirs')
@mock.patch.object(
Runner,
'run',
return_value=fakes.fake_ansible_runner_run_return()
)
def test_run_success_default(self, mock_run, mock_mkdirs, mock_exists,
mock_mkstemp):
retcode, output = utils.run_ansible_playbook(
self.mock_log,
'/tmp',
'existing.yaml',
'localhost,',
connection='local')
playbook='existing.yaml',
inventory='localhost,',
workdir='/tmp'
)
self.assertEqual(retcode, 0)
mock_exists.assert_called_once_with('/tmp/existing.yaml')
env = os.environ.copy()
env['ANSIBLE_LIBRARY'] = \
('/root/.ansible/plugins/modules:'
'/usr/share/ansible/plugins/modules:'
'/usr/share/openstack-tripleo-validations/library')
env['ANSIBLE_LOOKUP_PLUGINS'] = \
('root/.ansible/plugins/lookup:'
'/usr/share/ansible/plugins/lookup:'
'/usr/share/openstack-tripleo-validations/lookup_plugins')
env['ANSIBLE_CALLBACK_PLUGINS'] = \
('~/.ansible/plugins/callback:'
'/usr/share/ansible/plugins/callback:'
'/usr/share/openstack-tripleo-validations/callback_plugins')
env['ANSIBLE_ROLES_PATH'] = \
('/root/.ansible/roles:'
'/usr/share/ansible/roles:'
'/etc/ansible/roles:'
'/usr/share/openstack-tripleo-validations/roles')
env['ANSIBLE_CONFIG'] = '/tmp/fooBar.cfg'
env['ANSIBLE_HOST_KEY_CHECKING'] = 'False'
env['ANSIBLE_LOG_PATH'] = '/tmp/ansible.log'
env['TRIPLEO_PLAN_NAME'] = 'overcloud'
mock_run.assert_called_once_with(self.mock_log,
[self.ansible_playbook_cmd,
'-u', 'root',
'-i', 'localhost,', '-v',
'-c', 'local',
'/tmp/existing.yaml'],
env=env, retcode_only=False)
@mock.patch('os.path.exists', return_value=True)
@mock.patch('os.makedirs')
@mock.patch.object(
Runner,
'run',
return_value=fakes.fake_ansible_runner_run_return()
)
def test_run_success_ansible_cfg(self, mock_run, mock_mkdirs, mock_exists):
retcode, output = utils.run_ansible_playbook(
playbook='existing.yaml',
inventory='localhost,',
workdir='/tmp'
)
self.assertEqual(retcode, 0)
@mock.patch('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
@mock.patch('os.path.exists', return_value=True)
@mock.patch('tripleoclient.utils.run_command_and_log')
@mock.patch('os.makedirs')
@mock.patch.object(
Runner,
'run',
return_value=fakes.fake_ansible_runner_run_return()
)
def test_run_success_connection_local(self, mock_run, mock_mkdirs,
mock_exists, mock_mkstemp):
retcode, output = utils.run_ansible_playbook(
playbook='existing.yaml',
inventory='localhost,',
workdir='/tmp',
connection='local'
)
self.assertEqual(retcode, 0)
@mock.patch('os.makedirs', return_value=None)
@mock.patch('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
@mock.patch('os.path.exists', return_value=True)
@mock.patch.object(
Runner,
'run',
return_value=fakes.fake_ansible_runner_run_return()
)
def test_run_success_gathering_policy(self, mock_run, mock_exists,
mok_mkstemp):
mock_process = mock.Mock()
mock_process.returncode = 0
mock_run.return_value = mock_process
mock_mkstemp, mock_makedirs):
retcode, output = utils.run_ansible_playbook(
self.mock_log,
'/tmp',
'existing.yaml',
'localhost,',
gathering_policy='explicit')
playbook='existing.yaml',
inventory='localhost,',
workdir='/tmp',
connection='local',
gathering_policy='smart'
)
self.assertEqual(retcode, 0)
mock_exists.assert_called_once_with('/tmp/existing.yaml')
env = os.environ.copy()
env['ANSIBLE_LIBRARY'] = \
('/root/.ansible/plugins/modules:'
'/usr/share/ansible/plugins/modules:'
'/usr/share/openstack-tripleo-validations/library')
env['ANSIBLE_LOOKUP_PLUGINS'] = \
('root/.ansible/plugins/lookup:'
'/usr/share/ansible/plugins/lookup:'
'/usr/share/openstack-tripleo-validations/lookup_plugins')
env['ANSIBLE_CALLBACK_PLUGINS'] = \
('~/.ansible/plugins/callback:'
'/usr/share/ansible/plugins/callback:'
'/usr/share/openstack-tripleo-validations/callback_plugins')
env['ANSIBLE_ROLES_PATH'] = \
('/root/.ansible/roles:'
'/usr/share/ansible/roles:'
'/etc/ansible/roles:'
'/usr/share/openstack-tripleo-validations/roles')
env['ANSIBLE_CONFIG'] = '/tmp/fooBar.cfg'
env['ANSIBLE_HOST_KEY_CHECKING'] = 'False'
env['ANSIBLE_LOG_PATH'] = '/tmp/ansible.log'
env['TRIPLEO_PLAN_NAME'] = 'overcloud'
env['ANSIBLE_GATHERING'] = 'explicit'
mock_run.assert_called_once_with(self.mock_log,
[self.ansible_playbook_cmd,
'-u', 'root',
'-i', 'localhost,', '-v',
'-c', 'smart',
'/tmp/existing.yaml'],
env=env, retcode_only=False)
@mock.patch('os.makedirs', return_value=None)
@mock.patch('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
@mock.patch('os.path.exists', return_value=True)
@mock.patch('tripleoclient.utils.run_command_and_log')
def test_run_success_extra_vars(self, mock_run, mock_exists, mock_mkstemp):
mock_process = mock.Mock()
mock_process.returncode = 0
mock_run.return_value = mock_process
@mock.patch.object(
Runner,
'run',
return_value=fakes.fake_ansible_runner_run_return()
)
def test_run_success_extra_vars(self, mock_run, mock_exists, mock_mkstemp,
mock_makedirs):
arglist = {
'var_one': 'val_one',
}
retcode, output = utils.run_ansible_playbook(
self.mock_log,
'/tmp',
'existing.yaml',
'localhost,',
extra_vars=arglist)
playbook='existing.yaml',
inventory='localhost,',
workdir='/tmp',
connection='local',
gathering_policy='smart',
extra_vars=arglist
)
self.assertEqual(retcode, 0)
mock_exists.assert_called_once_with('/tmp/existing.yaml')
env = os.environ.copy()
env['ANSIBLE_LIBRARY'] = \
('/root/.ansible/plugins/modules:'
'/usr/share/ansible/plugins/modules:'
'/usr/share/openstack-tripleo-validations/library')
env['ANSIBLE_LOOKUP_PLUGINS'] = \
('root/.ansible/plugins/lookup:'
'/usr/share/ansible/plugins/lookup:'
'/usr/share/openstack-tripleo-validations/lookup_plugins')
env['ANSIBLE_CALLBACK_PLUGINS'] = \
('~/.ansible/plugins/callback:'
'/usr/share/ansible/plugins/callback:'
'/usr/share/openstack-tripleo-validations/callback_plugins')
env['ANSIBLE_ROLES_PATH'] = \
('/root/.ansible/roles:'
'/usr/share/ansible/roles:'
'/etc/ansible/roles:'
'/usr/share/openstack-tripleo-validations/roles')
env['ANSIBLE_CONFIG'] = '/tmp/fooBar.cfg'
env['ANSIBLE_HOST_KEY_CHECKING'] = 'False'
env['ANSIBLE_LOG_PATH'] = '/tmp/ansible.log'
env['TRIPLEO_PLAN_NAME'] = 'overcloud'
mock_run.assert_called_once_with(
self.mock_log, [
self.ansible_playbook_cmd, '-u', 'root',
'-i', 'localhost,', '-v',
'--extra-vars', '%s' % arglist,
'-c', 'smart', '/tmp/existing.yaml'
],
env=env,
retcode_only=False)
class TestRunCommandAndLog(TestCase):
@ -1170,14 +975,6 @@ class TestStoreCliParam(TestCase):
def setUp(self):
self.args = argparse.ArgumentParser()
@mock.patch('os.mkdir')
@mock.patch('os.path.exists')
def test_fail_to_create_file(self, mock_exists, mock_mkdir):
mock_exists.return_value = False
mock_mkdir.side_effect = OSError()
command = "undercloud install"
self.assertRaises(OSError, utils.store_cli_param, command, self.args)
@mock.patch('os.path.isdir')
@mock.patch('os.path.exists')
def test_exists_but_not_dir(self, mock_exists, mock_isdir):
@ -1741,36 +1538,6 @@ class TestGetLocalTimezone(TestCase):
self.assertEqual('UTC', utils.get_local_timezone())
class TestAnsibleSymlink(TestCase):
@mock.patch('tripleoclient.utils.run_command')
@mock.patch('os.path.exists', side_effect=[False, True])
def test_ansible_symlink_needed(self, mock_path, mock_cmd):
utils.ansible_symlink()
python_version = sys.version_info[0]
ansible_playbook_cmd = "ansible-playbook-{}".format(python_version)
mock_cmd.assert_called_once_with(['sudo', 'ln', '-s',
'/usr/bin/' + ansible_playbook_cmd,
'/usr/bin/ansible-playbook'],
name='ansible-playbook-symlink')
@mock.patch('tripleoclient.utils.run_command')
@mock.patch('os.path.exists', side_effect=[True, False])
def test_ansible3_symlink_needed(self, mock_path, mock_cmd):
utils.ansible_symlink()
python_version = sys.version_info[0]
ansible_playbook_cmd = "ansible-playbook-{}".format(python_version)
mock_cmd.assert_called_once_with(['sudo', 'ln', '-s',
'/usr/bin/ansible-playbook',
'/usr/bin/' + ansible_playbook_cmd],
name='ansible-playbook-3-symlink')
@mock.patch('tripleoclient.utils.run_command')
@mock.patch('os.path.exists', side_effect=[False, False])
def test_ansible_symlink_not_needed(self, mock_path, mock_cmd):
utils.ansible_symlink()
mock_cmd.assert_not_called()
class TestGetParamFieldName(TestCase):
def test_with_empty_val_data(self):
input_parameter = {}

View File

@ -262,9 +262,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'UndercloudHostsEntries':
['192.168.0.1 uc.ctlplane.localhost uc.ctlplane']}}
mock_rm = shutil.rmtree = mock.MagicMock()
self.cmd.take_action(parsed_args)
mock_rm.assert_not_called()
self.assertFalse(orchestration_client.stacks.create.called)
@ -309,6 +307,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
self.assertEqual(env_map.get('parameter_defaults'),
parameters_env.get('parameter_defaults'))
@mock.patch('os.chdir')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_undercloud_host_entry', autospec=True,
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
@ -339,7 +338,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_postconfig, mock_shutil_rmtree,
mock_invoke_plan_env_wf,
mock_stack_network_check,
mock_get_undercloud_host_entry):
mock_get_undercloud_host_entry,
mock_chdir):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
plane_management_fixture = deployment.PlanManagementFixture()
@ -446,6 +446,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
clients.tripleoclient.object_store.put_object.assert_called()
self.assertTrue(mock_invoke_plan_env_wf.called)
@mock.patch('os.chdir')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_undercloud_host_entry', autospec=True,
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
@ -470,7 +471,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_create_parameters_env, mock_validate_args,
mock_breakpoints_cleanup,
mock_postconfig, mock_deprecated_params, mock_stack_network_check,
mock_get_undercloud_host_entry):
mock_get_undercloud_host_entry,
mock_chdir):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
plane_management_fixture = deployment.PlanManagementFixture()
@ -526,9 +528,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_create_parameters_env.side_effect = _custom_create_params_env
mock_rm = shutil.rmtree = mock.MagicMock()
self.cmd.take_action(parsed_args)
mock_rm.assert_called_once()
execution_calls = workflow_client.executions.create.call_args_list
deploy_plan_call = execution_calls[1]
deploy_plan_call_input = deploy_plan_call[1]['workflow_input']

View File

@ -968,7 +968,10 @@ class TestContainerImageBuild(TestPluginV1):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
f, path = tempfile.mkstemp(dir=self.temp_dir)
with mock.patch('tempfile.mkdtemp') as mock_mkd:
mock_mkd.return_value = '/tmp/testing'
with mock.patch('tempfile.mkstemp') as mock_mkstemp:
with mock.patch('os.chdir'):
mock_mkstemp.return_value = f, path
self.cmd.take_action(parsed_args)
@ -977,8 +980,9 @@ class TestContainerImageBuild(TestPluginV1):
mock_builder.return_value.build_images.assert_called_once_with([
self.default_kolla_conf, '/tmp/kolla.conf',
path
], [], False, None)
], [], False, '/tmp/testing')
@mock.patch('os.chdir')
@mock.patch('os.fdopen', autospec=True)
@mock.patch('tempfile.mkdtemp')
@mock.patch('tempfile.mkstemp')
@ -997,7 +1001,8 @@ class TestContainerImageBuild(TestPluginV1):
mock_builder, mock_buildah,
mock_kolla_boolean_cfg,
mock_kolla_cfg, mock_mkstemp,
mock_mkdtemp, mock_fdopen):
mock_mkdtemp, mock_fdopen,
mock_chdir):
arglist = [
'--config-file',
'/tmp/bar.yaml',
@ -1056,7 +1061,10 @@ class TestContainerImageBuild(TestPluginV1):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
f, path = tempfile.mkstemp(dir=self.temp_dir)
with mock.patch('tempfile.mkdtemp') as mock_mkd:
mock_mkd.return_value = '/tmp/testing'
with mock.patch('tempfile.mkstemp') as mock_mkstemp:
with mock.patch('os.chdir'):
mock_mkstemp.return_value = f, path
self.cmd.take_action(parsed_args)
@ -1065,7 +1073,7 @@ class TestContainerImageBuild(TestPluginV1):
mock_builder.return_value.build_images.assert_called_once_with([
self.default_kolla_conf, '/tmp/kolla.conf',
path
], ['foo', 'bar'], False, None)
], ['foo', 'bar'], False, '/tmp/testing')
@mock.patch('tripleo_common.image.kolla_builder.KollaImageBuilder',
autospec=True)

View File

@ -24,11 +24,15 @@ from heatclient import exc as hc_exc
from tripleo_common.image import kolla_builder
from tripleoclient import exceptions
from tripleoclient.tests import fakes
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
import ansible_runner
# 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())
@ -47,6 +51,7 @@ class TestDeployUndercloud(TestPluginV1):
# Get the command object to test
self.cmd = tripleo_deploy.Deploy(self.app, None)
self.cmd.ansible_dir = '/tmp'
tripleo_deploy.Deploy.heat_pid = mock.MagicMock(
return_value=False)
@ -59,8 +64,7 @@ class TestDeployUndercloud(TestPluginV1):
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)
self.ansible_playbook_cmd = "ansible-playbook"
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy._is_undercloud_deploy')
@mock.patch('tripleoclient.utils.check_hostname')
@ -833,37 +837,6 @@ class TestDeployUndercloud(TestPluginV1):
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('/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('tripleoclient.utils.'
'run_command_and_log', autospec=True)
@mock.patch('os.chdir')
@mock.patch('os.execvp')
def test_launch_ansible_with_args(self, mock_execvp, mock_chdir, mock_run):
args = ['--skip-tags', 'validation']
self.cmd._launch_ansible('/tmp', args, operation='deploy')
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', '--skip-tags', 'validation'])
@mock.patch('os.execvp')
def test_launch_ansible_invalid_op(self, mock_execvp):
self.assertRaises(exceptions.DeploymentError, self.cmd._launch_ansible,
'/tmp', operation='unploy')
@mock.patch('tripleo_common.image.kolla_builder.'
'container_images_prepare_multi')
def test_prepare_container_images(self, mock_cipm):
@ -886,6 +859,18 @@ class TestDeployUndercloud(TestPluginV1):
env
)
@mock.patch.object(
ansible_runner.runner_config.RunnerConfig,
'prepare',
return_value=fakes.fake_ansible_runner_run_return()
)
@mock.patch.object(
ansible_runner.Runner,
'run',
return_value=fakes.fake_ansible_runner_run_return()
)
@mock.patch('os.path.exists')
@mock.patch('os.chdir')
@mock.patch('tripleoclient.utils.reset_cmdline')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_download_stack_outputs')
@ -903,8 +888,6 @@ class TestDeployUndercloud(TestPluginV1):
'_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', return_value=0)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_cleanup_working_dirs')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
@ -928,18 +911,17 @@ class TestDeployUndercloud(TestPluginV1):
@mock.patch('tripleoclient.utils.wait_for_stack_ready', return_value=True)
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
'_set_default_plan')
@mock.patch('tripleoclient.utils.ansible_symlink')
def test_take_action_standalone(self, mock_slink, mock_def_plan, mock_poll,
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, mock_outputs,
mock_cmdline):
mock_slink.side_effect = 'fake-cmd'
mock_cleanupdirs, mock_tarball,
mock_templates_dir, mock_open, mock_os,
mock_user, mock_cc, mock_chmod, mock_ac,
mock_outputs, mock_cmdline, mock_chdir,
mock_file_exists, mock_run,
mock_run_prepare):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
@ -957,6 +939,7 @@ class TestDeployUndercloud(TestPluginV1):
'-e', '../../../outside.yaml',
'--standalone'], [])
mock_file_exists.return_value = True
fake_orchestration = mock_launchheat(parsed_args)
self.cmd.take_action(parsed_args)
mock_createdirs.assert_called_once()
@ -967,16 +950,12 @@ class TestDeployUndercloud(TestPluginV1):
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)
mock_cmdline.assert_called_once()
@mock.patch('tripleoclient.utils.reset_cmdline')
@mock.patch('tripleoclient.utils.ansible_symlink')
def test_take_action(self, mock_slink, mock_cmdline):
mock_slink.side_effect = 'fake-cmd'
def test_take_action(self, mock_cmdline):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
@ -984,14 +963,11 @@ class TestDeployUndercloud(TestPluginV1):
'--output-dir', '/my'], [])
self.assertRaises(exceptions.DeploymentError,
self.cmd.take_action, parsed_args)
mock_cmdline.assert_called_once()
@mock.patch('tripleoclient.utils.reset_cmdline')
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy._standalone_deploy',
return_value=1)
@mock.patch('tripleoclient.utils.ansible_symlink')
def test_take_action_failure(self, mock_slink, mock_deploy, mock_cmdline):
mock_slink.side_effect = 'fake-cmd'
def test_take_action_failure(self, mock_deploy, mock_cmdline):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
@ -1000,7 +976,6 @@ class TestDeployUndercloud(TestPluginV1):
'--standalone'], [])
self.assertRaises(exceptions.DeploymentError,
self.cmd.take_action, parsed_args)
mock_cmdline.assert_called_once()
@mock.patch('os.path.isfile', return_value=False)
def test_set_stack_action_default_create(self, mock_isfile):

View File

@ -14,7 +14,6 @@
#
import mock
import sys
from osc_lib.tests import utils
import six
@ -31,48 +30,8 @@ class TestUpgrade(utils.TestCommand):
# Get the command object to test
self.cmd = tripleo_upgrade.Upgrade(self.app, None)
python_version = sys.version_info[0]
self.ansible_playbook_cmd = "ansible-playbook-%s" % (python_version)
@mock.patch('tripleoclient.utils.'
'run_command_and_log', autospec=True)
@mock.patch('os.chdir')
@mock.patch('os.execvp')
def test_launch_ansible_upgrade(self, mock_execvp, mock_chdir, mock_run):
self.cmd._launch_ansible('/tmp', operation='upgrade')
mock_chdir.assert_called_once()
mock_run.assert_called_once_with(self.cmd.log, [
self.ansible_playbook_cmd, '-i', '/tmp/inventory.yaml',
'upgrade_steps_playbook.yaml',
'--skip-tags', 'validation'])
@mock.patch('tripleoclient.utils.'
'run_command_and_log', autospec=True)
@mock.patch('os.chdir')
@mock.patch('os.execvp')
def test_launch_ansible_post_upgrade(self, mock_execvp, mock_chdir,
mock_run):
self.cmd._launch_ansible('/tmp', operation='post-upgrade')
mock_chdir.assert_called_once()
mock_run.assert_called_once_with(self.cmd.log, [
self.ansible_playbook_cmd, '-i', '/tmp/inventory.yaml',
'post_upgrade_steps_playbook.yaml',
'--skip-tags', 'validation'])
@mock.patch('tripleoclient.utils.'
'run_command_and_log', autospec=True)
@mock.patch('os.chdir')
@mock.patch('os.execvp')
def test_launch_ansible_online_upgrade(self, mock_execvp, mock_chdir,
mock_run):
self.cmd._launch_ansible('/tmp', operation='online-upgrade')
mock_chdir.assert_called_once()
mock_run.assert_called_once_with(self.cmd.log, [
self.ansible_playbook_cmd, '-i', '/tmp/inventory.yaml',
'external_upgrade_steps_playbook.yaml',
'--tags', 'online_upgrade'])
self.cmd.ansible_dir = '/tmp'
self.ansible_playbook_cmd = "ansible-playbook"
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.take_action',
autospec=True)
@ -127,9 +86,7 @@ class TestUpgrade(utils.TestCommand):
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy',
autospec=True)
@mock.patch('sys.stdin', spec=six.StringIO)
@mock.patch('tripleoclient.utils.ansible_symlink')
def test_take_action_prompt_no(self, mock_slink, mock_stdin, mock_deploy):
mock_slink.side_effect = 'fake-cmd'
def test_take_action_prompt_no(self, mock_stdin, mock_deploy):
mock_stdin.isatty.return_value = True
mock_stdin.readline.return_value = 'n'
parsed_args = self.check_parser(self.cmd,
@ -153,10 +110,7 @@ class TestUpgrade(utils.TestCommand):
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy',
autospec=True)
@mock.patch('sys.stdin', spec=six.StringIO)
@mock.patch('tripleoclient.utils.ansible_symlink')
def test_take_action_prompt_invalid_option(self, mock_slink, mock_stdin,
mock_deploy):
mock_slink.side_effect = 'fake-cmd'
def test_take_action_prompt_invalid_option(self, mock_stdin, mock_deploy):
mock_stdin.isatty.return_value = True
mock_stdin.readline.return_value = 'Dontwant'
parsed_args = self.check_parser(self.cmd,

View File

@ -14,7 +14,6 @@
#
import mock
import sys
from osc_lib.tests import utils
from tripleoclient.v1 import tripleo_validator
@ -115,56 +114,3 @@ class TestValidatorShowParameter(utils.TestCommand):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
class TestValidatorRun(utils.TestCommand):
def setUp(self):
super(TestValidatorRun, self).setUp()
# Get the command object to test
self.cmd = tripleo_validator.TripleOValidatorRun(self.app, None)
@mock.patch('sys.exit')
@mock.patch('logging.getLogger')
@mock.patch('pwd.getpwuid')
@mock.patch('os.getuid')
@mock.patch('tripleoclient.utils.get_tripleo_ansible_inventory',
return_value='/home/stack/inventory.yaml')
@mock.patch('tripleoclient.utils.run_ansible_playbook',
autospec=True)
def test_validation_run_with_ansible(self, plan_mock, mock_inventory,
mock_getuid, mock_getpwuid,
mock_logger, mock_sysexit):
mock_pwuid = mock.Mock()
mock_pwuid.pw_dir = '/home/stack'
mock_getpwuid.return_value = mock_pwuid
mock_log = mock.Mock()
mock_logger.return_value = mock_log
playbooks_dir = '/usr/share/openstack-tripleo-validations/playbooks'
arglist = [
'--validation',
'check-ftype'
]
verifylist = [('validation_name', ['check-ftype'])]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
plan_mock.assert_called_once_with(
logger=mock_log,
plan='overcloud',
inventory='/home/stack/inventory.yaml',
workdir=playbooks_dir,
log_path_dir='/home/stack',
playbook='check-ftype.yaml',
retries=False,
output_callback='validation_output',
extra_vars={},
python_interpreter='/usr/bin/python{}'.format(sys.version_info[0]),
gathering_policy='explicit'
)
assert mock_sysexit.called

View File

@ -36,8 +36,7 @@ class TestMinionDeploy(base.TestCase):
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_passwords')
@mock.patch('tripleoclient.v1.undercloud_preflight.minion_check')
@mock.patch('tripleoclient.utils.ansible_symlink')
@mock.patch('os.path.isdir', return_value=True)
@mock.patch('os.makedirs', return_value=None)
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_output',
return_value='output.yaml')
@mock.patch('tripleoclient.v1.minion_config._container_images_config')
@ -46,7 +45,7 @@ class TestMinionDeploy(base.TestCase):
@mock.patch('tripleoclient.utils.load_config')
def test_basic_deploy(self, mock_load_config, mock_get_user,
mock_write_env, mock_undercloud_output,
mock_images_config, mock_isdir, mock_ans_symlink,
mock_images_config, mock_isdir,
mock_check, mock_pass):
mock_get_user.return_value = 'foo'
cmd = minion_config.prepare_minion_deploy()
@ -102,9 +101,8 @@ class TestMinionDeploy(base.TestCase):
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_passwords')
@mock.patch('tripleoclient.v1.undercloud_preflight.minion_check')
@mock.patch('tripleoclient.utils.ansible_symlink')
@mock.patch('os.path.exists', return_value=True)
@mock.patch('os.path.isdir', return_value=True)
@mock.patch('os.makedirs', return_value=None)
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_output',
return_value='output.yaml')
@mock.patch('tripleoclient.v1.minion_config._container_images_config')
@ -113,7 +111,7 @@ class TestMinionDeploy(base.TestCase):
def test_configured_deploy(self, mock_load_config,
mock_write_env, mock_undercloud_output,
mock_images_config, mock_isdir, mock_exists,
mock_ans_symlink, mock_check, mock_pass):
mock_check, mock_pass):
self.conf.set_default('deployment_user', 'bar')
self.conf.set_default('enable_heat_engine', False)
self.conf.set_default('enable_ironic_conductor', True)

View File

@ -54,6 +54,7 @@ class TestUndercloudInstall(TestPluginV1):
self.cmd = undercloud.InstallUndercloud(self.app, app_args)
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('os.geteuid', return_value=1001)
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('six.moves.builtins.open')
@mock.patch('shutil.copy')
@ -63,7 +64,7 @@ class TestUndercloudInstall(TestPluginV1):
def test_undercloud_install_default(self, mock_subprocess,
mock_wr,
mock_os, mock_copy,
mock_open, mock_user):
mock_open, mock_user, mock_getuid):
arglist = ['--no-validations']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -118,14 +119,16 @@ class TestUndercloudInstall(TestPluginV1):
'undercloud-stack-vstate-dropin.yaml'])
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('os.geteuid', return_value=1001)
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('shutil.copy')
@mock.patch('os.mkdir')
@mock.patch('os.makedirs', return_value=None)
@mock.patch('tripleoclient.utils.write_env_file', autospec=True)
@mock.patch('subprocess.check_call', autospec=True)
def test_undercloud_install_with_heat_customized(self, mock_subprocess,
mock_wr, mock_os,
mock_copy, mock_user):
mock_copy, mock_user,
mock_getuid):
self.conf.config(output_dir='/foo')
self.conf.config(templates='/usertht')
self.conf.config(heat_native='false')
@ -178,6 +181,7 @@ class TestUndercloudInstall(TestPluginV1):
'--force-stack-update'])
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('os.geteuid', return_value=1001)
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('shutil.copy')
@mock.patch('os.mkdir')
@ -200,7 +204,8 @@ class TestUndercloudInstall(TestPluginV1):
mock_sroutes,
mock_masq,
mock_wr, mock_os,
mock_copy, mock_user):
mock_copy, mock_user,
mock_getuid):
self.conf.config(net_config_override='/foo/net-config.json')
self.conf.config(local_interface='ethX')
self.conf.config(undercloud_public_host='4.3.2.1')
@ -348,6 +353,7 @@ class TestUndercloudInstall(TestPluginV1):
'undercloud-stack-vstate-dropin.yaml'])
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('os.geteuid', return_value=1001)
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('six.moves.builtins.open')
@mock.patch('shutil.copy')
@ -357,7 +363,8 @@ class TestUndercloudInstall(TestPluginV1):
def test_undercloud_install_with_heat_and_debug(self, mock_subprocess,
mock_wr,
mock_os, mock_copy,
mock_open, mock_user):
mock_open, mock_user,
mock_getuid):
self.conf.config(undercloud_log_file='/foo/bar')
arglist = ['--no-validations']
verifylist = []
@ -416,6 +423,7 @@ class TestUndercloudInstall(TestPluginV1):
'undercloud-stack-vstate-dropin.yaml'])
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('os.geteuid', return_value=1001)
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('six.moves.builtins.open')
@mock.patch('shutil.copy')
@ -425,7 +433,8 @@ class TestUndercloudInstall(TestPluginV1):
def test_undercloud_install_with_heat_true(self, mock_subprocess,
mock_wr,
mock_os, mock_copy,
mock_open, mock_user):
mock_open, mock_user,
mock_getuid):
self.conf.config(undercloud_log_file='/foo/bar')
arglist = ['--no-validations']
verifylist = []
@ -480,6 +489,7 @@ class TestUndercloudInstall(TestPluginV1):
'undercloud-stack-vstate-dropin.yaml'])
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('os.geteuid', return_value=1001)
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('shutil.copy')
@mock.patch('os.mkdir')
@ -487,7 +497,8 @@ class TestUndercloudInstall(TestPluginV1):
@mock.patch('subprocess.check_call', autospec=True)
def test_undercloud_install_with_swift_encryption(self, mock_subprocess,
mock_wr, mock_os,
mock_copy, mock_user):
mock_copy, mock_user,
mock_getuid):
arglist = ['--no-validations']
verifylist = []
self.conf.set_default('enable_swift_encryption', True)
@ -564,6 +575,7 @@ class TestUndercloudUpgrade(TestPluginV1):
self.cmd = undercloud.UpgradeUndercloud(self.app, app_args)
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('os.geteuid', return_value=1001)
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('shutil.copy')
@mock.patch('os.mkdir')
@ -571,7 +583,8 @@ class TestUndercloudUpgrade(TestPluginV1):
@mock.patch('subprocess.check_call', autospec=True)
def test_undercloud_upgrade_default(self, mock_subprocess,
mock_wr,
mock_os, mock_copy, mock_user):
mock_os, mock_copy, mock_user,
mock_getuid):
arglist = ['--no-validations']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -629,6 +642,7 @@ class TestUndercloudUpgrade(TestPluginV1):
'undercloud-stack-vstate-dropin.yaml'])
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('os.geteuid', return_value=1001)
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('shutil.copy')
@mock.patch('os.mkdir')
@ -636,7 +650,8 @@ class TestUndercloudUpgrade(TestPluginV1):
@mock.patch('subprocess.check_call', autospec=True)
def test_undercloud_upgrade_with_heat_enabled(self, mock_subprocess,
mock_wr, mock_os,
mock_copy, mock_user):
mock_copy, mock_user,
mock_getuid):
arglist = ['--no-validations']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -693,6 +708,7 @@ class TestUndercloudUpgrade(TestPluginV1):
'undercloud-stack-vstate-dropin.yaml'])
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('os.geteuid', return_value=1001)
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('shutil.copy')
@mock.patch('os.mkdir')
@ -700,7 +716,8 @@ class TestUndercloudUpgrade(TestPluginV1):
@mock.patch('subprocess.check_call', autospec=True)
def test_undercloud_upgrade_with_heat_true(self, mock_subprocess,
mock_wr, mock_os,
mock_copy, mock_user):
mock_copy, mock_user,
mock_getuid):
arglist = ['--no-validations']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -757,6 +774,7 @@ class TestUndercloudUpgrade(TestPluginV1):
'/usr/share/openstack-tripleo-heat-templates/'
'undercloud-stack-vstate-dropin.yaml'])
@mock.patch('os.geteuid', return_value=1001)
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('shutil.copy')
@mock.patch('os.mkdir')
@ -764,7 +782,8 @@ class TestUndercloudUpgrade(TestPluginV1):
@mock.patch('subprocess.check_call', autospec=True)
def test_undercloud_upgrade_with_heat_and_yes(self, mock_subprocess,
mock_wr, mock_os,
mock_user, mock_copy):
mock_copy, mock_user,
mock_getuid):
arglist = ['--no-validations', '-y']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -822,6 +841,7 @@ class TestUndercloudUpgrade(TestPluginV1):
'undercloud-stack-vstate-dropin.yaml'])
# TODO(cjeanner) drop once we have proper oslo.privsep
@mock.patch('os.geteuid', return_value=1001)
@mock.patch('getpass.getuser', return_value='stack')
@mock.patch('shutil.copy')
@mock.patch('os.mkdir')
@ -829,7 +849,8 @@ class TestUndercloudUpgrade(TestPluginV1):
@mock.patch('subprocess.check_call', autospec=True)
def test_undercloud_upgrade_with_heat_and_debug(self, mock_subprocess,
mock_wr, mock_os,
mock_copy, mock_user):
mock_copy, mock_user,
mock_getuid):
arglist = ['--no-validations']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)

View File

@ -46,14 +46,16 @@ class TestDeploymentWorkflows(utils.TestCommand):
"message": "Fail.",
}])
@mock.patch('os.chdir')
@mock.patch('tripleoclient.workflows.deployment.wait_for_ssh_port')
@mock.patch('tripleoclient.workflows.deployment.time.sleep')
@mock.patch('tripleoclient.workflows.deployment.shutil.rmtree')
@mock.patch('tripleoclient.utils.shutil.rmtree')
@mock.patch('tripleoclient.workflows.deployment.open')
@mock.patch('tripleoclient.workflows.deployment.tempfile')
@mock.patch('tripleoclient.utils.tempfile')
@mock.patch('tripleoclient.workflows.deployment.subprocess.check_call')
def test_enable_ssh_admin(self, mock_check_call, mock_tempfile, mock_open,
mock_rmtree, mock_sleep, mock_wait_for_ssh_port):
mock_rmtree, mock_sleep, mock_wait_for_ssh_port,
mock_chdir):
log = mock.Mock()
hosts = 'a', 'b', 'c'
ssh_user = 'test-user'
@ -86,15 +88,16 @@ class TestDeploymentWorkflows(utils.TestCommand):
self.assertEqual(1, mock_rmtree.call_count)
self.assertEqual('/foo', mock_rmtree.call_args[0][0])
@mock.patch('os.chdir')
@mock.patch('tripleoclient.workflows.deployment.wait_for_ssh_port')
@mock.patch('tripleoclient.workflows.deployment.time.sleep')
@mock.patch('tripleoclient.workflows.deployment.shutil.rmtree')
@mock.patch('tripleoclient.utils.shutil.rmtree')
@mock.patch('tripleoclient.workflows.deployment.open')
@mock.patch('tripleoclient.workflows.deployment.tempfile')
@mock.patch('tripleoclient.utils.tempfile')
@mock.patch('tripleoclient.workflows.deployment.subprocess.check_call')
def test_enable_ssh_admin_error(self, mock_check_call, mock_tempfile,
mock_open, mock_rmtree, mock_sleep,
mock_wait_for_ssh_port):
mock_wait_for_ssh_port, mock_chdir):
log = mock.Mock()
hosts = 'a', 'b', 'c'
ssh_user = 'test-user'

View File

@ -155,11 +155,11 @@ class TestDownloadContainer(fakes.TestDeployOvercloud):
'test',
'test')
@mock.patch('os.path.exists')
def test_download_files(self, exists_mock):
@mock.patch('os.makedirs')
def test_download_files(self, makedirs_mock):
support.check_local_space = mock.MagicMock()
support.check_local_space.return_value = True
exists_mock.return_value = True
makedirs_mock.return_value = None
oc = self.app.client_manager.object_store
oc.object_list.return_value = [
{'name': 'test1'}

View File

@ -28,13 +28,14 @@ import getpass
import glob
import hashlib
import logging
import shutil
from six.moves.configparser import ConfigParser
import json
import netaddr
import os
import os.path
import pwd
import shutil
import simplejson
import six
import socket
@ -44,6 +45,8 @@ import tempfile
import time
import yaml
import ansible_runner
from heatclient.common import event_utils
from heatclient.common import template_utils
from heatclient.common import utils as heat_utils
@ -63,204 +66,450 @@ from tripleoclient import exceptions
LOG = logging.getLogger(__name__ + ".utils")
def run_ansible_playbook(logger,
workdir,
playbook,
inventory,
log_path_dir=None,
ansible_config=None,
retries=True,
connection='smart',
output_callback='json',
python_interpreter=None,
ssh_user='root',
key=None,
module_path=None,
limit_hosts=None,
tags=None,
skip_tags=None,
verbosity=1,
extra_vars=None,
plan='overcloud',
gathering_policy=None):
"""Simple wrapper for ansible-playbook
# NOTE(cloudnull): This is setting the FileExistsError for py2 environments.
# When we no longer support py2 (centos7) this should be
# removed.
try:
FileExistsError = FileExistsError
except NameError:
FileExistsError = OSError
:param logger: logger instance
:type logger: Logger
:param plan: plan name (Defaults to "overcloud")
:type plan: String
class Pushd(object):
"""Simple context manager to change directories and then return."""
:param workdir: location of the playbook
:type workdir: String
def __init__(self, directory):
"""This context manager will enter and exit directories.
:param playbook: playbook filename
>>> with Pushd(directory='/tmp'):
... with open('file', 'w') as f:
... f.write('test')
:param directory: path to change directory to
:type directory: `string`
"""
self.dir = directory
self.pwd = self.cwd = os.getcwd()
def __enter__(self):
os.chdir(self.dir)
self.cwd = os.getcwd()
return self
def __exit__(self, *args):
if self.pwd != self.cwd:
os.chdir(self.pwd)
class TempDirs(object):
"""Simple context manager to manage temp directories."""
def __init__(self, dir_path=None, dir_prefix='tripleo', cleanup=True,
chdir=True):
"""This context manager will create, push, and cleanup temp directories.
>>> with TempDirs() as t:
... with open('file', 'w') as f:
... f.write('test')
... print(t)
... os.mkdir('testing')
... with open(os.path.join(t, 'file')) as w:
... print(w.read())
... with open('testing/file', 'w') as f:
... f.write('things')
... with open(os.path.join(t, 'testing/file')) as w:
... print(w.read())
:param dir_path: path to create the temp directory
:type dir_path: `string`
:param dir_prefix: prefix to add to a temp directory
:type dir_prefix: `string`
:paramm cleanup: when enabled the temp directory will be
removed on exit.
:type cleanup: `boolean`
:param chdir: Change to/from the created temporary dir on enter/exit.
:type chdir: `boolean`
"""
# NOTE(cloudnull): kwargs for tempfile.mkdtemp are created
# because args are not processed correctly
# in py2. When we drop py2 support (cent7)
# these args can be removed and used directly
# in the `tempfile.mkdtemp` function.
tempdir_kwargs = dict()
if dir_path:
tempdir_kwargs['dir'] = dir_path
if dir_prefix:
tempdir_kwargs['prefix'] = dir_prefix
self.dir = tempfile.mkdtemp(**tempdir_kwargs)
self.pushd = Pushd(directory=self.dir)
self.cleanup = cleanup
self.chdir = chdir
def __enter__(self):
if self.chdir:
self.pushd.__enter__()
return self.dir
def __exit__(self, *args):
if self.chdir:
self.pushd.__exit__()
if self.cleanup:
self.clean()
else:
LOG.warning("Not cleaning temporary directory [ %s ]" % self.dir)
def clean(self):
shutil.rmtree(self.dir, ignore_errors=True)
LOG.info("Temporary directory [ %s ] cleaned up" % self.dir)
def _encode_envvars(env):
"""Encode a hash of values.
:param env: A hash of key=value items.
:type env: `dict`.
"""
for key, value in env.items():
env[key] = six.text_type(value)
else:
return env
def makedirs(dir_path):
"""Recursively make directories and log the interaction.
:param dir_path: full path of the directories to make.
:type dir_path: `string`
:returns: `boolean`
"""
try:
os.makedirs(dir_path)
except FileExistsError:
LOG.debug(
'Directory "{}" was not created because it'
' already exists.'.format(
dir_path
)
)
return False
else:
LOG.debug('Directory "{}" was created.'.format(dir_path))
return True
def run_ansible_playbook(playbook, inventory, workdir, playbook_dir=None,
connection='smart', output_callback='yaml',
ssh_user='root', key=None, module_path=None,
limit_hosts=None, tags=None, skip_tags=None,
verbosity=0, quiet=False, extra_vars=None,
plan='overcloud', gathering_policy='smart',
extra_env_variables=None):
"""Simple wrapper for ansible-playbook.
:param playbook: Playbook filename.
:type playbook: String
:param inventory: either proper inventory file, or a coma-separated list
:param inventory: Either proper inventory file, or a coma-separated list.
:type inventory: String
:param ansible_config: Pass either Absolute Path, or None to generate a
temporary file, or False to not manage configuration at all
:type ansible_config: String
:param workdir: Location of the working directory.
:type workdir: String
:param log_path_dir: Dir path location for ansible log file.
Defaults to "None"
:type retries: String
:param playbook_dir: Location of the playbook directory.
(defaults to workdir).
:type playbook_dir: String
:param retries: do you want to get a retry_file?
:type retries: Boolean
:param connection: connection type (local, smart, etc)
:param connection: Connection type (local, smart, etc).
:type connection: String
:param output_callback: Callback for output format. Defaults to "json"
:param output_callback: Callback for output format. Defaults to "json".
:type output_callback: String
:param python_interpreter: Absolute path for the Python interpreter
on the host where Ansible is run.
:type python_interpreter: String
:param ssh_user: user for the ssh connection
:param ssh_user: User for the ssh connection.
:type ssh_user: String
:param key: private key to use for the ssh connection
:param key: Private key to use for the ssh connection.
:type key: String
:param module_path: location of the ansible module and library
:param module_path: Location of the ansible module and library.
:type module_path: String
:param limit_hosts: limit the execution to the hosts
:param limit_hosts: Limit the execution to the hosts.
:type limit_hosts: String
:param tags: run specific tags
:param tags: Run specific tags.
:type tags: String
:param skip_tags: skip specific tags
:param skip_tags: Skip specific tags.
:type skip_tags: String
:param verbosity: verbosity level for Ansible execution
:param verbosity: Verbosity level for Ansible execution.
:type verbosity: Integer
:param extra_vars: set additional variables as a Dict
or the absolute path of a JSON or YAML file type
:param quiet: Disable all output (Defaults to False)
:type quiet: Boolean
:param extra_vars: Set additional variables as a Dict or the absolute
path of a JSON or YAML file type.
:type extra_vars: Either a Dict or the absolute path of JSON or YAML
:param gathering_policy: This setting controls the default policy of
fact gathering ('smart', 'implicit', 'explicit'). Defaults to None.
When not specified, the policy will be the default Ansible one, ie.
'implicit'.
:type gathering_facts: String
"""
env = os.environ.copy()
:param plan: Plan name (Defaults to "overcloud").
:type plan: String
env['ANSIBLE_LIBRARY'] = \
('/root/.ansible/plugins/modules:'
:param gathering_policy: This setting controls the default policy of
fact gathering ('smart', 'implicit', 'explicit').
:type gathering_facts: String
:param extra_env_variables: Dict option to extend or override any of the
default environment variables.
:type extra_env_variables: Dict
"""
def _playbook_check(play):
if not os.path.exists(play):
play = os.path.join(playbook_dir, play)
if not os.path.exists(play):
raise RuntimeError('No such playbook: {}'.format(play))
LOG.debug('Ansible playbook {} found'.format(play))
return play
if not playbook_dir:
playbook_dir = workdir
if isinstance(playbook, (list, set)):
playbook = [_playbook_check(play=i) for i in playbook]
LOG.info(
'Running Ansible playbooks: {},'
' Working directory: {},'
' Playbook directory: {}'.format(
playbook,
workdir,
playbook_dir
)
)
else:
playbook = _playbook_check(play=playbook)
LOG.info(
'Running Ansible playbook: {},'
' Working directory: {},'
' Playbook directory: {}'.format(
playbook,
workdir,
playbook_dir
)
)
cwd = os.getcwd()
ansible_fact_path = os.path.join(
os.path.join(
tempfile.gettempdir(),
'tripleo-ansible'
),
'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)
}
if extra_vars:
if isinstance(extra_vars, dict):
extravars.update(extra_vars)
elif os.path.exists(extra_vars) and os.path.isfile(extra_vars):
with open(extra_vars) as f:
extravars.update(yaml.safe_load(f.read()))
env = os.environ.copy()
env['ANSIBLE_DISPLAY_FAILED_STDERR'] = True
env['ANSIBLE_FORKS'] = 36
env['ANSIBLE_TIMEOUT'] = 30
env['ANSIBLE_GATHER_TIMEOUT'] = 45
env['ANSIBLE_SSH_RETRIES'] = 3
env['ANSIBLE_PIPELINING'] = True
env['ANSIBLE_REMOTE_USER'] = ssh_user
env['ANSIBLE_STDOUT_CALLBACK'] = output_callback
env['ANSIBLE_LIBRARY'] = os.path.expanduser(
'~/.ansible/plugins/modules:'
'{}:{}:'
'/usr/share/ansible/tripleo-plugins/modules:'
'/usr/share/ansible/plugins/modules:'
'%s/library' % constants.DEFAULT_VALIDATIONS_BASEDIR)
env['ANSIBLE_LOOKUP_PLUGINS'] = \
('root/.ansible/plugins/lookup:'
'/usr/share/ceph-ansible/library:'
'{}/library'.format(
os.path.join(workdir, 'modules'),
os.path.join(cwd, 'modules'),
constants.DEFAULT_VALIDATIONS_BASEDIR
)
)
env['ANSIBLE_LOOKUP_PLUGINS'] = os.path.expanduser(
'~/.ansible/plugins/lookup:'
'{}:{}:'
'/usr/share/ansible/tripleo-plugins/lookup:'
'/usr/share/ansible/plugins/lookup:'
'%s/lookup_plugins' % constants.DEFAULT_VALIDATIONS_BASEDIR)
env['ANSIBLE_CALLBACK_PLUGINS'] = \
('~/.ansible/plugins/callback:'
'/usr/share/ceph-ansible/plugins/lookup:'
'{}/lookup_plugins'.format(
os.path.join(workdir, 'lookup'),
os.path.join(cwd, 'lookup'),
constants.DEFAULT_VALIDATIONS_BASEDIR
)
)
env['ANSIBLE_CALLBACK_PLUGINS'] = os.path.expanduser(
'~/.ansible/plugins/callback:'
'{}:{}:'
'/usr/share/ansible/tripleo-plugins/callback:'
'/usr/share/ansible/plugins/callback:'
'%s/callback_plugins' % constants.DEFAULT_VALIDATIONS_BASEDIR)
env['ANSIBLE_ROLES_PATH'] = \
('/root/.ansible/roles:'
'/usr/share/ceph-ansible/plugins/callback:'
'{}/callback_plugins'.format(
os.path.join(workdir, 'callback'),
os.path.join(cwd, 'callback'),
constants.DEFAULT_VALIDATIONS_BASEDIR
)
)
env['ANSIBLE_ACTION_PLUGINS'] = os.path.expanduser(
'~/.ansible/plugins/action:'
'{}:{}:'
'/usr/share/ansible/tripleo-plugins/action:'
'/usr/share/ansible/plugins/action:'
'/usr/share/ceph-ansible/plugins/actions:'
'{}/action_plugins'.format(
os.path.join(workdir, 'action'),
os.path.join(cwd, 'action'),
constants.DEFAULT_VALIDATIONS_BASEDIR
)
)
env['ANSIBLE_FILTER_PLUGINS'] = os.path.expanduser(
'~/.ansible/plugins/filter:'
'{}:{}:'
'/usr/share/ansible/tripleo-plugins/filter:'
'/usr/share/ansible/plugins/filter:'
'/usr/share/ceph-ansible/plugins/filter:'
'{}/filter_plugins'.format(
os.path.join(workdir, 'filter'),
os.path.join(cwd, 'filter'),
constants.DEFAULT_VALIDATIONS_BASEDIR
)
)
env['ANSIBLE_ROLES_PATH'] = os.path.expanduser(
'~/.ansible/roles:'
'{}:{}:'
'/usr/share/ansible/tripleo-roles:'
'/usr/share/ansible/roles:'
'/usr/share/ceph-ansible/roles:'
'/etc/ansible/roles:'
'%s/roles' % constants.DEFAULT_VALIDATIONS_BASEDIR)
'{}/roles'.format(
os.path.join(workdir, 'roles'),
os.path.join(cwd, 'roles'),
constants.DEFAULT_VALIDATIONS_BASEDIR
)
)
env['ANSIBLE_CALLBACK_WHITELIST'] = 'profile_tasks,validation_output'
env['ANSIBLE_RETRY_FILES_ENABLED'] = False
env['ANSIBLE_HOST_KEY_CHECKING'] = False
env['ANSIBLE_TRANSPORT'] = connection
env['ANSIBLE_CACHE_PLUGIN_TIMEOUT'] = 7200
if connection == 'local':
env['ANSIBLE_PYTHON_INTERPRETER'] = sys.executable
if gathering_policy in ('smart', 'explicit', 'implicit'):
env['ANSIBLE_GATHERING'] = gathering_policy
if module_path:
env['ANSIBLE_LIBRARY'] = ':'.join(
[env['ANSIBLE_LIBRARY'], module_path]
)
env['TRIPLEO_PLAN_NAME'] = plan
if not log_path_dir or not os.path.exists(log_path_dir):
env['ANSIBLE_LOG_PATH'] = os.path.join(workdir, 'ansible.log')
try:
user_pwd = pwd.getpwuid(int(os.getenv('SUDO_UID', os.getuid())))
except TypeError:
home = os.path.expanduser('~')
else:
env['ANSIBLE_LOG_PATH'] = os.path.join(log_path_dir, 'ansible.log')
home = user_pwd.pw_dir
env['ANSIBLE_HOST_KEY_CHECKING'] = 'False'
if gathering_policy in ['smart', 'explicit', 'implicit']:
env['ANSIBLE_GATHERING'] = gathering_policy
if extra_vars is None:
extra_vars = {}
cleanup = False
if ansible_config is None:
_, tmp_config = tempfile.mkstemp(prefix=playbook, suffix='ansible.cfg')
with open(tmp_config, 'w+') as f:
f.write("[defaults]\nstdout_callback = %s\n" % output_callback)
if not retries:
f.write("retry_files_enabled = False\n")
f.close()
env['ANSIBLE_CONFIG'] = tmp_config
cleanup = True
elif os.path.isabs(ansible_config):
if os.path.exists(ansible_config):
env['ANSIBLE_CONFIG'] = ansible_config
else:
raise RuntimeError('No such configuration file: %s' %
ansible_config)
elif os.path.exists(os.path.join(workdir, ansible_config)):
env['ANSIBLE_CONFIG'] = os.path.join(workdir, ansible_config)
play = os.path.join(workdir, playbook)
if os.path.exists(play):
cmd = ["ansible-playbook-{}".format(sys.version_info[0]),
'-u', ssh_user,
'-i', inventory
]
if 0 < verbosity < 6:
cmd.extend(['-' + ('v' * verbosity)])
env['ANSIBLE_LOG_PATH'] = os.path.join(home, 'ansible.log')
if key:
cmd.extend(['--private-key=%s' % key])
env['ANSIBLE_PRIVATE_KEY_FILE'] = key
if module_path:
cmd.extend(['--module-path=%s' % module_path])
if extra_env_variables:
if not isinstance(extra_env_variables, dict):
msg = "extra_env_variables must be a dict"
LOG.error(msg)
raise SystemError(msg)
else:
env.update(extra_env_variables)
if limit_hosts:
cmd.extend(['-l %s' % limit_hosts])
if tags:
cmd.extend(['-t %s' % tags])
with TempDirs(chdir=False) as ansible_artifact_path:
r_opts = {
'private_data_dir': workdir,
'project_dir': playbook_dir,
'inventory': inventory,
'envvars': _encode_envvars(env=env),
'playbook': playbook,
'verbosity': verbosity,
'quiet': quiet,
'extravars': extravars,
'fact_cache': ansible_fact_path,
'fact_cache_type': 'jsonfile',
'artifact_dir': ansible_artifact_path,
'rotate_artifacts': 256
}
if skip_tags:
cmd.extend(['--skip_tags %s' % skip_tags])
r_opts['skip_tags'] = skip_tags
if python_interpreter:
cmd.extend([
'--extra-vars',
'ansible_python_interpreter=%s' % python_interpreter
])
if tags:
r_opts['tags'] = tags
if extra_vars:
if isinstance(extra_vars, dict) and extra_vars:
cmd.extend(['--extra-vars', '%s' % convert(extra_vars)])
elif os.path.exists(extra_vars) and os.path.isfile(extra_vars):
# We don't need to check if the content of the file is
# a valid YAML or JSON, the ansible-playbook command
# will do it better
cmd.extend(['--extra-vars', '@{}'.format(extra_vars)])
if limit_hosts:
r_opts['limit'] = limit_hosts
runner_config = ansible_runner.runner_config.RunnerConfig(**r_opts)
runner_config.prepare()
runner = ansible_runner.Runner(config=runner_config)
status, rc = runner.run()
if verbosity > 1:
status = runner.stdout
if rc == 0:
LOG.info(
'Ansible execution success. playbook: {}'.format(
playbook
)
)
return rc, status
else:
raise RuntimeError('No such extra vars file: %s' % extra_vars)
cmd.extend(['-c', connection, play])
proc = run_command_and_log(logger, cmd, env=env, retcode_only=False)
proc.wait()
cleanup and os.unlink(tmp_config)
if proc.returncode != 0:
raise RuntimeError(proc.stdout.read())
return proc.returncode, proc.stdout.read()
else:
cleanup and os.unlink(tmp_config)
raise RuntimeError('No such playbook: %s' % play)
err_msg = (
'Ansible execution failed. playbook: {},'
' Run Status: {},'
' Return Code: {}'.format(
playbook,
status,
rc
)
)
if not quiet:
LOG.error(err_msg)
raise RuntimeError(err_msg)
def convert(data):
@ -398,13 +647,7 @@ def store_cli_param(command_name, parsed_args):
command_name = command_name.replace(" ", "-")
history_path = os.path.join(os.path.expanduser("~"), '.tripleo')
if not os.path.exists(history_path):
try:
os.mkdir(history_path)
except OSError as e:
messages = _("Unable to create TripleO history directory: "
"{0}, {1}").format(history_path, e)
raise OSError(messages)
makedirs(history_path)
if os.path.isdir(history_path):
try:
with open(os.path.join(history_path,
@ -1190,14 +1433,14 @@ def run_update_ansible_action(log, clients, nodes, inventory,
tags=tags, skip_tags=skip_tags,
verbosity=verbosity, extra_vars=extra_vars)
else:
run_ansible_playbook(logger=LOG,
workdir=workdir,
playbook=book,
run_ansible_playbook(playbook=book,
inventory=inventory,
workdir=workdir,
ssh_user=ssh_user,
key=ssh_private_key(workdir, priv_key),
module_path='/usr/share/ansible-modules',
limit_hosts=nodes, tags=tags,
limit_hosts=nodes,
tags=tags,
skip_tags=skip_tags)
@ -1339,24 +1582,14 @@ def bulk_symlink(log, src, dst, tmpd='/tmp'):
"""
log.debug("Symlinking %s to %s, via temp dir %s" %
(src, dst, tmpd))
tmp = None
try:
if not os.path.exists(tmpd):
raise exceptions.NotFound("{} does not exist. Cannot create a "
"temp folder using this path".format(
tmpd))
tmp = tempfile.mkdtemp(dir=tmpd)
subprocess.check_call(['mkdir', '-p', dst])
os.chmod(tmp, 0o755)
makedirs(dst)
with TempDirs(dir_path=tmpd) as tmp:
for obj in os.listdir(src):
if not os.path.exists(os.path.join(dst, obj)):
tmpf = os.path.join(tmp, obj)
os.symlink(os.path.join(src, obj), tmpf)
os.rename(tmpf, os.path.join(dst, obj))
except Exception:
raise
finally:
if tmp:
shutil.rmtree(tmp, ignore_errors=True)
def run_command_and_log(log, cmd, cwd=None, env=None, retcode_only=True):
@ -1450,12 +1683,13 @@ def build_prepare_env(environment_files, environment_directories):
env_url = heat_utils.normalise_file_path_to_url(path)
return request.urlopen(env_url).read()
env_f, env = (
return (
template_utils.process_multiple_environments_and_files(
env_files, env_path_is_object=lambda path: True,
object_request=get_env_file))
return env
env_files,
env_path_is_object=lambda path: True,
object_request=get_env_file
)
)[1]
def rel_or_abs_path(file_path, tht_root):
@ -1812,7 +2046,7 @@ def parse_all_validations_on_disk(path, groups=None):
validations_abspath = glob.glob("{path}/*.yaml".format(path=path))
for pl in validations_abspath:
validation_id, ext = os.path.splitext(os.path.basename(pl))
validation_id, _ext = os.path.splitext(os.path.basename(pl))
with open(pl, 'r') as val_playbook:
contents = yaml.safe_load(val_playbook)
@ -1910,23 +2144,6 @@ def get_local_timezone():
return timezone
def ansible_symlink():
# https://bugs.launchpad.net/tripleo/+bug/1812837
python_version = sys.version_info[0]
ansible_playbook_cmd = "ansible-playbook-{}".format(python_version)
cmd = ['sudo', 'ln', '-s']
if not os.path.exists('/usr/bin/ansible-playbook'):
if os.path.exists('/usr/bin/' + ansible_playbook_cmd):
cmd.extend(['/usr/bin/' + ansible_playbook_cmd,
'/usr/bin/ansible-playbook'])
run_command(cmd, name='ansible-playbook-symlink')
else:
if not os.path.exists('/usr/bin/' + ansible_playbook_cmd):
cmd.extend(['/usr/bin/ansible-playbook',
'/usr/bin/' + ansible_playbook_cmd])
run_command(cmd, name='ansible-playbook-3-symlink')
def check_file_for_enabled_service(env_file):
"""Checks environment file for the said service.
@ -1979,7 +2196,7 @@ def reset_cmdline():
output = ''
try:
output = run_command(['reset', '-I'])
except RuntimeError as e:
except RuntimeError:
LOG.warning('Unable to reset command line. Try manually running '
'"reset" if the command line is broken.')
sys.stdout.write(output)
@ -2003,7 +2220,7 @@ def safe_write(path, data):
f.write(data)
except OSError as error:
if error.errno != errno.EEXIST:
msg = _('The output file %(file)s can not be '
'created. Error: %(msg)') % {'file': path,
'msg': error.message}
msg = _(
'The output file {file} can not be created. Error: {msg}'
).format(file=path, msg=str(error))
raise oscexc.CommandError(msg)

View File

@ -194,14 +194,11 @@ class BuildImage(command.Command):
tmp.write('list_dependencies=true')
kolla_config_files = list(parsed_args.kolla_config_files)
kolla_config_files.append(path)
kolla_tmp_dir = None
if parsed_args.use_buildah:
# A temporary directory is needed to let Kolla generates the
# Dockerfiles that will be used by Buildah to build the images.
kolla_tmp_dir = tempfile.mkdtemp(prefix='kolla-')
with utils.TempDirs(dir_prefix='kolla-') as kolla_tmp_dir:
try:
builder = kolla_builder.KollaImageBuilder(parsed_args.config_files)
builder = kolla_builder.KollaImageBuilder(
parsed_args.config_files
)
result = builder.build_images(kolla_config_files,
parsed_args.excludes,
parsed_args.use_buildah,
@ -221,14 +218,21 @@ class BuildImage(command.Command):
bb.build_all()
elif parsed_args.list_dependencies:
deps = json.loads(result)
yaml.safe_dump(deps, self.app.stdout, indent=2,
default_flow_style=False)
yaml.safe_dump(
deps,
self.app.stdout,
indent=2,
default_flow_style=False
)
elif parsed_args.list_images:
deps = json.loads(result)
images = []
BuildImage.images_from_deps(images, deps)
yaml.safe_dump(images, self.app.stdout,
default_flow_style=False)
yaml.safe_dump(
images,
self.app.stdout,
default_flow_style=False
)
elif result:
self.app.stdout.write(result)
finally:

View File

@ -148,8 +148,7 @@ def prepare_minion_deploy(upgrade=False, no_validations=False,
# we move any user provided environment files into this root later.
tempdir = os.path.join(os.path.abspath(CONF['output_dir']),
'tripleo-config-generated-env-files')
if not os.path.isdir(tempdir):
os.mkdir(tempdir)
utils.makedirs(tempdir)
env_data['PythonInterpreter'] = sys.executable
@ -275,8 +274,7 @@ def prepare_minion_deploy(upgrade=False, no_validations=False,
deploy_args += ['--deployment-user', u]
deploy_args += ['--output-dir=%s' % CONF['output_dir']]
if not os.path.isdir(CONF['output_dir']):
os.mkdir(CONF['output_dir'])
utils.makedirs(CONF['output_dir'])
# TODO(aschultz): move this to a central class
if CONF.get('net_config_override', None):
@ -345,7 +343,6 @@ def prepare_minion_deploy(upgrade=False, no_validations=False,
utils.set_hostname(CONF.get('minion_hostname'))
if CONF.get('minion_enable_validations') and not no_validations:
utils.ansible_symlink()
undercloud_preflight.minion_check(verbose_level, upgrade)
if CONF.get('custom_env_files'):

View File

@ -19,6 +19,7 @@ from osc_lib.i18n import _
from oslo_concurrency import processutils
from tripleoclient import command
from tripleoclient import utils
from tripleoclient.workflows import deployment
@ -76,8 +77,7 @@ class DownloadConfig(command.Command):
str(e))
raise OSError(message)
if not os.path.exists(config_dir):
os.makedirs(config_dir)
utils.makedirs(config_dir)
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)

View File

@ -24,7 +24,6 @@ import re
import shutil
import six
import subprocess
import tempfile
import time
import yaml
@ -167,8 +166,7 @@ class DeployOvercloud(command.Command):
user_env_path = os.path.join(
user_env_dir, os.path.basename(abs_env_path))
self.log.debug("user_env_path=%s" % user_env_path)
if not os.path.exists(user_env_dir):
os.makedirs(user_env_dir)
utils.makedirs(user_env_dir)
with open(user_env_path, 'w') as f:
self.log.debug("Writing user environment %s" % user_env_path)
f.write(contents)
@ -350,8 +348,7 @@ class DeployOvercloud(command.Command):
if not os.path.isfile(file_path):
self.log.debug("Missing in templates directory, downloading \
%s from swift into %s" % (pf, file_path))
if not os.path.exists(os.path.dirname(file_path)):
os.makedirs(os.path.dirname(file_path))
utils.makedirs(os.path.dirname(file_path))
# open in binary as the swiftclient get/put error under
# python3 if opened as Text I/O
with open(file_path, 'wb') as f:
@ -361,20 +358,16 @@ class DeployOvercloud(command.Command):
# copy tht_root to temporary directory because we need to
# download any missing (e.g j2 rendered) files from the plan
tht_root = os.path.abspath(parsed_args.templates)
tht_tmp = tempfile.mkdtemp(prefix='tripleoclient-')
with utils.TempDirs(dir_prefix='tripleoclient-',
cleanup=(not parsed_args.no_cleanup)) as tht_tmp:
new_tht_root = "%s/tripleo-heat-templates" % tht_tmp
self.log.debug("Creating temporary templates tree in %s"
% new_tht_root)
try:
self.log.debug(
"Creating temporary templates tree in %s" % new_tht_root
)
shutil.copytree(tht_root, new_tht_root, symlinks=True)
self._deploy_tripleo_heat_templates(stack, parsed_args,
new_tht_root, tht_root)
finally:
if parsed_args.no_cleanup:
self.log.warning("Not cleaning temporary directory %s"
% tht_tmp)
else:
shutil.rmtree(tht_tmp)
def _deploy_tripleo_heat_templates(self, stack, parsed_args,
tht_root, user_tht_root):

View File

@ -103,7 +103,7 @@ class Deploy(command.Command):
deployment_user = None
ansible_dir = None
python_version = sys.version_info[0]
ansible_playbook_cmd = "ansible-playbook-{}".format(python_version)
ansible_playbook_cmd = "ansible-playbook"
python_cmd = "python{}".format(python_version)
def _is_undercloud_deploy(self, parsed_args):
@ -256,13 +256,12 @@ class Deploy(command.Command):
def _create_persistent_dirs(self):
"""Creates temporary working directories"""
if not os.path.exists(constants.STANDALONE_EPHEMERAL_STACK_VSTATE):
os.mkdir(constants.STANDALONE_EPHEMERAL_STACK_VSTATE)
utils.makedirs(constants.STANDALONE_EPHEMERAL_STACK_VSTATE)
def _create_working_dirs(self, stack_name='undercloud'):
"""Creates temporary working directories"""
if self.output_dir and not os.path.exists(self.output_dir):
os.mkdir(self.output_dir)
if self.output_dir:
utils.makedirs(self.output_dir)
if not self.tht_render:
self.tht_render = os.path.join(self.output_dir,
'tripleo-heat-installer-templates')
@ -901,27 +900,6 @@ class Deploy(command.Command):
yaml.safe_dump(output, f, default_flow_style=False)
return output
# Never returns, calls exec()
def _launch_ansible(self, ansible_dir, extra_args=None,
operation="deploy"):
if operation not in constants.DEPLOY_ANSIBLE_ACTIONS.keys():
self.log.error(_('Operation %s is not allowed') % operation)
raise exceptions.DeploymentError('Invalid operation to run in '
'ansible.')
list_args = constants.DEPLOY_ANSIBLE_ACTIONS[operation].split()
if extra_args:
list_args.extend(extra_args)
self.log.warning(_('** Running ansible %s tasks **') % operation)
os.chdir(ansible_dir)
playbook_inventory = os.path.join(ansible_dir, 'inventory.yaml')
cmd = [self.ansible_playbook_cmd, '-i', playbook_inventory] + list_args
self.log.debug('Running Ansible %s tasks: %s' % (operation, ' '
.join(cmd)))
return utils.run_command_and_log(self.log, cmd)
def get_parser(self, prog_name):
parser = argparse.ArgumentParser(
description=self.get_description(),
@ -1303,38 +1281,46 @@ class Deploy(command.Command):
_('Using the existing %s for deployment') % ansible_config)
shutil.copy(ansible_config, self.ansible_dir)
extra_args = []
extra_args = dict()
if not parsed_args.inflight:
extra_args = ['--skip-tags', 'opendev-validation']
extra_args = {'skip_tags': 'opendev-validation'}
# Kill heat, we're done with it now.
if not parsed_args.keep_running:
self._kill_heat(parsed_args)
if not parsed_args.output_only:
operations = list()
if parsed_args.upgrade:
# Run Upgrade tasks before the deployment
rc = self._launch_ansible(self.ansible_dir,
operation='upgrade',
extra_args=extra_args)
if rc != 0:
raise exceptions.DeploymentError('Upgrade failed')
rc = self._launch_ansible(self.ansible_dir,
extra_args=extra_args)
if rc != 0:
raise exceptions.DeploymentError('Deployment failed')
operations.append(
constants.DEPLOY_ANSIBLE_ACTIONS['upgrade']
)
operations.append(
constants.DEPLOY_ANSIBLE_ACTIONS['deploy']
)
if parsed_args.upgrade:
# Run Post Upgrade tasks after the deployment
rc = self._launch_ansible(self.ansible_dir,
operation='post-upgrade',
extra_args=extra_args)
if rc != 0:
raise exceptions.DeploymentError('Post Upgrade failed')
operations.append(
constants.DEPLOY_ANSIBLE_ACTIONS['post-upgrade']
)
# Run Online Upgrade tasks after the deployment
rc = self._launch_ansible(self.ansible_dir,
operation='online-upgrade',
extra_args=extra_args)
if rc != 0:
raise exceptions.DeploymentError(
'Online Upgrade failed')
operations.append(
constants.DEPLOY_ANSIBLE_ACTIONS['online-upgrade']
)
with utils.Pushd(self.ansible_dir):
for operation in operations:
for k, v in extra_args.items():
if k in operation:
operation[k] = ','.join([operation[k], v])
else:
operation[k] = v
rc = utils.run_ansible_playbook(
inventory=os.path.join(
self.ansible_dir,
'inventory.yaml'
),
workdir=self.ansible_dir,
**operation
)[0]
except Exception as e:
self.log.error("Exception: %s" % six.text_type(e))
self.log.error(traceback.print_exc())
@ -1415,7 +1401,6 @@ class Deploy(command.Command):
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
utils.ansible_symlink()
unconf_msg = _('User did not confirm upgrade, so exiting. '
'Consider using the --yes parameter if you '
'prefer to skip this warning in the future')

View File

@ -17,7 +17,6 @@ import argparse
import json
import logging
import os
import pwd
import six
import sys
import textwrap
@ -363,23 +362,6 @@ class TripleOValidatorRun(command.Command):
return parser
def _run_ansible(self, logger, plan, workdir, log_path_dir, playbook,
inventory, retries, output_callback, extra_vars,
python_interpreter, gathering_policy):
rc, output = oooutils.run_ansible_playbook(
logger=logger,
plan=plan,
workdir=workdir,
log_path_dir=log_path_dir,
playbook=playbook,
inventory=inventory,
retries=retries,
output_callback=output_callback,
extra_vars=extra_vars,
python_interpreter=python_interpreter,
gathering_policy=gathering_policy)
return rc, output
def _run_validator_run(self, parsed_args):
LOG = logging.getLogger(__name__ + ".ValidationsRunAnsible")
playbooks = []
@ -407,9 +389,6 @@ class TripleOValidatorRun(command.Command):
for pb in parsed_args.validation_name:
playbooks.append(pb + '.yaml')
python_interpreter = \
"/usr/bin/python{}".format(sys.version_info[0])
static_inventory = oooutils.get_tripleo_ansible_inventory(
ssh_user='heat-admin',
stack=parsed_args.plan,
@ -418,28 +397,27 @@ class TripleOValidatorRun(command.Command):
failed_val = False
with ThreadPoolExecutor(max_workers=parsed_args.workers) as executor:
with oooutils.TempDirs() as tmp:
with ThreadPoolExecutor(max_workers=parsed_args.workers) as exe:
LOG.debug(_('Running the validations with Ansible'))
tasks_exec = {
executor.submit(
self._run_ansible,
logger=LOG,
exe.submit(
oooutils.run_ansible_playbook,
plan=parsed_args.plan,
workdir=constants.ANSIBLE_VALIDATION_DIR,
log_path_dir=pwd.getpwuid(os.getuid()).pw_dir,
workdir=tmp,
playbook=playbook,
playbook_dir=constants.ANSIBLE_VALIDATION_DIR,
inventory=static_inventory,
retries=False,
output_callback='validation_output',
quiet=True,
extra_vars=extra_vars_input,
python_interpreter=python_interpreter,
gathering_policy='explicit'): playbook
for playbook in playbooks
}
for tk, pl in six.iteritems(tasks_exec):
try:
rc, output = tk.result()
_rc, output = tk.result()
print('[SUCCESS] - {}\n{}'.format(pl, oooutils.indent(output)))
except Exception as e:
failed_val = True

View File

@ -417,8 +417,7 @@ def prepare_undercloud_deploy(upgrade=False, no_validations=True,
# we move any user provided environment files into this root later.
tempdir = os.path.join(os.path.abspath(CONF['output_dir']),
'tripleo-config-generated-env-files')
if not os.path.isdir(tempdir):
os.mkdir(tempdir)
utils.makedirs(tempdir)
# Set the undercloud home dir parameter so that stackrc is produced in
# the users home directory.
@ -705,8 +704,7 @@ def prepare_undercloud_deploy(upgrade=False, no_validations=True,
deploy_args += ['--deployment-user', u]
deploy_args += ['--output-dir=%s' % CONF['output_dir']]
if not os.path.isdir(CONF['output_dir']):
os.mkdir(CONF['output_dir'])
utils.makedirs(CONF['output_dir'])
if CONF.get('cleanup'):
deploy_args.append('--cleanup')
@ -782,7 +780,6 @@ def prepare_undercloud_deploy(upgrade=False, no_validations=True,
deploy_args += ['--hieradata-override=%s' % data_file]
if CONF.get('enable_validations') and not no_validations:
utils.ansible_symlink()
undercloud_preflight.check(verbose_level, upgrade)
deploy_args += ['-e', os.path.join(
tht_templates, "environments/tripleo-validations.yaml")]

View File

@ -84,19 +84,19 @@ def _check_diskspace(upgrade=False):
Second one checks minimal disk space for an upgrade.
"""
if upgrade:
playbook = 'undercloud-disk-space-pre-upgrade.yaml'
playbook_args = constants.DEPLOY_ANSIBLE_ACTIONS['preflight-upgrade']
else:
playbook = 'undercloud-disk-space.yaml'
playbook_args = constants.DEPLOY_ANSIBLE_ACTIONS['preflight-deploy']
python_interpreter = "/usr/bin/python{}".format(sys.version_info[0])
utils.run_ansible_playbook(logger=LOG,
workdir=constants.ANSIBLE_VALIDATION_DIR,
playbook=playbook,
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
workdir=tmp,
inventory='undercloud,',
retries=False,
connection='local',
output_callback='validation_output',
python_interpreter=python_interpreter)
playbook_dir=constants.ANSIBLE_VALIDATION_DIR,
**playbook_args
)
def _check_memory():

View File

@ -14,10 +14,8 @@ from __future__ import print_function
import copy
import os
import pprint
import shutil
import socket
import subprocess
import tempfile
import time
from heatclient.common import event_utils
@ -219,7 +217,7 @@ def get_hosts_and_enable_ssh_admin(log, clients, stack, overcloud_ssh_network,
"Check if the user/ip are corrects.\n".format(hosts))
else:
log.error("Unknown error. "
"Original message is:\n{}".format(hosts, e.message))
"Original message is:\n{} {}".format(hosts, e))
else:
raise exceptions.DeploymentError("Cannot find any hosts on '{}'"
@ -239,12 +237,11 @@ def enable_ssh_admin(log, clients, plan_name, hosts, ssh_user, ssh_key):
"-o StrictHostKeyChecking=no "
"-o PasswordAuthentication=no "
"-o UserKnownHostsFile=/dev/null")
tmp_key_dir = tempfile.mkdtemp()
with utils.TempDirs() as tmp_key_dir:
tmp_key_private = os.path.join(tmp_key_dir, 'id_rsa')
tmp_key_public = os.path.join(tmp_key_dir, 'id_rsa.pub')
tmp_key_comment = "TripleO split stack short term key"
try:
tmp_key_command = ["ssh-keygen", "-N", "", "-t", "rsa", "-b", "4096",
"-f", tmp_key_private, "-C", tmp_key_comment]
DEVNULL = open(os.devnull, 'w')
@ -324,9 +321,6 @@ def enable_ssh_admin(log, clients, plan_name, hosts, ssh_user, ssh_key):
tmp_key_comment]
print("Removing TripleO short term key from %s" % host)
subprocess.check_call(rm_tmp_key_command, stderr=subprocess.STDOUT)
finally:
print("Removing short term keys locally")
shutil.rmtree(tmp_key_dir)
print("Enabling ssh admin - COMPLETE.")

View File

@ -20,6 +20,7 @@ from osc_lib.i18n import _
from tripleoclient.exceptions import ContainerDeleteFailed
from tripleoclient.exceptions import DownloadError
from tripleoclient.exceptions import LogFetchError
from tripleoclient import utils
from tripleoclient.workflows import base
@ -44,9 +45,10 @@ def download_files(clients, container_name, destination):
if not os.path.dirname(destination):
destination = os.path.join(os.sep, os.getcwd(), destination)
if not os.path.exists(destination):
print('Creating destination path: {}'.format(destination))
os.makedirs(destination)
if utils.makedirs(destination):
print('Created destination path: {}'.format(destination))
else:
print('Destination path exists: {}'.format(destination))
if not check_local_space(destination, object_list):
raise DownloadError(_('Not enough local space to download files.'))