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:
parent
29b00170b3
commit
bcc9c66747
|
@ -1,5 +1,6 @@
|
||||||
alembic==0.8.10
|
alembic==0.8.10
|
||||||
amqp==2.1.1
|
amqp==2.1.1
|
||||||
|
ansible-runner===1.4.4
|
||||||
aodhclient==0.9.0
|
aodhclient==0.9.0
|
||||||
appdirs==1.3.0
|
appdirs==1.3.0
|
||||||
asn1crypto==0.23.0
|
asn1crypto==0.23.0
|
||||||
|
|
|
@ -19,3 +19,4 @@ websocket-client>=0.44.0 # LGPLv2+
|
||||||
tripleo-common>=11.3.1 # Apache-2.0
|
tripleo-common>=11.3.1 # Apache-2.0
|
||||||
cryptography>=2.1 # BSD/Apache-2.0
|
cryptography>=2.1 # BSD/Apache-2.0
|
||||||
futures>=3.0.0;python_version=='2.7' or python_version=='2.6' # BSD
|
futures>=3.0.0;python_version=='2.7' or python_version=='2.6' # BSD
|
||||||
|
ansible-runner>=1.4.4 # Apache 2.0
|
||||||
|
|
|
@ -102,16 +102,33 @@ CTLPLANE_INSPECTION_IPRANGE_DEFAULT = '192.168.24.100,192.168.24.120'
|
||||||
CTLPLANE_GATEWAY_DEFAULT = '192.168.24.1'
|
CTLPLANE_GATEWAY_DEFAULT = '192.168.24.1'
|
||||||
CTLPLANE_DNS_NAMESERVERS_DEFAULT = []
|
CTLPLANE_DNS_NAMESERVERS_DEFAULT = []
|
||||||
|
|
||||||
# Ansible parameters used for the actions being
|
# Ansible parameters used for the actions being executed during tripleo
|
||||||
# executed during tripleo deploy/upgrade.
|
# 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_ANSIBLE_ACTIONS = {
|
||||||
'deploy': 'deploy_steps_playbook.yaml',
|
'deploy': {
|
||||||
'upgrade': 'upgrade_steps_playbook.yaml --skip-tags '
|
'playbook': 'deploy_steps_playbook.yaml'
|
||||||
'validation',
|
},
|
||||||
'post-upgrade': 'post_upgrade_steps_playbook.yaml '
|
'upgrade': {
|
||||||
'--skip-tags validation',
|
'playbook': 'upgrade_steps_playbook.yaml',
|
||||||
'online-upgrade': 'external_upgrade_steps_playbook.yaml '
|
'skip_tags': 'validation'
|
||||||
'--tags online_upgrade',
|
},
|
||||||
|
'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
|
# Key-value pair of deprecated service and its warning message
|
||||||
|
|
|
@ -80,3 +80,8 @@ class FakeClientWrapper(object):
|
||||||
|
|
||||||
def messaging_websocket(self):
|
def messaging_websocket(self):
|
||||||
return self.ws
|
return self.ws
|
||||||
|
|
||||||
|
|
||||||
|
def fake_ansible_runner_run_return(rc=0):
|
||||||
|
|
||||||
|
return 'Test Status', rc
|
||||||
|
|
|
@ -38,9 +38,13 @@ import yaml
|
||||||
from tripleoclient import exceptions
|
from tripleoclient import exceptions
|
||||||
from tripleoclient import utils
|
from tripleoclient import utils
|
||||||
|
|
||||||
|
from tripleoclient.tests import fakes
|
||||||
|
|
||||||
from six.moves.configparser import ConfigParser
|
from six.moves.configparser import ConfigParser
|
||||||
from six.moves.urllib import error as url_error
|
from six.moves.urllib import error as url_error
|
||||||
|
|
||||||
|
from ansible_runner import Runner
|
||||||
|
|
||||||
|
|
||||||
class TestRunAnsiblePlaybook(TestCase):
|
class TestRunAnsiblePlaybook(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -48,329 +52,130 @@ class TestRunAnsiblePlaybook(TestCase):
|
||||||
self.addCleanup(self.unlink_patch.stop)
|
self.addCleanup(self.unlink_patch.stop)
|
||||||
self.unlink_patch.start()
|
self.unlink_patch.start()
|
||||||
self.mock_log = mock.Mock('logging.getLogger')
|
self.mock_log = mock.Mock('logging.getLogger')
|
||||||
python_version = sys.version_info[0]
|
self.ansible_playbook_cmd = "ansible-playbook"
|
||||||
self.ansible_playbook_cmd = "ansible-playbook-%s" % (python_version)
|
|
||||||
|
|
||||||
@mock.patch('os.path.exists', return_value=False)
|
@mock.patch('os.path.exists', return_value=False)
|
||||||
@mock.patch('tripleoclient.utils.run_command_and_log')
|
@mock.patch('tripleoclient.utils.run_command_and_log')
|
||||||
def test_no_playbook(self, mock_run, mock_exists):
|
def test_no_playbook(self, mock_run, mock_exists):
|
||||||
self.assertRaises(RuntimeError,
|
self.assertRaises(
|
||||||
utils.run_ansible_playbook,
|
RuntimeError,
|
||||||
self.mock_log,
|
utils.run_ansible_playbook,
|
||||||
'/tmp',
|
'non-existing.yaml',
|
||||||
'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_run.assert_not_called()
|
||||||
|
|
||||||
@mock.patch('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
|
@mock.patch('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
|
||||||
@mock.patch('os.path.exists', return_value=True)
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
@mock.patch('tripleoclient.utils.run_command_and_log')
|
@mock.patch('os.makedirs')
|
||||||
def test_subprocess_error(self, mock_run, mock_exists, mock_mkstemp):
|
@mock.patch.object(
|
||||||
mock_process = mock.Mock()
|
Runner,
|
||||||
mock_process.returncode = 1
|
'run',
|
||||||
mock_process.stdout.read.side_effect = ["Error\n"]
|
return_value=fakes.fake_ansible_runner_run_return(rc=1)
|
||||||
mock_run.return_value = mock_process
|
)
|
||||||
|
def test_subprocess_error(self, mock_run, mock_mkdirs, mock_exists,
|
||||||
env = os.environ.copy()
|
mock_mkstemp):
|
||||||
env['ANSIBLE_LIBRARY'] = \
|
self.assertRaises(
|
||||||
('/root/.ansible/plugins/modules:'
|
RuntimeError,
|
||||||
'/usr/share/ansible/plugins/modules:'
|
utils.run_ansible_playbook,
|
||||||
'/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,
|
|
||||||
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',
|
'existing.yaml',
|
||||||
'localhost,',
|
'localhost,',
|
||||||
ansible_config='/tmp/foo.cfg')
|
'/tmp'
|
||||||
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)
|
|
||||||
|
|
||||||
@mock.patch('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
|
@mock.patch('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
|
||||||
@mock.patch('os.path.exists', return_value=True)
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
@mock.patch('tripleoclient.utils.run_command_and_log')
|
@mock.patch('os.makedirs')
|
||||||
def test_run_success_connection_local(self, mock_run, mock_exists,
|
@mock.patch.object(
|
||||||
mok_mkstemp):
|
Runner,
|
||||||
mock_process = mock.Mock()
|
'run',
|
||||||
mock_process.returncode = 0
|
return_value=fakes.fake_ansible_runner_run_return()
|
||||||
mock_run.return_value = mock_process
|
)
|
||||||
|
def test_run_success_default(self, mock_run, mock_mkdirs, mock_exists,
|
||||||
|
mock_mkstemp):
|
||||||
retcode, output = utils.run_ansible_playbook(
|
retcode, output = utils.run_ansible_playbook(
|
||||||
self.mock_log,
|
playbook='existing.yaml',
|
||||||
'/tmp',
|
inventory='localhost,',
|
||||||
'existing.yaml',
|
workdir='/tmp'
|
||||||
'localhost,',
|
)
|
||||||
connection='local')
|
|
||||||
self.assertEqual(retcode, 0)
|
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,
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
[self.ansible_playbook_cmd,
|
@mock.patch('os.makedirs')
|
||||||
'-u', 'root',
|
@mock.patch.object(
|
||||||
'-i', 'localhost,', '-v',
|
Runner,
|
||||||
'-c', 'local',
|
'run',
|
||||||
'/tmp/existing.yaml'],
|
return_value=fakes.fake_ansible_runner_run_return()
|
||||||
env=env, retcode_only=False)
|
)
|
||||||
|
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('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
|
||||||
@mock.patch('os.path.exists', return_value=True)
|
@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,
|
def test_run_success_gathering_policy(self, mock_run, mock_exists,
|
||||||
mok_mkstemp):
|
mock_mkstemp, mock_makedirs):
|
||||||
mock_process = mock.Mock()
|
|
||||||
mock_process.returncode = 0
|
|
||||||
mock_run.return_value = mock_process
|
|
||||||
|
|
||||||
retcode, output = utils.run_ansible_playbook(
|
retcode, output = utils.run_ansible_playbook(
|
||||||
self.mock_log,
|
playbook='existing.yaml',
|
||||||
'/tmp',
|
inventory='localhost,',
|
||||||
'existing.yaml',
|
workdir='/tmp',
|
||||||
'localhost,',
|
connection='local',
|
||||||
gathering_policy='explicit')
|
gathering_policy='smart'
|
||||||
|
)
|
||||||
self.assertEqual(retcode, 0)
|
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('tempfile.mkstemp', return_value=('foo', '/tmp/fooBar.cfg'))
|
||||||
@mock.patch('os.path.exists', return_value=True)
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
@mock.patch('tripleoclient.utils.run_command_and_log')
|
@mock.patch.object(
|
||||||
def test_run_success_extra_vars(self, mock_run, mock_exists, mock_mkstemp):
|
Runner,
|
||||||
mock_process = mock.Mock()
|
'run',
|
||||||
mock_process.returncode = 0
|
return_value=fakes.fake_ansible_runner_run_return()
|
||||||
mock_run.return_value = mock_process
|
)
|
||||||
|
def test_run_success_extra_vars(self, mock_run, mock_exists, mock_mkstemp,
|
||||||
|
mock_makedirs):
|
||||||
arglist = {
|
arglist = {
|
||||||
'var_one': 'val_one',
|
'var_one': 'val_one',
|
||||||
}
|
}
|
||||||
|
|
||||||
retcode, output = utils.run_ansible_playbook(
|
retcode, output = utils.run_ansible_playbook(
|
||||||
self.mock_log,
|
playbook='existing.yaml',
|
||||||
'/tmp',
|
inventory='localhost,',
|
||||||
'existing.yaml',
|
workdir='/tmp',
|
||||||
'localhost,',
|
connection='local',
|
||||||
extra_vars=arglist)
|
gathering_policy='smart',
|
||||||
|
extra_vars=arglist
|
||||||
|
)
|
||||||
self.assertEqual(retcode, 0)
|
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):
|
class TestRunCommandAndLog(TestCase):
|
||||||
|
@ -1170,14 +975,6 @@ class TestStoreCliParam(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.args = argparse.ArgumentParser()
|
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.isdir')
|
||||||
@mock.patch('os.path.exists')
|
@mock.patch('os.path.exists')
|
||||||
def test_exists_but_not_dir(self, mock_exists, mock_isdir):
|
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())
|
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):
|
class TestGetParamFieldName(TestCase):
|
||||||
def test_with_empty_val_data(self):
|
def test_with_empty_val_data(self):
|
||||||
input_parameter = {}
|
input_parameter = {}
|
||||||
|
|
|
@ -262,9 +262,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
||||||
'UndercloudHostsEntries':
|
'UndercloudHostsEntries':
|
||||||
['192.168.0.1 uc.ctlplane.localhost uc.ctlplane']}}
|
['192.168.0.1 uc.ctlplane.localhost uc.ctlplane']}}
|
||||||
|
|
||||||
mock_rm = shutil.rmtree = mock.MagicMock()
|
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
mock_rm.assert_not_called()
|
|
||||||
|
|
||||||
self.assertFalse(orchestration_client.stacks.create.called)
|
self.assertFalse(orchestration_client.stacks.create.called)
|
||||||
|
|
||||||
|
@ -309,6 +307,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
||||||
self.assertEqual(env_map.get('parameter_defaults'),
|
self.assertEqual(env_map.get('parameter_defaults'),
|
||||||
parameters_env.get('parameter_defaults'))
|
parameters_env.get('parameter_defaults'))
|
||||||
|
|
||||||
|
@mock.patch('os.chdir')
|
||||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||||
'_get_undercloud_host_entry', autospec=True,
|
'_get_undercloud_host_entry', autospec=True,
|
||||||
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
|
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_postconfig, mock_shutil_rmtree,
|
||||||
mock_invoke_plan_env_wf,
|
mock_invoke_plan_env_wf,
|
||||||
mock_stack_network_check,
|
mock_stack_network_check,
|
||||||
mock_get_undercloud_host_entry):
|
mock_get_undercloud_host_entry,
|
||||||
|
mock_chdir):
|
||||||
fixture = deployment.DeploymentWorkflowFixture()
|
fixture = deployment.DeploymentWorkflowFixture()
|
||||||
self.useFixture(fixture)
|
self.useFixture(fixture)
|
||||||
plane_management_fixture = deployment.PlanManagementFixture()
|
plane_management_fixture = deployment.PlanManagementFixture()
|
||||||
|
@ -446,6 +446,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
||||||
clients.tripleoclient.object_store.put_object.assert_called()
|
clients.tripleoclient.object_store.put_object.assert_called()
|
||||||
self.assertTrue(mock_invoke_plan_env_wf.called)
|
self.assertTrue(mock_invoke_plan_env_wf.called)
|
||||||
|
|
||||||
|
@mock.patch('os.chdir')
|
||||||
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
|
||||||
'_get_undercloud_host_entry', autospec=True,
|
'_get_undercloud_host_entry', autospec=True,
|
||||||
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
|
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_create_parameters_env, mock_validate_args,
|
||||||
mock_breakpoints_cleanup,
|
mock_breakpoints_cleanup,
|
||||||
mock_postconfig, mock_deprecated_params, mock_stack_network_check,
|
mock_postconfig, mock_deprecated_params, mock_stack_network_check,
|
||||||
mock_get_undercloud_host_entry):
|
mock_get_undercloud_host_entry,
|
||||||
|
mock_chdir):
|
||||||
fixture = deployment.DeploymentWorkflowFixture()
|
fixture = deployment.DeploymentWorkflowFixture()
|
||||||
self.useFixture(fixture)
|
self.useFixture(fixture)
|
||||||
plane_management_fixture = deployment.PlanManagementFixture()
|
plane_management_fixture = deployment.PlanManagementFixture()
|
||||||
|
@ -526,9 +528,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
||||||
|
|
||||||
mock_create_parameters_env.side_effect = _custom_create_params_env
|
mock_create_parameters_env.side_effect = _custom_create_params_env
|
||||||
|
|
||||||
mock_rm = shutil.rmtree = mock.MagicMock()
|
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
mock_rm.assert_called_once()
|
|
||||||
execution_calls = workflow_client.executions.create.call_args_list
|
execution_calls = workflow_client.executions.create.call_args_list
|
||||||
deploy_plan_call = execution_calls[1]
|
deploy_plan_call = execution_calls[1]
|
||||||
deploy_plan_call_input = deploy_plan_call[1]['workflow_input']
|
deploy_plan_call_input = deploy_plan_call[1]['workflow_input']
|
||||||
|
|
|
@ -968,17 +968,21 @@ class TestContainerImageBuild(TestPluginV1):
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
f, path = tempfile.mkstemp(dir=self.temp_dir)
|
f, path = tempfile.mkstemp(dir=self.temp_dir)
|
||||||
with mock.patch('tempfile.mkstemp') as mock_mkstemp:
|
with mock.patch('tempfile.mkdtemp') as mock_mkd:
|
||||||
mock_mkstemp.return_value = f, path
|
mock_mkd.return_value = '/tmp/testing'
|
||||||
self.cmd.take_action(parsed_args)
|
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)
|
||||||
|
|
||||||
mock_builder.assert_called_once_with([
|
mock_builder.assert_called_once_with([
|
||||||
'/tmp/foo.yaml', '/tmp/bar.yaml'])
|
'/tmp/foo.yaml', '/tmp/bar.yaml'])
|
||||||
mock_builder.return_value.build_images.assert_called_once_with([
|
mock_builder.return_value.build_images.assert_called_once_with([
|
||||||
self.default_kolla_conf, '/tmp/kolla.conf',
|
self.default_kolla_conf, '/tmp/kolla.conf',
|
||||||
path
|
path
|
||||||
], [], False, None)
|
], [], False, '/tmp/testing')
|
||||||
|
|
||||||
|
@mock.patch('os.chdir')
|
||||||
@mock.patch('os.fdopen', autospec=True)
|
@mock.patch('os.fdopen', autospec=True)
|
||||||
@mock.patch('tempfile.mkdtemp')
|
@mock.patch('tempfile.mkdtemp')
|
||||||
@mock.patch('tempfile.mkstemp')
|
@mock.patch('tempfile.mkstemp')
|
||||||
|
@ -997,7 +1001,8 @@ class TestContainerImageBuild(TestPluginV1):
|
||||||
mock_builder, mock_buildah,
|
mock_builder, mock_buildah,
|
||||||
mock_kolla_boolean_cfg,
|
mock_kolla_boolean_cfg,
|
||||||
mock_kolla_cfg, mock_mkstemp,
|
mock_kolla_cfg, mock_mkstemp,
|
||||||
mock_mkdtemp, mock_fdopen):
|
mock_mkdtemp, mock_fdopen,
|
||||||
|
mock_chdir):
|
||||||
arglist = [
|
arglist = [
|
||||||
'--config-file',
|
'--config-file',
|
||||||
'/tmp/bar.yaml',
|
'/tmp/bar.yaml',
|
||||||
|
@ -1056,16 +1061,19 @@ class TestContainerImageBuild(TestPluginV1):
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
f, path = tempfile.mkstemp(dir=self.temp_dir)
|
f, path = tempfile.mkstemp(dir=self.temp_dir)
|
||||||
with mock.patch('tempfile.mkstemp') as mock_mkstemp:
|
with mock.patch('tempfile.mkdtemp') as mock_mkd:
|
||||||
mock_mkstemp.return_value = f, path
|
mock_mkd.return_value = '/tmp/testing'
|
||||||
self.cmd.take_action(parsed_args)
|
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)
|
||||||
|
|
||||||
mock_builder.assert_called_once_with([
|
mock_builder.assert_called_once_with([
|
||||||
'/tmp/foo.yaml', '/tmp/bar.yaml'])
|
'/tmp/foo.yaml', '/tmp/bar.yaml'])
|
||||||
mock_builder.return_value.build_images.assert_called_once_with([
|
mock_builder.return_value.build_images.assert_called_once_with([
|
||||||
self.default_kolla_conf, '/tmp/kolla.conf',
|
self.default_kolla_conf, '/tmp/kolla.conf',
|
||||||
path
|
path
|
||||||
], ['foo', 'bar'], False, None)
|
], ['foo', 'bar'], False, '/tmp/testing')
|
||||||
|
|
||||||
@mock.patch('tripleo_common.image.kolla_builder.KollaImageBuilder',
|
@mock.patch('tripleo_common.image.kolla_builder.KollaImageBuilder',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
|
|
|
@ -24,11 +24,15 @@ from heatclient import exc as hc_exc
|
||||||
from tripleo_common.image import kolla_builder
|
from tripleo_common.image import kolla_builder
|
||||||
|
|
||||||
from tripleoclient import exceptions
|
from tripleoclient import exceptions
|
||||||
|
from tripleoclient.tests import fakes
|
||||||
from tripleoclient.tests.v1.test_plugin import TestPluginV1
|
from tripleoclient.tests.v1.test_plugin import TestPluginV1
|
||||||
|
|
||||||
# Load the plugin init module for the plugin list and show commands
|
# Load the plugin init module for the plugin list and show commands
|
||||||
from tripleoclient.v1 import tripleo_deploy
|
from tripleoclient.v1 import tripleo_deploy
|
||||||
|
|
||||||
|
import ansible_runner
|
||||||
|
|
||||||
|
|
||||||
# TODO(sbaker) Remove after a tripleo-common release contains this new function
|
# TODO(sbaker) Remove after a tripleo-common release contains this new function
|
||||||
if not hasattr(kolla_builder, 'container_images_prepare_multi'):
|
if not hasattr(kolla_builder, 'container_images_prepare_multi'):
|
||||||
setattr(kolla_builder, 'container_images_prepare_multi', mock.Mock())
|
setattr(kolla_builder, 'container_images_prepare_multi', mock.Mock())
|
||||||
|
@ -47,6 +51,7 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = tripleo_deploy.Deploy(self.app, None)
|
self.cmd = tripleo_deploy.Deploy(self.app, None)
|
||||||
|
self.cmd.ansible_dir = '/tmp'
|
||||||
|
|
||||||
tripleo_deploy.Deploy.heat_pid = mock.MagicMock(
|
tripleo_deploy.Deploy.heat_pid = mock.MagicMock(
|
||||||
return_value=False)
|
return_value=False)
|
||||||
|
@ -59,8 +64,7 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
self.orc.stacks.create = mock.MagicMock(
|
self.orc.stacks.create = mock.MagicMock(
|
||||||
return_value={'stack': {'id': 'foo'}})
|
return_value={'stack': {'id': 'foo'}})
|
||||||
|
|
||||||
python_version = sys.version_info[0]
|
self.ansible_playbook_cmd = "ansible-playbook"
|
||||||
self.ansible_playbook_cmd = "ansible-playbook-%s" % (python_version)
|
|
||||||
|
|
||||||
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy._is_undercloud_deploy')
|
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy._is_undercloud_deploy')
|
||||||
@mock.patch('tripleoclient.utils.check_hostname')
|
@mock.patch('tripleoclient.utils.check_hostname')
|
||||||
|
@ -833,37 +837,6 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
mock_inventory.write_static_inventory.assert_called_once_with(
|
mock_inventory.write_static_inventory.assert_called_once_with(
|
||||||
fake_output_dir + '/inventory.yaml', extra_vars)
|
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.'
|
@mock.patch('tripleo_common.image.kolla_builder.'
|
||||||
'container_images_prepare_multi')
|
'container_images_prepare_multi')
|
||||||
def test_prepare_container_images(self, mock_cipm):
|
def test_prepare_container_images(self, mock_cipm):
|
||||||
|
@ -886,6 +859,18 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
env
|
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.utils.reset_cmdline')
|
||||||
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
|
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
|
||||||
'_download_stack_outputs')
|
'_download_stack_outputs')
|
||||||
|
@ -903,8 +888,6 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
'_populate_templates_dir')
|
'_populate_templates_dir')
|
||||||
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
|
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
|
||||||
'_create_install_artifact', return_value='/tmp/foo.tar.bzip2')
|
'_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.'
|
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
|
||||||
'_cleanup_working_dirs')
|
'_cleanup_working_dirs')
|
||||||
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
|
@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.utils.wait_for_stack_ready', return_value=True)
|
||||||
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
|
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
|
||||||
'_set_default_plan')
|
'_set_default_plan')
|
||||||
@mock.patch('tripleoclient.utils.ansible_symlink')
|
def test_take_action_standalone(self, mock_def_plan, mock_poll,
|
||||||
def test_take_action_standalone(self, mock_slink, mock_def_plan, mock_poll,
|
|
||||||
mock_environ, mock_geteuid, mock_puppet,
|
mock_environ, mock_geteuid, mock_puppet,
|
||||||
mock_killheat, mock_launchheat,
|
mock_killheat, mock_launchheat,
|
||||||
mock_download, mock_tht,
|
mock_download, mock_tht,
|
||||||
mock_wait_for_port, mock_createdirs,
|
mock_wait_for_port, mock_createdirs,
|
||||||
mock_cleanupdirs, mock_launchansible,
|
mock_cleanupdirs, mock_tarball,
|
||||||
mock_tarball, mock_templates_dir,
|
mock_templates_dir, mock_open, mock_os,
|
||||||
mock_open, mock_os, mock_user, mock_cc,
|
mock_user, mock_cc, mock_chmod, mock_ac,
|
||||||
mock_chmod, mock_ac, mock_outputs,
|
mock_outputs, mock_cmdline, mock_chdir,
|
||||||
mock_cmdline):
|
mock_file_exists, mock_run,
|
||||||
mock_slink.side_effect = 'fake-cmd'
|
mock_run_prepare):
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
['--local-ip', '127.0.0.1',
|
['--local-ip', '127.0.0.1',
|
||||||
'--templates', '/tmp/thtroot',
|
'--templates', '/tmp/thtroot',
|
||||||
|
@ -957,6 +939,7 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
'-e', '../../../outside.yaml',
|
'-e', '../../../outside.yaml',
|
||||||
'--standalone'], [])
|
'--standalone'], [])
|
||||||
|
|
||||||
|
mock_file_exists.return_value = True
|
||||||
fake_orchestration = mock_launchheat(parsed_args)
|
fake_orchestration = mock_launchheat(parsed_args)
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
mock_createdirs.assert_called_once()
|
mock_createdirs.assert_called_once()
|
||||||
|
@ -967,16 +950,12 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
mock_download.assert_called_with(self.cmd, fake_orchestration,
|
mock_download.assert_called_with(self.cmd, fake_orchestration,
|
||||||
'undercloud', 'Undercloud',
|
'undercloud', 'Undercloud',
|
||||||
sys.executable)
|
sys.executable)
|
||||||
mock_launchansible.assert_called_once()
|
|
||||||
mock_tarball.assert_called_once()
|
mock_tarball.assert_called_once()
|
||||||
mock_cleanupdirs.assert_called_once()
|
mock_cleanupdirs.assert_called_once()
|
||||||
self.assertEqual(mock_killheat.call_count, 2)
|
self.assertEqual(mock_killheat.call_count, 2)
|
||||||
mock_cmdline.assert_called_once()
|
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.reset_cmdline')
|
@mock.patch('tripleoclient.utils.reset_cmdline')
|
||||||
@mock.patch('tripleoclient.utils.ansible_symlink')
|
def test_take_action(self, mock_cmdline):
|
||||||
def test_take_action(self, mock_slink, mock_cmdline):
|
|
||||||
mock_slink.side_effect = 'fake-cmd'
|
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
['--local-ip', '127.0.0.1',
|
['--local-ip', '127.0.0.1',
|
||||||
'--templates', '/tmp/thtroot',
|
'--templates', '/tmp/thtroot',
|
||||||
|
@ -984,14 +963,11 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
'--output-dir', '/my'], [])
|
'--output-dir', '/my'], [])
|
||||||
self.assertRaises(exceptions.DeploymentError,
|
self.assertRaises(exceptions.DeploymentError,
|
||||||
self.cmd.take_action, parsed_args)
|
self.cmd.take_action, parsed_args)
|
||||||
mock_cmdline.assert_called_once()
|
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.reset_cmdline')
|
@mock.patch('tripleoclient.utils.reset_cmdline')
|
||||||
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy._standalone_deploy',
|
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy._standalone_deploy',
|
||||||
return_value=1)
|
return_value=1)
|
||||||
@mock.patch('tripleoclient.utils.ansible_symlink')
|
def test_take_action_failure(self, mock_deploy, mock_cmdline):
|
||||||
def test_take_action_failure(self, mock_slink, mock_deploy, mock_cmdline):
|
|
||||||
mock_slink.side_effect = 'fake-cmd'
|
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
['--local-ip', '127.0.0.1',
|
['--local-ip', '127.0.0.1',
|
||||||
'--templates', '/tmp/thtroot',
|
'--templates', '/tmp/thtroot',
|
||||||
|
@ -1000,7 +976,6 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
'--standalone'], [])
|
'--standalone'], [])
|
||||||
self.assertRaises(exceptions.DeploymentError,
|
self.assertRaises(exceptions.DeploymentError,
|
||||||
self.cmd.take_action, parsed_args)
|
self.cmd.take_action, parsed_args)
|
||||||
mock_cmdline.assert_called_once()
|
|
||||||
|
|
||||||
@mock.patch('os.path.isfile', return_value=False)
|
@mock.patch('os.path.isfile', return_value=False)
|
||||||
def test_set_stack_action_default_create(self, mock_isfile):
|
def test_set_stack_action_default_create(self, mock_isfile):
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
import sys
|
|
||||||
|
|
||||||
from osc_lib.tests import utils
|
from osc_lib.tests import utils
|
||||||
import six
|
import six
|
||||||
|
@ -31,48 +30,8 @@ class TestUpgrade(utils.TestCommand):
|
||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = tripleo_upgrade.Upgrade(self.app, None)
|
self.cmd = tripleo_upgrade.Upgrade(self.app, None)
|
||||||
|
self.cmd.ansible_dir = '/tmp'
|
||||||
python_version = sys.version_info[0]
|
self.ansible_playbook_cmd = "ansible-playbook"
|
||||||
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'])
|
|
||||||
|
|
||||||
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.take_action',
|
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.take_action',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
|
@ -127,9 +86,7 @@ class TestUpgrade(utils.TestCommand):
|
||||||
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy',
|
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch('sys.stdin', spec=six.StringIO)
|
@mock.patch('sys.stdin', spec=six.StringIO)
|
||||||
@mock.patch('tripleoclient.utils.ansible_symlink')
|
def test_take_action_prompt_no(self, mock_stdin, mock_deploy):
|
||||||
def test_take_action_prompt_no(self, mock_slink, mock_stdin, mock_deploy):
|
|
||||||
mock_slink.side_effect = 'fake-cmd'
|
|
||||||
mock_stdin.isatty.return_value = True
|
mock_stdin.isatty.return_value = True
|
||||||
mock_stdin.readline.return_value = 'n'
|
mock_stdin.readline.return_value = 'n'
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
|
@ -153,10 +110,7 @@ class TestUpgrade(utils.TestCommand):
|
||||||
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy',
|
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch('sys.stdin', spec=six.StringIO)
|
@mock.patch('sys.stdin', spec=six.StringIO)
|
||||||
@mock.patch('tripleoclient.utils.ansible_symlink')
|
def test_take_action_prompt_invalid_option(self, mock_stdin, mock_deploy):
|
||||||
def test_take_action_prompt_invalid_option(self, mock_slink, mock_stdin,
|
|
||||||
mock_deploy):
|
|
||||||
mock_slink.side_effect = 'fake-cmd'
|
|
||||||
mock_stdin.isatty.return_value = True
|
mock_stdin.isatty.return_value = True
|
||||||
mock_stdin.readline.return_value = 'Dontwant'
|
mock_stdin.readline.return_value = 'Dontwant'
|
||||||
parsed_args = self.check_parser(self.cmd,
|
parsed_args = self.check_parser(self.cmd,
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
import sys
|
|
||||||
|
|
||||||
from osc_lib.tests import utils
|
from osc_lib.tests import utils
|
||||||
from tripleoclient.v1 import tripleo_validator
|
from tripleoclient.v1 import tripleo_validator
|
||||||
|
@ -115,56 +114,3 @@ class TestValidatorShowParameter(utils.TestCommand):
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
self.cmd.take_action(parsed_args)
|
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
|
|
||||||
|
|
|
@ -36,8 +36,7 @@ class TestMinionDeploy(base.TestCase):
|
||||||
|
|
||||||
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_passwords')
|
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_passwords')
|
||||||
@mock.patch('tripleoclient.v1.undercloud_preflight.minion_check')
|
@mock.patch('tripleoclient.v1.undercloud_preflight.minion_check')
|
||||||
@mock.patch('tripleoclient.utils.ansible_symlink')
|
@mock.patch('os.makedirs', return_value=None)
|
||||||
@mock.patch('os.path.isdir', return_value=True)
|
|
||||||
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_output',
|
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_output',
|
||||||
return_value='output.yaml')
|
return_value='output.yaml')
|
||||||
@mock.patch('tripleoclient.v1.minion_config._container_images_config')
|
@mock.patch('tripleoclient.v1.minion_config._container_images_config')
|
||||||
|
@ -46,7 +45,7 @@ class TestMinionDeploy(base.TestCase):
|
||||||
@mock.patch('tripleoclient.utils.load_config')
|
@mock.patch('tripleoclient.utils.load_config')
|
||||||
def test_basic_deploy(self, mock_load_config, mock_get_user,
|
def test_basic_deploy(self, mock_load_config, mock_get_user,
|
||||||
mock_write_env, mock_undercloud_output,
|
mock_write_env, mock_undercloud_output,
|
||||||
mock_images_config, mock_isdir, mock_ans_symlink,
|
mock_images_config, mock_isdir,
|
||||||
mock_check, mock_pass):
|
mock_check, mock_pass):
|
||||||
mock_get_user.return_value = 'foo'
|
mock_get_user.return_value = 'foo'
|
||||||
cmd = minion_config.prepare_minion_deploy()
|
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.minion_config._process_undercloud_passwords')
|
||||||
@mock.patch('tripleoclient.v1.undercloud_preflight.minion_check')
|
@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.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',
|
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_output',
|
||||||
return_value='output.yaml')
|
return_value='output.yaml')
|
||||||
@mock.patch('tripleoclient.v1.minion_config._container_images_config')
|
@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,
|
def test_configured_deploy(self, mock_load_config,
|
||||||
mock_write_env, mock_undercloud_output,
|
mock_write_env, mock_undercloud_output,
|
||||||
mock_images_config, mock_isdir, mock_exists,
|
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('deployment_user', 'bar')
|
||||||
self.conf.set_default('enable_heat_engine', False)
|
self.conf.set_default('enable_heat_engine', False)
|
||||||
self.conf.set_default('enable_ironic_conductor', True)
|
self.conf.set_default('enable_ironic_conductor', True)
|
||||||
|
|
|
@ -54,6 +54,7 @@ class TestUndercloudInstall(TestPluginV1):
|
||||||
self.cmd = undercloud.InstallUndercloud(self.app, app_args)
|
self.cmd = undercloud.InstallUndercloud(self.app, app_args)
|
||||||
|
|
||||||
# TODO(cjeanner) drop once we have proper oslo.privsep
|
# 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('getpass.getuser', return_value='stack')
|
||||||
@mock.patch('six.moves.builtins.open')
|
@mock.patch('six.moves.builtins.open')
|
||||||
@mock.patch('shutil.copy')
|
@mock.patch('shutil.copy')
|
||||||
|
@ -63,7 +64,7 @@ class TestUndercloudInstall(TestPluginV1):
|
||||||
def test_undercloud_install_default(self, mock_subprocess,
|
def test_undercloud_install_default(self, mock_subprocess,
|
||||||
mock_wr,
|
mock_wr,
|
||||||
mock_os, mock_copy,
|
mock_os, mock_copy,
|
||||||
mock_open, mock_user):
|
mock_open, mock_user, mock_getuid):
|
||||||
arglist = ['--no-validations']
|
arglist = ['--no-validations']
|
||||||
verifylist = []
|
verifylist = []
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
@ -118,14 +119,16 @@ class TestUndercloudInstall(TestPluginV1):
|
||||||
'undercloud-stack-vstate-dropin.yaml'])
|
'undercloud-stack-vstate-dropin.yaml'])
|
||||||
|
|
||||||
# TODO(cjeanner) drop once we have proper oslo.privsep
|
# 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('getpass.getuser', return_value='stack')
|
||||||
@mock.patch('shutil.copy')
|
@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('tripleoclient.utils.write_env_file', autospec=True)
|
||||||
@mock.patch('subprocess.check_call', autospec=True)
|
@mock.patch('subprocess.check_call', autospec=True)
|
||||||
def test_undercloud_install_with_heat_customized(self, mock_subprocess,
|
def test_undercloud_install_with_heat_customized(self, mock_subprocess,
|
||||||
mock_wr, mock_os,
|
mock_wr, mock_os,
|
||||||
mock_copy, mock_user):
|
mock_copy, mock_user,
|
||||||
|
mock_getuid):
|
||||||
self.conf.config(output_dir='/foo')
|
self.conf.config(output_dir='/foo')
|
||||||
self.conf.config(templates='/usertht')
|
self.conf.config(templates='/usertht')
|
||||||
self.conf.config(heat_native='false')
|
self.conf.config(heat_native='false')
|
||||||
|
@ -178,6 +181,7 @@ class TestUndercloudInstall(TestPluginV1):
|
||||||
'--force-stack-update'])
|
'--force-stack-update'])
|
||||||
|
|
||||||
# TODO(cjeanner) drop once we have proper oslo.privsep
|
# 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('getpass.getuser', return_value='stack')
|
||||||
@mock.patch('shutil.copy')
|
@mock.patch('shutil.copy')
|
||||||
@mock.patch('os.mkdir')
|
@mock.patch('os.mkdir')
|
||||||
|
@ -200,7 +204,8 @@ class TestUndercloudInstall(TestPluginV1):
|
||||||
mock_sroutes,
|
mock_sroutes,
|
||||||
mock_masq,
|
mock_masq,
|
||||||
mock_wr, mock_os,
|
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(net_config_override='/foo/net-config.json')
|
||||||
self.conf.config(local_interface='ethX')
|
self.conf.config(local_interface='ethX')
|
||||||
self.conf.config(undercloud_public_host='4.3.2.1')
|
self.conf.config(undercloud_public_host='4.3.2.1')
|
||||||
|
@ -348,6 +353,7 @@ class TestUndercloudInstall(TestPluginV1):
|
||||||
'undercloud-stack-vstate-dropin.yaml'])
|
'undercloud-stack-vstate-dropin.yaml'])
|
||||||
|
|
||||||
# TODO(cjeanner) drop once we have proper oslo.privsep
|
# 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('getpass.getuser', return_value='stack')
|
||||||
@mock.patch('six.moves.builtins.open')
|
@mock.patch('six.moves.builtins.open')
|
||||||
@mock.patch('shutil.copy')
|
@mock.patch('shutil.copy')
|
||||||
|
@ -357,7 +363,8 @@ class TestUndercloudInstall(TestPluginV1):
|
||||||
def test_undercloud_install_with_heat_and_debug(self, mock_subprocess,
|
def test_undercloud_install_with_heat_and_debug(self, mock_subprocess,
|
||||||
mock_wr,
|
mock_wr,
|
||||||
mock_os, mock_copy,
|
mock_os, mock_copy,
|
||||||
mock_open, mock_user):
|
mock_open, mock_user,
|
||||||
|
mock_getuid):
|
||||||
self.conf.config(undercloud_log_file='/foo/bar')
|
self.conf.config(undercloud_log_file='/foo/bar')
|
||||||
arglist = ['--no-validations']
|
arglist = ['--no-validations']
|
||||||
verifylist = []
|
verifylist = []
|
||||||
|
@ -416,6 +423,7 @@ class TestUndercloudInstall(TestPluginV1):
|
||||||
'undercloud-stack-vstate-dropin.yaml'])
|
'undercloud-stack-vstate-dropin.yaml'])
|
||||||
|
|
||||||
# TODO(cjeanner) drop once we have proper oslo.privsep
|
# 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('getpass.getuser', return_value='stack')
|
||||||
@mock.patch('six.moves.builtins.open')
|
@mock.patch('six.moves.builtins.open')
|
||||||
@mock.patch('shutil.copy')
|
@mock.patch('shutil.copy')
|
||||||
|
@ -425,7 +433,8 @@ class TestUndercloudInstall(TestPluginV1):
|
||||||
def test_undercloud_install_with_heat_true(self, mock_subprocess,
|
def test_undercloud_install_with_heat_true(self, mock_subprocess,
|
||||||
mock_wr,
|
mock_wr,
|
||||||
mock_os, mock_copy,
|
mock_os, mock_copy,
|
||||||
mock_open, mock_user):
|
mock_open, mock_user,
|
||||||
|
mock_getuid):
|
||||||
self.conf.config(undercloud_log_file='/foo/bar')
|
self.conf.config(undercloud_log_file='/foo/bar')
|
||||||
arglist = ['--no-validations']
|
arglist = ['--no-validations']
|
||||||
verifylist = []
|
verifylist = []
|
||||||
|
@ -480,6 +489,7 @@ class TestUndercloudInstall(TestPluginV1):
|
||||||
'undercloud-stack-vstate-dropin.yaml'])
|
'undercloud-stack-vstate-dropin.yaml'])
|
||||||
|
|
||||||
# TODO(cjeanner) drop once we have proper oslo.privsep
|
# 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('getpass.getuser', return_value='stack')
|
||||||
@mock.patch('shutil.copy')
|
@mock.patch('shutil.copy')
|
||||||
@mock.patch('os.mkdir')
|
@mock.patch('os.mkdir')
|
||||||
|
@ -487,7 +497,8 @@ class TestUndercloudInstall(TestPluginV1):
|
||||||
@mock.patch('subprocess.check_call', autospec=True)
|
@mock.patch('subprocess.check_call', autospec=True)
|
||||||
def test_undercloud_install_with_swift_encryption(self, mock_subprocess,
|
def test_undercloud_install_with_swift_encryption(self, mock_subprocess,
|
||||||
mock_wr, mock_os,
|
mock_wr, mock_os,
|
||||||
mock_copy, mock_user):
|
mock_copy, mock_user,
|
||||||
|
mock_getuid):
|
||||||
arglist = ['--no-validations']
|
arglist = ['--no-validations']
|
||||||
verifylist = []
|
verifylist = []
|
||||||
self.conf.set_default('enable_swift_encryption', True)
|
self.conf.set_default('enable_swift_encryption', True)
|
||||||
|
@ -564,6 +575,7 @@ class TestUndercloudUpgrade(TestPluginV1):
|
||||||
self.cmd = undercloud.UpgradeUndercloud(self.app, app_args)
|
self.cmd = undercloud.UpgradeUndercloud(self.app, app_args)
|
||||||
|
|
||||||
# TODO(cjeanner) drop once we have proper oslo.privsep
|
# 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('getpass.getuser', return_value='stack')
|
||||||
@mock.patch('shutil.copy')
|
@mock.patch('shutil.copy')
|
||||||
@mock.patch('os.mkdir')
|
@mock.patch('os.mkdir')
|
||||||
|
@ -571,7 +583,8 @@ class TestUndercloudUpgrade(TestPluginV1):
|
||||||
@mock.patch('subprocess.check_call', autospec=True)
|
@mock.patch('subprocess.check_call', autospec=True)
|
||||||
def test_undercloud_upgrade_default(self, mock_subprocess,
|
def test_undercloud_upgrade_default(self, mock_subprocess,
|
||||||
mock_wr,
|
mock_wr,
|
||||||
mock_os, mock_copy, mock_user):
|
mock_os, mock_copy, mock_user,
|
||||||
|
mock_getuid):
|
||||||
arglist = ['--no-validations']
|
arglist = ['--no-validations']
|
||||||
verifylist = []
|
verifylist = []
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
@ -629,6 +642,7 @@ class TestUndercloudUpgrade(TestPluginV1):
|
||||||
'undercloud-stack-vstate-dropin.yaml'])
|
'undercloud-stack-vstate-dropin.yaml'])
|
||||||
|
|
||||||
# TODO(cjeanner) drop once we have proper oslo.privsep
|
# 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('getpass.getuser', return_value='stack')
|
||||||
@mock.patch('shutil.copy')
|
@mock.patch('shutil.copy')
|
||||||
@mock.patch('os.mkdir')
|
@mock.patch('os.mkdir')
|
||||||
|
@ -636,7 +650,8 @@ class TestUndercloudUpgrade(TestPluginV1):
|
||||||
@mock.patch('subprocess.check_call', autospec=True)
|
@mock.patch('subprocess.check_call', autospec=True)
|
||||||
def test_undercloud_upgrade_with_heat_enabled(self, mock_subprocess,
|
def test_undercloud_upgrade_with_heat_enabled(self, mock_subprocess,
|
||||||
mock_wr, mock_os,
|
mock_wr, mock_os,
|
||||||
mock_copy, mock_user):
|
mock_copy, mock_user,
|
||||||
|
mock_getuid):
|
||||||
arglist = ['--no-validations']
|
arglist = ['--no-validations']
|
||||||
verifylist = []
|
verifylist = []
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
@ -693,6 +708,7 @@ class TestUndercloudUpgrade(TestPluginV1):
|
||||||
'undercloud-stack-vstate-dropin.yaml'])
|
'undercloud-stack-vstate-dropin.yaml'])
|
||||||
|
|
||||||
# TODO(cjeanner) drop once we have proper oslo.privsep
|
# 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('getpass.getuser', return_value='stack')
|
||||||
@mock.patch('shutil.copy')
|
@mock.patch('shutil.copy')
|
||||||
@mock.patch('os.mkdir')
|
@mock.patch('os.mkdir')
|
||||||
|
@ -700,7 +716,8 @@ class TestUndercloudUpgrade(TestPluginV1):
|
||||||
@mock.patch('subprocess.check_call', autospec=True)
|
@mock.patch('subprocess.check_call', autospec=True)
|
||||||
def test_undercloud_upgrade_with_heat_true(self, mock_subprocess,
|
def test_undercloud_upgrade_with_heat_true(self, mock_subprocess,
|
||||||
mock_wr, mock_os,
|
mock_wr, mock_os,
|
||||||
mock_copy, mock_user):
|
mock_copy, mock_user,
|
||||||
|
mock_getuid):
|
||||||
arglist = ['--no-validations']
|
arglist = ['--no-validations']
|
||||||
verifylist = []
|
verifylist = []
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
@ -757,6 +774,7 @@ class TestUndercloudUpgrade(TestPluginV1):
|
||||||
'/usr/share/openstack-tripleo-heat-templates/'
|
'/usr/share/openstack-tripleo-heat-templates/'
|
||||||
'undercloud-stack-vstate-dropin.yaml'])
|
'undercloud-stack-vstate-dropin.yaml'])
|
||||||
|
|
||||||
|
@mock.patch('os.geteuid', return_value=1001)
|
||||||
@mock.patch('getpass.getuser', return_value='stack')
|
@mock.patch('getpass.getuser', return_value='stack')
|
||||||
@mock.patch('shutil.copy')
|
@mock.patch('shutil.copy')
|
||||||
@mock.patch('os.mkdir')
|
@mock.patch('os.mkdir')
|
||||||
|
@ -764,7 +782,8 @@ class TestUndercloudUpgrade(TestPluginV1):
|
||||||
@mock.patch('subprocess.check_call', autospec=True)
|
@mock.patch('subprocess.check_call', autospec=True)
|
||||||
def test_undercloud_upgrade_with_heat_and_yes(self, mock_subprocess,
|
def test_undercloud_upgrade_with_heat_and_yes(self, mock_subprocess,
|
||||||
mock_wr, mock_os,
|
mock_wr, mock_os,
|
||||||
mock_user, mock_copy):
|
mock_copy, mock_user,
|
||||||
|
mock_getuid):
|
||||||
arglist = ['--no-validations', '-y']
|
arglist = ['--no-validations', '-y']
|
||||||
verifylist = []
|
verifylist = []
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
@ -822,6 +841,7 @@ class TestUndercloudUpgrade(TestPluginV1):
|
||||||
'undercloud-stack-vstate-dropin.yaml'])
|
'undercloud-stack-vstate-dropin.yaml'])
|
||||||
|
|
||||||
# TODO(cjeanner) drop once we have proper oslo.privsep
|
# 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('getpass.getuser', return_value='stack')
|
||||||
@mock.patch('shutil.copy')
|
@mock.patch('shutil.copy')
|
||||||
@mock.patch('os.mkdir')
|
@mock.patch('os.mkdir')
|
||||||
|
@ -829,7 +849,8 @@ class TestUndercloudUpgrade(TestPluginV1):
|
||||||
@mock.patch('subprocess.check_call', autospec=True)
|
@mock.patch('subprocess.check_call', autospec=True)
|
||||||
def test_undercloud_upgrade_with_heat_and_debug(self, mock_subprocess,
|
def test_undercloud_upgrade_with_heat_and_debug(self, mock_subprocess,
|
||||||
mock_wr, mock_os,
|
mock_wr, mock_os,
|
||||||
mock_copy, mock_user):
|
mock_copy, mock_user,
|
||||||
|
mock_getuid):
|
||||||
arglist = ['--no-validations']
|
arglist = ['--no-validations']
|
||||||
verifylist = []
|
verifylist = []
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
|
@ -46,14 +46,16 @@ class TestDeploymentWorkflows(utils.TestCommand):
|
||||||
"message": "Fail.",
|
"message": "Fail.",
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
@mock.patch('os.chdir')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.wait_for_ssh_port')
|
@mock.patch('tripleoclient.workflows.deployment.wait_for_ssh_port')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.time.sleep')
|
@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.open')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.tempfile')
|
@mock.patch('tripleoclient.utils.tempfile')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.subprocess.check_call')
|
@mock.patch('tripleoclient.workflows.deployment.subprocess.check_call')
|
||||||
def test_enable_ssh_admin(self, mock_check_call, mock_tempfile, mock_open,
|
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()
|
log = mock.Mock()
|
||||||
hosts = 'a', 'b', 'c'
|
hosts = 'a', 'b', 'c'
|
||||||
ssh_user = 'test-user'
|
ssh_user = 'test-user'
|
||||||
|
@ -86,15 +88,16 @@ class TestDeploymentWorkflows(utils.TestCommand):
|
||||||
self.assertEqual(1, mock_rmtree.call_count)
|
self.assertEqual(1, mock_rmtree.call_count)
|
||||||
self.assertEqual('/foo', mock_rmtree.call_args[0][0])
|
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.wait_for_ssh_port')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.time.sleep')
|
@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.open')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.tempfile')
|
@mock.patch('tripleoclient.utils.tempfile')
|
||||||
@mock.patch('tripleoclient.workflows.deployment.subprocess.check_call')
|
@mock.patch('tripleoclient.workflows.deployment.subprocess.check_call')
|
||||||
def test_enable_ssh_admin_error(self, mock_check_call, mock_tempfile,
|
def test_enable_ssh_admin_error(self, mock_check_call, mock_tempfile,
|
||||||
mock_open, mock_rmtree, mock_sleep,
|
mock_open, mock_rmtree, mock_sleep,
|
||||||
mock_wait_for_ssh_port):
|
mock_wait_for_ssh_port, mock_chdir):
|
||||||
log = mock.Mock()
|
log = mock.Mock()
|
||||||
hosts = 'a', 'b', 'c'
|
hosts = 'a', 'b', 'c'
|
||||||
ssh_user = 'test-user'
|
ssh_user = 'test-user'
|
||||||
|
|
|
@ -155,11 +155,11 @@ class TestDownloadContainer(fakes.TestDeployOvercloud):
|
||||||
'test',
|
'test',
|
||||||
'test')
|
'test')
|
||||||
|
|
||||||
@mock.patch('os.path.exists')
|
@mock.patch('os.makedirs')
|
||||||
def test_download_files(self, exists_mock):
|
def test_download_files(self, makedirs_mock):
|
||||||
support.check_local_space = mock.MagicMock()
|
support.check_local_space = mock.MagicMock()
|
||||||
support.check_local_space.return_value = True
|
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 = self.app.client_manager.object_store
|
||||||
oc.object_list.return_value = [
|
oc.object_list.return_value = [
|
||||||
{'name': 'test1'}
|
{'name': 'test1'}
|
||||||
|
|
|
@ -28,13 +28,14 @@ import getpass
|
||||||
import glob
|
import glob
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import shutil
|
|
||||||
from six.moves.configparser import ConfigParser
|
from six.moves.configparser import ConfigParser
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import netaddr
|
import netaddr
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
|
import pwd
|
||||||
|
import shutil
|
||||||
import simplejson
|
import simplejson
|
||||||
import six
|
import six
|
||||||
import socket
|
import socket
|
||||||
|
@ -44,6 +45,8 @@ import tempfile
|
||||||
import time
|
import time
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
import ansible_runner
|
||||||
|
|
||||||
from heatclient.common import event_utils
|
from heatclient.common import event_utils
|
||||||
from heatclient.common import template_utils
|
from heatclient.common import template_utils
|
||||||
from heatclient.common import utils as heat_utils
|
from heatclient.common import utils as heat_utils
|
||||||
|
@ -63,204 +66,450 @@ from tripleoclient import exceptions
|
||||||
LOG = logging.getLogger(__name__ + ".utils")
|
LOG = logging.getLogger(__name__ + ".utils")
|
||||||
|
|
||||||
|
|
||||||
def run_ansible_playbook(logger,
|
# NOTE(cloudnull): This is setting the FileExistsError for py2 environments.
|
||||||
workdir,
|
# When we no longer support py2 (centos7) this should be
|
||||||
playbook,
|
# removed.
|
||||||
inventory,
|
try:
|
||||||
log_path_dir=None,
|
FileExistsError = FileExistsError
|
||||||
ansible_config=None,
|
except NameError:
|
||||||
retries=True,
|
FileExistsError = OSError
|
||||||
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
|
|
||||||
|
|
||||||
:param logger: logger instance
|
|
||||||
:type logger: Logger
|
|
||||||
|
|
||||||
:param plan: plan name (Defaults to "overcloud")
|
class Pushd(object):
|
||||||
:type plan: String
|
"""Simple context manager to change directories and then return."""
|
||||||
|
|
||||||
:param workdir: location of the playbook
|
def __init__(self, directory):
|
||||||
:type workdir: String
|
"""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
|
: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
|
:type inventory: String
|
||||||
|
|
||||||
:param ansible_config: Pass either Absolute Path, or None to generate a
|
:param workdir: Location of the working directory.
|
||||||
temporary file, or False to not manage configuration at all
|
:type workdir: String
|
||||||
:type ansible_config: String
|
|
||||||
|
|
||||||
:param log_path_dir: Dir path location for ansible log file.
|
:param playbook_dir: Location of the playbook directory.
|
||||||
Defaults to "None"
|
(defaults to workdir).
|
||||||
:type retries: String
|
:type playbook_dir: String
|
||||||
|
|
||||||
:param retries: do you want to get a retry_file?
|
:param connection: Connection type (local, smart, etc).
|
||||||
:type retries: Boolean
|
|
||||||
|
|
||||||
:param connection: connection type (local, smart, etc)
|
|
||||||
:type connection: String
|
: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
|
:type output_callback: String
|
||||||
|
|
||||||
:param python_interpreter: Absolute path for the Python interpreter
|
:param ssh_user: User for the ssh connection.
|
||||||
on the host where Ansible is run.
|
|
||||||
:type python_interpreter: String
|
|
||||||
|
|
||||||
:param ssh_user: user for the ssh connection
|
|
||||||
:type ssh_user: String
|
: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
|
: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
|
: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
|
:type limit_hosts: String
|
||||||
|
|
||||||
:param tags: run specific tags
|
:param tags: Run specific tags.
|
||||||
:type tags: String
|
:type tags: String
|
||||||
|
|
||||||
:param skip_tags: skip specific tags
|
:param skip_tags: Skip specific tags.
|
||||||
:type skip_tags: String
|
:type skip_tags: String
|
||||||
|
|
||||||
:param verbosity: verbosity level for Ansible execution
|
:param verbosity: Verbosity level for Ansible execution.
|
||||||
:type verbosity: Integer
|
:type verbosity: Integer
|
||||||
|
|
||||||
:param extra_vars: set additional variables as a Dict
|
:param quiet: Disable all output (Defaults to False)
|
||||||
or the absolute path of a JSON or YAML file type
|
: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
|
:type extra_vars: Either a Dict or the absolute path of JSON or YAML
|
||||||
|
|
||||||
:param gathering_policy: This setting controls the default policy of
|
:param plan: Plan name (Defaults to "overcloud").
|
||||||
fact gathering ('smart', 'implicit', 'explicit'). Defaults to None.
|
:type plan: String
|
||||||
When not specified, the policy will be the default Ansible one, ie.
|
|
||||||
'implicit'.
|
|
||||||
:type gathering_facts: String
|
|
||||||
"""
|
|
||||||
env = os.environ.copy()
|
|
||||||
|
|
||||||
env['ANSIBLE_LIBRARY'] = \
|
:param gathering_policy: This setting controls the default policy of
|
||||||
('/root/.ansible/plugins/modules:'
|
fact gathering ('smart', 'implicit', 'explicit').
|
||||||
'/usr/share/ansible/plugins/modules:'
|
:type gathering_facts: String
|
||||||
'%s/library' % constants.DEFAULT_VALIDATIONS_BASEDIR)
|
|
||||||
env['ANSIBLE_LOOKUP_PLUGINS'] = \
|
:param extra_env_variables: Dict option to extend or override any of the
|
||||||
('root/.ansible/plugins/lookup:'
|
default environment variables.
|
||||||
'/usr/share/ansible/plugins/lookup:'
|
:type extra_env_variables: Dict
|
||||||
'%s/lookup_plugins' % constants.DEFAULT_VALIDATIONS_BASEDIR)
|
"""
|
||||||
env['ANSIBLE_CALLBACK_PLUGINS'] = \
|
|
||||||
('~/.ansible/plugins/callback:'
|
def _playbook_check(play):
|
||||||
'/usr/share/ansible/plugins/callback:'
|
if not os.path.exists(play):
|
||||||
'%s/callback_plugins' % constants.DEFAULT_VALIDATIONS_BASEDIR)
|
play = os.path.join(playbook_dir, play)
|
||||||
env['ANSIBLE_ROLES_PATH'] = \
|
if not os.path.exists(play):
|
||||||
('/root/.ansible/roles:'
|
raise RuntimeError('No such playbook: {}'.format(play))
|
||||||
'/usr/share/ansible/roles:'
|
LOG.debug('Ansible playbook {} found'.format(play))
|
||||||
'/etc/ansible/roles:'
|
return play
|
||||||
'%s/roles' % constants.DEFAULT_VALIDATIONS_BASEDIR)
|
|
||||||
|
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:'
|
||||||
|
'/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:'
|
||||||
|
'/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:'
|
||||||
|
'/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:'
|
||||||
|
'{}/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
|
env['TRIPLEO_PLAN_NAME'] = plan
|
||||||
|
|
||||||
if not log_path_dir or not os.path.exists(log_path_dir):
|
try:
|
||||||
env['ANSIBLE_LOG_PATH'] = os.path.join(workdir, 'ansible.log')
|
user_pwd = pwd.getpwuid(int(os.getenv('SUDO_UID', os.getuid())))
|
||||||
|
except TypeError:
|
||||||
|
home = os.path.expanduser('~')
|
||||||
else:
|
else:
|
||||||
env['ANSIBLE_LOG_PATH'] = os.path.join(log_path_dir, 'ansible.log')
|
home = user_pwd.pw_dir
|
||||||
|
|
||||||
env['ANSIBLE_HOST_KEY_CHECKING'] = 'False'
|
env['ANSIBLE_LOG_PATH'] = os.path.join(home, 'ansible.log')
|
||||||
|
|
||||||
if gathering_policy in ['smart', 'explicit', 'implicit']:
|
if key:
|
||||||
env['ANSIBLE_GATHERING'] = gathering_policy
|
env['ANSIBLE_PRIVATE_KEY_FILE'] = key
|
||||||
|
|
||||||
if extra_vars is None:
|
if extra_env_variables:
|
||||||
extra_vars = {}
|
if not isinstance(extra_env_variables, dict):
|
||||||
|
msg = "extra_env_variables must be a dict"
|
||||||
cleanup = False
|
LOG.error(msg)
|
||||||
if ansible_config is None:
|
raise SystemError(msg)
|
||||||
_, 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:
|
else:
|
||||||
raise RuntimeError('No such configuration file: %s' %
|
env.update(extra_env_variables)
|
||||||
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)
|
with TempDirs(chdir=False) as ansible_artifact_path:
|
||||||
|
r_opts = {
|
||||||
if os.path.exists(play):
|
'private_data_dir': workdir,
|
||||||
cmd = ["ansible-playbook-{}".format(sys.version_info[0]),
|
'project_dir': playbook_dir,
|
||||||
'-u', ssh_user,
|
'inventory': inventory,
|
||||||
'-i', inventory
|
'envvars': _encode_envvars(env=env),
|
||||||
]
|
'playbook': playbook,
|
||||||
|
'verbosity': verbosity,
|
||||||
if 0 < verbosity < 6:
|
'quiet': quiet,
|
||||||
cmd.extend(['-' + ('v' * verbosity)])
|
'extravars': extravars,
|
||||||
|
'fact_cache': ansible_fact_path,
|
||||||
if key:
|
'fact_cache_type': 'jsonfile',
|
||||||
cmd.extend(['--private-key=%s' % key])
|
'artifact_dir': ansible_artifact_path,
|
||||||
|
'rotate_artifacts': 256
|
||||||
if module_path:
|
}
|
||||||
cmd.extend(['--module-path=%s' % module_path])
|
|
||||||
|
|
||||||
if limit_hosts:
|
|
||||||
cmd.extend(['-l %s' % limit_hosts])
|
|
||||||
|
|
||||||
if tags:
|
|
||||||
cmd.extend(['-t %s' % tags])
|
|
||||||
|
|
||||||
if skip_tags:
|
if skip_tags:
|
||||||
cmd.extend(['--skip_tags %s' % skip_tags])
|
r_opts['skip_tags'] = skip_tags
|
||||||
|
|
||||||
if python_interpreter:
|
if tags:
|
||||||
cmd.extend([
|
r_opts['tags'] = tags
|
||||||
'--extra-vars',
|
|
||||||
'ansible_python_interpreter=%s' % python_interpreter
|
|
||||||
])
|
|
||||||
|
|
||||||
if extra_vars:
|
if limit_hosts:
|
||||||
if isinstance(extra_vars, dict) and extra_vars:
|
r_opts['limit'] = limit_hosts
|
||||||
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)])
|
|
||||||
else:
|
|
||||||
raise RuntimeError('No such extra vars file: %s' % extra_vars)
|
|
||||||
|
|
||||||
cmd.extend(['-c', connection, play])
|
runner_config = ansible_runner.runner_config.RunnerConfig(**r_opts)
|
||||||
|
runner_config.prepare()
|
||||||
|
runner = ansible_runner.Runner(config=runner_config)
|
||||||
|
status, rc = runner.run()
|
||||||
|
|
||||||
proc = run_command_and_log(logger, cmd, env=env, retcode_only=False)
|
if verbosity > 1:
|
||||||
proc.wait()
|
status = runner.stdout
|
||||||
cleanup and os.unlink(tmp_config)
|
|
||||||
if proc.returncode != 0:
|
if rc == 0:
|
||||||
raise RuntimeError(proc.stdout.read())
|
LOG.info(
|
||||||
return proc.returncode, proc.stdout.read()
|
'Ansible execution success. playbook: {}'.format(
|
||||||
|
playbook
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return rc, status
|
||||||
else:
|
else:
|
||||||
cleanup and os.unlink(tmp_config)
|
err_msg = (
|
||||||
raise RuntimeError('No such playbook: %s' % play)
|
'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):
|
def convert(data):
|
||||||
|
@ -398,13 +647,7 @@ def store_cli_param(command_name, parsed_args):
|
||||||
command_name = command_name.replace(" ", "-")
|
command_name = command_name.replace(" ", "-")
|
||||||
|
|
||||||
history_path = os.path.join(os.path.expanduser("~"), '.tripleo')
|
history_path = os.path.join(os.path.expanduser("~"), '.tripleo')
|
||||||
if not os.path.exists(history_path):
|
makedirs(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)
|
|
||||||
if os.path.isdir(history_path):
|
if os.path.isdir(history_path):
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(history_path,
|
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,
|
tags=tags, skip_tags=skip_tags,
|
||||||
verbosity=verbosity, extra_vars=extra_vars)
|
verbosity=verbosity, extra_vars=extra_vars)
|
||||||
else:
|
else:
|
||||||
run_ansible_playbook(logger=LOG,
|
run_ansible_playbook(playbook=book,
|
||||||
workdir=workdir,
|
|
||||||
playbook=book,
|
|
||||||
inventory=inventory,
|
inventory=inventory,
|
||||||
|
workdir=workdir,
|
||||||
ssh_user=ssh_user,
|
ssh_user=ssh_user,
|
||||||
key=ssh_private_key(workdir, priv_key),
|
key=ssh_private_key(workdir, priv_key),
|
||||||
module_path='/usr/share/ansible-modules',
|
module_path='/usr/share/ansible-modules',
|
||||||
limit_hosts=nodes, tags=tags,
|
limit_hosts=nodes,
|
||||||
|
tags=tags,
|
||||||
skip_tags=skip_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" %
|
log.debug("Symlinking %s to %s, via temp dir %s" %
|
||||||
(src, dst, tmpd))
|
(src, dst, tmpd))
|
||||||
tmp = None
|
|
||||||
try:
|
makedirs(dst)
|
||||||
if not os.path.exists(tmpd):
|
with TempDirs(dir_path=tmpd) as tmp:
|
||||||
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)
|
|
||||||
for obj in os.listdir(src):
|
for obj in os.listdir(src):
|
||||||
tmpf = os.path.join(tmp, obj)
|
if not os.path.exists(os.path.join(dst, obj)):
|
||||||
os.symlink(os.path.join(src, obj), tmpf)
|
tmpf = os.path.join(tmp, obj)
|
||||||
os.rename(tmpf, os.path.join(dst, obj))
|
os.symlink(os.path.join(src, obj), tmpf)
|
||||||
except Exception:
|
os.rename(tmpf, os.path.join(dst, obj))
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
if tmp:
|
|
||||||
shutil.rmtree(tmp, ignore_errors=True)
|
|
||||||
|
|
||||||
|
|
||||||
def run_command_and_log(log, cmd, cwd=None, env=None, retcode_only=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)
|
env_url = heat_utils.normalise_file_path_to_url(path)
|
||||||
return request.urlopen(env_url).read()
|
return request.urlopen(env_url).read()
|
||||||
|
|
||||||
env_f, env = (
|
return (
|
||||||
template_utils.process_multiple_environments_and_files(
|
template_utils.process_multiple_environments_and_files(
|
||||||
env_files, env_path_is_object=lambda path: True,
|
env_files,
|
||||||
object_request=get_env_file))
|
env_path_is_object=lambda path: True,
|
||||||
|
object_request=get_env_file
|
||||||
return env
|
)
|
||||||
|
)[1]
|
||||||
|
|
||||||
|
|
||||||
def rel_or_abs_path(file_path, tht_root):
|
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))
|
validations_abspath = glob.glob("{path}/*.yaml".format(path=path))
|
||||||
|
|
||||||
for pl in validations_abspath:
|
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:
|
with open(pl, 'r') as val_playbook:
|
||||||
contents = yaml.safe_load(val_playbook)
|
contents = yaml.safe_load(val_playbook)
|
||||||
|
@ -1910,23 +2144,6 @@ def get_local_timezone():
|
||||||
return 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):
|
def check_file_for_enabled_service(env_file):
|
||||||
"""Checks environment file for the said service.
|
"""Checks environment file for the said service.
|
||||||
|
|
||||||
|
@ -1979,7 +2196,7 @@ def reset_cmdline():
|
||||||
output = ''
|
output = ''
|
||||||
try:
|
try:
|
||||||
output = run_command(['reset', '-I'])
|
output = run_command(['reset', '-I'])
|
||||||
except RuntimeError as e:
|
except RuntimeError:
|
||||||
LOG.warning('Unable to reset command line. Try manually running '
|
LOG.warning('Unable to reset command line. Try manually running '
|
||||||
'"reset" if the command line is broken.')
|
'"reset" if the command line is broken.')
|
||||||
sys.stdout.write(output)
|
sys.stdout.write(output)
|
||||||
|
@ -2003,7 +2220,7 @@ def safe_write(path, data):
|
||||||
f.write(data)
|
f.write(data)
|
||||||
except OSError as error:
|
except OSError as error:
|
||||||
if error.errno != errno.EEXIST:
|
if error.errno != errno.EEXIST:
|
||||||
msg = _('The output file %(file)s can not be '
|
msg = _(
|
||||||
'created. Error: %(msg)') % {'file': path,
|
'The output file {file} can not be created. Error: {msg}'
|
||||||
'msg': error.message}
|
).format(file=path, msg=str(error))
|
||||||
raise oscexc.CommandError(msg)
|
raise oscexc.CommandError(msg)
|
||||||
|
|
|
@ -194,45 +194,49 @@ class BuildImage(command.Command):
|
||||||
tmp.write('list_dependencies=true')
|
tmp.write('list_dependencies=true')
|
||||||
kolla_config_files = list(parsed_args.kolla_config_files)
|
kolla_config_files = list(parsed_args.kolla_config_files)
|
||||||
kolla_config_files.append(path)
|
kolla_config_files.append(path)
|
||||||
kolla_tmp_dir = None
|
with utils.TempDirs(dir_prefix='kolla-') as kolla_tmp_dir:
|
||||||
if parsed_args.use_buildah:
|
try:
|
||||||
# A temporary directory is needed to let Kolla generates the
|
builder = kolla_builder.KollaImageBuilder(
|
||||||
# Dockerfiles that will be used by Buildah to build the images.
|
parsed_args.config_files
|
||||||
kolla_tmp_dir = tempfile.mkdtemp(prefix='kolla-')
|
)
|
||||||
|
result = builder.build_images(kolla_config_files,
|
||||||
|
parsed_args.excludes,
|
||||||
|
parsed_args.use_buildah,
|
||||||
|
kolla_tmp_dir)
|
||||||
|
|
||||||
try:
|
if parsed_args.use_buildah:
|
||||||
builder = kolla_builder.KollaImageBuilder(parsed_args.config_files)
|
deps = json.loads(result)
|
||||||
result = builder.build_images(kolla_config_files,
|
kolla_cfg = utils.get_read_config(kolla_config_files)
|
||||||
parsed_args.excludes,
|
bb = buildah.BuildahBuilder(
|
||||||
parsed_args.use_buildah,
|
kolla_tmp_dir, deps,
|
||||||
kolla_tmp_dir)
|
utils.get_from_cfg(kolla_cfg, "base"),
|
||||||
|
utils.get_from_cfg(kolla_cfg, "type"),
|
||||||
if parsed_args.use_buildah:
|
utils.get_from_cfg(kolla_cfg, "tag"),
|
||||||
deps = json.loads(result)
|
utils.get_from_cfg(kolla_cfg, "namespace"),
|
||||||
kolla_cfg = utils.get_read_config(kolla_config_files)
|
utils.get_from_cfg(kolla_cfg, "registry"),
|
||||||
bb = buildah.BuildahBuilder(
|
utils.getboolean_from_cfg(kolla_cfg, "push"))
|
||||||
kolla_tmp_dir, deps,
|
bb.build_all()
|
||||||
utils.get_from_cfg(kolla_cfg, "base"),
|
elif parsed_args.list_dependencies:
|
||||||
utils.get_from_cfg(kolla_cfg, "type"),
|
deps = json.loads(result)
|
||||||
utils.get_from_cfg(kolla_cfg, "tag"),
|
yaml.safe_dump(
|
||||||
utils.get_from_cfg(kolla_cfg, "namespace"),
|
deps,
|
||||||
utils.get_from_cfg(kolla_cfg, "registry"),
|
self.app.stdout,
|
||||||
utils.getboolean_from_cfg(kolla_cfg, "push"))
|
indent=2,
|
||||||
bb.build_all()
|
default_flow_style=False
|
||||||
elif parsed_args.list_dependencies:
|
)
|
||||||
deps = json.loads(result)
|
elif parsed_args.list_images:
|
||||||
yaml.safe_dump(deps, self.app.stdout, indent=2,
|
deps = json.loads(result)
|
||||||
default_flow_style=False)
|
images = []
|
||||||
elif parsed_args.list_images:
|
BuildImage.images_from_deps(images, deps)
|
||||||
deps = json.loads(result)
|
yaml.safe_dump(
|
||||||
images = []
|
images,
|
||||||
BuildImage.images_from_deps(images, deps)
|
self.app.stdout,
|
||||||
yaml.safe_dump(images, self.app.stdout,
|
default_flow_style=False
|
||||||
default_flow_style=False)
|
)
|
||||||
elif result:
|
elif result:
|
||||||
self.app.stdout.write(result)
|
self.app.stdout.write(result)
|
||||||
finally:
|
finally:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
|
|
||||||
|
|
||||||
class PrepareImageFiles(command.Command):
|
class PrepareImageFiles(command.Command):
|
||||||
|
|
|
@ -148,8 +148,7 @@ def prepare_minion_deploy(upgrade=False, no_validations=False,
|
||||||
# we move any user provided environment files into this root later.
|
# we move any user provided environment files into this root later.
|
||||||
tempdir = os.path.join(os.path.abspath(CONF['output_dir']),
|
tempdir = os.path.join(os.path.abspath(CONF['output_dir']),
|
||||||
'tripleo-config-generated-env-files')
|
'tripleo-config-generated-env-files')
|
||||||
if not os.path.isdir(tempdir):
|
utils.makedirs(tempdir)
|
||||||
os.mkdir(tempdir)
|
|
||||||
|
|
||||||
env_data['PythonInterpreter'] = sys.executable
|
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 += ['--deployment-user', u]
|
||||||
|
|
||||||
deploy_args += ['--output-dir=%s' % CONF['output_dir']]
|
deploy_args += ['--output-dir=%s' % CONF['output_dir']]
|
||||||
if not os.path.isdir(CONF['output_dir']):
|
utils.makedirs(CONF['output_dir'])
|
||||||
os.mkdir(CONF['output_dir'])
|
|
||||||
|
|
||||||
# TODO(aschultz): move this to a central class
|
# TODO(aschultz): move this to a central class
|
||||||
if CONF.get('net_config_override', None):
|
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'))
|
utils.set_hostname(CONF.get('minion_hostname'))
|
||||||
|
|
||||||
if CONF.get('minion_enable_validations') and not no_validations:
|
if CONF.get('minion_enable_validations') and not no_validations:
|
||||||
utils.ansible_symlink()
|
|
||||||
undercloud_preflight.minion_check(verbose_level, upgrade)
|
undercloud_preflight.minion_check(verbose_level, upgrade)
|
||||||
|
|
||||||
if CONF.get('custom_env_files'):
|
if CONF.get('custom_env_files'):
|
||||||
|
|
|
@ -19,6 +19,7 @@ from osc_lib.i18n import _
|
||||||
from oslo_concurrency import processutils
|
from oslo_concurrency import processutils
|
||||||
|
|
||||||
from tripleoclient import command
|
from tripleoclient import command
|
||||||
|
from tripleoclient import utils
|
||||||
from tripleoclient.workflows import deployment
|
from tripleoclient.workflows import deployment
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,8 +77,7 @@ class DownloadConfig(command.Command):
|
||||||
str(e))
|
str(e))
|
||||||
raise OSError(message)
|
raise OSError(message)
|
||||||
|
|
||||||
if not os.path.exists(config_dir):
|
utils.makedirs(config_dir)
|
||||||
os.makedirs(config_dir)
|
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
self.log.debug("take_action(%s)" % parsed_args)
|
self.log.debug("take_action(%s)" % parsed_args)
|
||||||
|
|
|
@ -24,7 +24,6 @@ import re
|
||||||
import shutil
|
import shutil
|
||||||
import six
|
import six
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
|
||||||
import time
|
import time
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
@ -167,8 +166,7 @@ class DeployOvercloud(command.Command):
|
||||||
user_env_path = os.path.join(
|
user_env_path = os.path.join(
|
||||||
user_env_dir, os.path.basename(abs_env_path))
|
user_env_dir, os.path.basename(abs_env_path))
|
||||||
self.log.debug("user_env_path=%s" % user_env_path)
|
self.log.debug("user_env_path=%s" % user_env_path)
|
||||||
if not os.path.exists(user_env_dir):
|
utils.makedirs(user_env_dir)
|
||||||
os.makedirs(user_env_dir)
|
|
||||||
with open(user_env_path, 'w') as f:
|
with open(user_env_path, 'w') as f:
|
||||||
self.log.debug("Writing user environment %s" % user_env_path)
|
self.log.debug("Writing user environment %s" % user_env_path)
|
||||||
f.write(contents)
|
f.write(contents)
|
||||||
|
@ -350,8 +348,7 @@ class DeployOvercloud(command.Command):
|
||||||
if not os.path.isfile(file_path):
|
if not os.path.isfile(file_path):
|
||||||
self.log.debug("Missing in templates directory, downloading \
|
self.log.debug("Missing in templates directory, downloading \
|
||||||
%s from swift into %s" % (pf, file_path))
|
%s from swift into %s" % (pf, file_path))
|
||||||
if not os.path.exists(os.path.dirname(file_path)):
|
utils.makedirs(os.path.dirname(file_path))
|
||||||
os.makedirs(os.path.dirname(file_path))
|
|
||||||
# open in binary as the swiftclient get/put error under
|
# open in binary as the swiftclient get/put error under
|
||||||
# python3 if opened as Text I/O
|
# python3 if opened as Text I/O
|
||||||
with open(file_path, 'wb') as f:
|
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
|
# copy tht_root to temporary directory because we need to
|
||||||
# download any missing (e.g j2 rendered) files from the plan
|
# download any missing (e.g j2 rendered) files from the plan
|
||||||
tht_root = os.path.abspath(parsed_args.templates)
|
tht_root = os.path.abspath(parsed_args.templates)
|
||||||
tht_tmp = tempfile.mkdtemp(prefix='tripleoclient-')
|
|
||||||
new_tht_root = "%s/tripleo-heat-templates" % tht_tmp
|
with utils.TempDirs(dir_prefix='tripleoclient-',
|
||||||
self.log.debug("Creating temporary templates tree in %s"
|
cleanup=(not parsed_args.no_cleanup)) as tht_tmp:
|
||||||
% new_tht_root)
|
new_tht_root = "%s/tripleo-heat-templates" % tht_tmp
|
||||||
try:
|
self.log.debug(
|
||||||
|
"Creating temporary templates tree in %s" % new_tht_root
|
||||||
|
)
|
||||||
shutil.copytree(tht_root, new_tht_root, symlinks=True)
|
shutil.copytree(tht_root, new_tht_root, symlinks=True)
|
||||||
self._deploy_tripleo_heat_templates(stack, parsed_args,
|
self._deploy_tripleo_heat_templates(stack, parsed_args,
|
||||||
new_tht_root, tht_root)
|
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,
|
def _deploy_tripleo_heat_templates(self, stack, parsed_args,
|
||||||
tht_root, user_tht_root):
|
tht_root, user_tht_root):
|
||||||
|
|
|
@ -103,7 +103,7 @@ class Deploy(command.Command):
|
||||||
deployment_user = None
|
deployment_user = None
|
||||||
ansible_dir = None
|
ansible_dir = None
|
||||||
python_version = sys.version_info[0]
|
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)
|
python_cmd = "python{}".format(python_version)
|
||||||
|
|
||||||
def _is_undercloud_deploy(self, parsed_args):
|
def _is_undercloud_deploy(self, parsed_args):
|
||||||
|
@ -256,13 +256,12 @@ class Deploy(command.Command):
|
||||||
|
|
||||||
def _create_persistent_dirs(self):
|
def _create_persistent_dirs(self):
|
||||||
"""Creates temporary working directories"""
|
"""Creates temporary working directories"""
|
||||||
if not os.path.exists(constants.STANDALONE_EPHEMERAL_STACK_VSTATE):
|
utils.makedirs(constants.STANDALONE_EPHEMERAL_STACK_VSTATE)
|
||||||
os.mkdir(constants.STANDALONE_EPHEMERAL_STACK_VSTATE)
|
|
||||||
|
|
||||||
def _create_working_dirs(self, stack_name='undercloud'):
|
def _create_working_dirs(self, stack_name='undercloud'):
|
||||||
"""Creates temporary working directories"""
|
"""Creates temporary working directories"""
|
||||||
if self.output_dir and not os.path.exists(self.output_dir):
|
if self.output_dir:
|
||||||
os.mkdir(self.output_dir)
|
utils.makedirs(self.output_dir)
|
||||||
if not self.tht_render:
|
if not self.tht_render:
|
||||||
self.tht_render = os.path.join(self.output_dir,
|
self.tht_render = os.path.join(self.output_dir,
|
||||||
'tripleo-heat-installer-templates')
|
'tripleo-heat-installer-templates')
|
||||||
|
@ -901,27 +900,6 @@ class Deploy(command.Command):
|
||||||
yaml.safe_dump(output, f, default_flow_style=False)
|
yaml.safe_dump(output, f, default_flow_style=False)
|
||||||
return output
|
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):
|
def get_parser(self, prog_name):
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description=self.get_description(),
|
description=self.get_description(),
|
||||||
|
@ -1303,38 +1281,46 @@ class Deploy(command.Command):
|
||||||
_('Using the existing %s for deployment') % ansible_config)
|
_('Using the existing %s for deployment') % ansible_config)
|
||||||
shutil.copy(ansible_config, self.ansible_dir)
|
shutil.copy(ansible_config, self.ansible_dir)
|
||||||
|
|
||||||
extra_args = []
|
extra_args = dict()
|
||||||
if not parsed_args.inflight:
|
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.
|
# Kill heat, we're done with it now.
|
||||||
if not parsed_args.keep_running:
|
if not parsed_args.keep_running:
|
||||||
self._kill_heat(parsed_args)
|
self._kill_heat(parsed_args)
|
||||||
if not parsed_args.output_only:
|
if not parsed_args.output_only:
|
||||||
|
operations = list()
|
||||||
if parsed_args.upgrade:
|
if parsed_args.upgrade:
|
||||||
# Run Upgrade tasks before the deployment
|
# Run Upgrade tasks before the deployment
|
||||||
rc = self._launch_ansible(self.ansible_dir,
|
operations.append(
|
||||||
operation='upgrade',
|
constants.DEPLOY_ANSIBLE_ACTIONS['upgrade']
|
||||||
extra_args=extra_args)
|
)
|
||||||
if rc != 0:
|
operations.append(
|
||||||
raise exceptions.DeploymentError('Upgrade failed')
|
constants.DEPLOY_ANSIBLE_ACTIONS['deploy']
|
||||||
rc = self._launch_ansible(self.ansible_dir,
|
)
|
||||||
extra_args=extra_args)
|
|
||||||
if rc != 0:
|
|
||||||
raise exceptions.DeploymentError('Deployment failed')
|
|
||||||
if parsed_args.upgrade:
|
if parsed_args.upgrade:
|
||||||
# Run Post Upgrade tasks after the deployment
|
# Run Post Upgrade tasks after the deployment
|
||||||
rc = self._launch_ansible(self.ansible_dir,
|
operations.append(
|
||||||
operation='post-upgrade',
|
constants.DEPLOY_ANSIBLE_ACTIONS['post-upgrade']
|
||||||
extra_args=extra_args)
|
)
|
||||||
if rc != 0:
|
|
||||||
raise exceptions.DeploymentError('Post Upgrade failed')
|
|
||||||
# Run Online Upgrade tasks after the deployment
|
# Run Online Upgrade tasks after the deployment
|
||||||
rc = self._launch_ansible(self.ansible_dir,
|
operations.append(
|
||||||
operation='online-upgrade',
|
constants.DEPLOY_ANSIBLE_ACTIONS['online-upgrade']
|
||||||
extra_args=extra_args)
|
)
|
||||||
if rc != 0:
|
with utils.Pushd(self.ansible_dir):
|
||||||
raise exceptions.DeploymentError(
|
for operation in operations:
|
||||||
'Online Upgrade failed')
|
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:
|
except Exception as e:
|
||||||
self.log.error("Exception: %s" % six.text_type(e))
|
self.log.error("Exception: %s" % six.text_type(e))
|
||||||
self.log.error(traceback.print_exc())
|
self.log.error(traceback.print_exc())
|
||||||
|
@ -1415,7 +1401,6 @@ class Deploy(command.Command):
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
self.log.debug("take_action(%s)" % parsed_args)
|
self.log.debug("take_action(%s)" % parsed_args)
|
||||||
utils.ansible_symlink()
|
|
||||||
unconf_msg = _('User did not confirm upgrade, so exiting. '
|
unconf_msg = _('User did not confirm upgrade, so exiting. '
|
||||||
'Consider using the --yes parameter if you '
|
'Consider using the --yes parameter if you '
|
||||||
'prefer to skip this warning in the future')
|
'prefer to skip this warning in the future')
|
||||||
|
|
|
@ -17,7 +17,6 @@ import argparse
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pwd
|
|
||||||
import six
|
import six
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
|
@ -363,23 +362,6 @@ class TripleOValidatorRun(command.Command):
|
||||||
|
|
||||||
return parser
|
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):
|
def _run_validator_run(self, parsed_args):
|
||||||
LOG = logging.getLogger(__name__ + ".ValidationsRunAnsible")
|
LOG = logging.getLogger(__name__ + ".ValidationsRunAnsible")
|
||||||
playbooks = []
|
playbooks = []
|
||||||
|
@ -407,9 +389,6 @@ class TripleOValidatorRun(command.Command):
|
||||||
for pb in parsed_args.validation_name:
|
for pb in parsed_args.validation_name:
|
||||||
playbooks.append(pb + '.yaml')
|
playbooks.append(pb + '.yaml')
|
||||||
|
|
||||||
python_interpreter = \
|
|
||||||
"/usr/bin/python{}".format(sys.version_info[0])
|
|
||||||
|
|
||||||
static_inventory = oooutils.get_tripleo_ansible_inventory(
|
static_inventory = oooutils.get_tripleo_ansible_inventory(
|
||||||
ssh_user='heat-admin',
|
ssh_user='heat-admin',
|
||||||
stack=parsed_args.plan,
|
stack=parsed_args.plan,
|
||||||
|
@ -418,28 +397,27 @@ class TripleOValidatorRun(command.Command):
|
||||||
|
|
||||||
failed_val = False
|
failed_val = False
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=parsed_args.workers) as executor:
|
with oooutils.TempDirs() as tmp:
|
||||||
LOG.debug(_('Running the validations with Ansible'))
|
with ThreadPoolExecutor(max_workers=parsed_args.workers) as exe:
|
||||||
tasks_exec = {
|
LOG.debug(_('Running the validations with Ansible'))
|
||||||
executor.submit(
|
tasks_exec = {
|
||||||
self._run_ansible,
|
exe.submit(
|
||||||
logger=LOG,
|
oooutils.run_ansible_playbook,
|
||||||
plan=parsed_args.plan,
|
plan=parsed_args.plan,
|
||||||
workdir=constants.ANSIBLE_VALIDATION_DIR,
|
workdir=tmp,
|
||||||
log_path_dir=pwd.getpwuid(os.getuid()).pw_dir,
|
playbook=playbook,
|
||||||
playbook=playbook,
|
playbook_dir=constants.ANSIBLE_VALIDATION_DIR,
|
||||||
inventory=static_inventory,
|
inventory=static_inventory,
|
||||||
retries=False,
|
output_callback='validation_output',
|
||||||
output_callback='validation_output',
|
quiet=True,
|
||||||
extra_vars=extra_vars_input,
|
extra_vars=extra_vars_input,
|
||||||
python_interpreter=python_interpreter,
|
gathering_policy='explicit'): playbook
|
||||||
gathering_policy='explicit'): playbook
|
for playbook in playbooks
|
||||||
for playbook in playbooks
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for tk, pl in six.iteritems(tasks_exec):
|
for tk, pl in six.iteritems(tasks_exec):
|
||||||
try:
|
try:
|
||||||
rc, output = tk.result()
|
_rc, output = tk.result()
|
||||||
print('[SUCCESS] - {}\n{}'.format(pl, oooutils.indent(output)))
|
print('[SUCCESS] - {}\n{}'.format(pl, oooutils.indent(output)))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
failed_val = True
|
failed_val = True
|
||||||
|
|
|
@ -417,8 +417,7 @@ def prepare_undercloud_deploy(upgrade=False, no_validations=True,
|
||||||
# we move any user provided environment files into this root later.
|
# we move any user provided environment files into this root later.
|
||||||
tempdir = os.path.join(os.path.abspath(CONF['output_dir']),
|
tempdir = os.path.join(os.path.abspath(CONF['output_dir']),
|
||||||
'tripleo-config-generated-env-files')
|
'tripleo-config-generated-env-files')
|
||||||
if not os.path.isdir(tempdir):
|
utils.makedirs(tempdir)
|
||||||
os.mkdir(tempdir)
|
|
||||||
|
|
||||||
# Set the undercloud home dir parameter so that stackrc is produced in
|
# Set the undercloud home dir parameter so that stackrc is produced in
|
||||||
# the users home directory.
|
# the users home directory.
|
||||||
|
@ -705,8 +704,7 @@ def prepare_undercloud_deploy(upgrade=False, no_validations=True,
|
||||||
deploy_args += ['--deployment-user', u]
|
deploy_args += ['--deployment-user', u]
|
||||||
|
|
||||||
deploy_args += ['--output-dir=%s' % CONF['output_dir']]
|
deploy_args += ['--output-dir=%s' % CONF['output_dir']]
|
||||||
if not os.path.isdir(CONF['output_dir']):
|
utils.makedirs(CONF['output_dir'])
|
||||||
os.mkdir(CONF['output_dir'])
|
|
||||||
|
|
||||||
if CONF.get('cleanup'):
|
if CONF.get('cleanup'):
|
||||||
deploy_args.append('--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]
|
deploy_args += ['--hieradata-override=%s' % data_file]
|
||||||
|
|
||||||
if CONF.get('enable_validations') and not no_validations:
|
if CONF.get('enable_validations') and not no_validations:
|
||||||
utils.ansible_symlink()
|
|
||||||
undercloud_preflight.check(verbose_level, upgrade)
|
undercloud_preflight.check(verbose_level, upgrade)
|
||||||
deploy_args += ['-e', os.path.join(
|
deploy_args += ['-e', os.path.join(
|
||||||
tht_templates, "environments/tripleo-validations.yaml")]
|
tht_templates, "environments/tripleo-validations.yaml")]
|
||||||
|
|
|
@ -84,19 +84,19 @@ def _check_diskspace(upgrade=False):
|
||||||
Second one checks minimal disk space for an upgrade.
|
Second one checks minimal disk space for an upgrade.
|
||||||
"""
|
"""
|
||||||
if upgrade:
|
if upgrade:
|
||||||
playbook = 'undercloud-disk-space-pre-upgrade.yaml'
|
playbook_args = constants.DEPLOY_ANSIBLE_ACTIONS['preflight-upgrade']
|
||||||
else:
|
else:
|
||||||
playbook = 'undercloud-disk-space.yaml'
|
playbook_args = constants.DEPLOY_ANSIBLE_ACTIONS['preflight-deploy']
|
||||||
|
|
||||||
python_interpreter = "/usr/bin/python{}".format(sys.version_info[0])
|
with utils.TempDirs() as tmp:
|
||||||
utils.run_ansible_playbook(logger=LOG,
|
utils.run_ansible_playbook(
|
||||||
workdir=constants.ANSIBLE_VALIDATION_DIR,
|
workdir=tmp,
|
||||||
playbook=playbook,
|
inventory='undercloud,',
|
||||||
inventory='undercloud,',
|
connection='local',
|
||||||
retries=False,
|
output_callback='validation_output',
|
||||||
connection='local',
|
playbook_dir=constants.ANSIBLE_VALIDATION_DIR,
|
||||||
output_callback='validation_output',
|
**playbook_args
|
||||||
python_interpreter=python_interpreter)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _check_memory():
|
def _check_memory():
|
||||||
|
|
|
@ -14,10 +14,8 @@ from __future__ import print_function
|
||||||
import copy
|
import copy
|
||||||
import os
|
import os
|
||||||
import pprint
|
import pprint
|
||||||
import shutil
|
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from heatclient.common import event_utils
|
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))
|
"Check if the user/ip are corrects.\n".format(hosts))
|
||||||
else:
|
else:
|
||||||
log.error("Unknown error. "
|
log.error("Unknown error. "
|
||||||
"Original message is:\n{}".format(hosts, e.message))
|
"Original message is:\n{} {}".format(hosts, e))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise exceptions.DeploymentError("Cannot find any hosts on '{}'"
|
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 StrictHostKeyChecking=no "
|
||||||
"-o PasswordAuthentication=no "
|
"-o PasswordAuthentication=no "
|
||||||
"-o UserKnownHostsFile=/dev/null")
|
"-o UserKnownHostsFile=/dev/null")
|
||||||
tmp_key_dir = tempfile.mkdtemp()
|
|
||||||
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:
|
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"
|
||||||
tmp_key_command = ["ssh-keygen", "-N", "", "-t", "rsa", "-b", "4096",
|
tmp_key_command = ["ssh-keygen", "-N", "", "-t", "rsa", "-b", "4096",
|
||||||
"-f", tmp_key_private, "-C", tmp_key_comment]
|
"-f", tmp_key_private, "-C", tmp_key_comment]
|
||||||
DEVNULL = open(os.devnull, 'w')
|
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]
|
tmp_key_comment]
|
||||||
print("Removing TripleO short term key from %s" % host)
|
print("Removing TripleO short term key from %s" % host)
|
||||||
subprocess.check_call(rm_tmp_key_command, stderr=subprocess.STDOUT)
|
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.")
|
print("Enabling ssh admin - COMPLETE.")
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ from osc_lib.i18n import _
|
||||||
from tripleoclient.exceptions import ContainerDeleteFailed
|
from tripleoclient.exceptions import ContainerDeleteFailed
|
||||||
from tripleoclient.exceptions import DownloadError
|
from tripleoclient.exceptions import DownloadError
|
||||||
from tripleoclient.exceptions import LogFetchError
|
from tripleoclient.exceptions import LogFetchError
|
||||||
|
from tripleoclient import utils
|
||||||
from tripleoclient.workflows import base
|
from tripleoclient.workflows import base
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,9 +45,10 @@ def download_files(clients, container_name, destination):
|
||||||
if not os.path.dirname(destination):
|
if not os.path.dirname(destination):
|
||||||
destination = os.path.join(os.sep, os.getcwd(), destination)
|
destination = os.path.join(os.sep, os.getcwd(), destination)
|
||||||
|
|
||||||
if not os.path.exists(destination):
|
if utils.makedirs(destination):
|
||||||
print('Creating destination path: {}'.format(destination))
|
print('Created destination path: {}'.format(destination))
|
||||||
os.makedirs(destination)
|
else:
|
||||||
|
print('Destination path exists: {}'.format(destination))
|
||||||
|
|
||||||
if not check_local_space(destination, object_list):
|
if not check_local_space(destination, object_list):
|
||||||
raise DownloadError(_('Not enough local space to download files.'))
|
raise DownloadError(_('Not enough local space to download files.'))
|
||||||
|
|
Loading…
Reference in New Issue