Save temporary working data
This change adds a new install artifact that contains the heat temporary directory, hieradata override file, and the ansible scripts used during the undercloud install. This includes a new lower limit of tripleo-common 9.0.1 Depends-On: Ie00f71b12f56262985c47810be0e80402e9e558e Depends-On: I16f19a121212d989e1184103054eb890ec4b9297 Change-Id: I62d34ea7817d5e62613ddcdbb69f2a7463273ccf Related-Bug: #1761810
This commit is contained in:
parent
50d52c3d23
commit
f6a9761985
|
@ -145,7 +145,7 @@ testrepository==0.0.18
|
||||||
testtools==2.2.0
|
testtools==2.2.0
|
||||||
tooz==1.58.0
|
tooz==1.58.0
|
||||||
traceback2==1.4.0
|
traceback2==1.4.0
|
||||||
tripleo-common==8.5.0
|
tripleo-common==9.0.1
|
||||||
ujson==1.35
|
ujson==1.35
|
||||||
unittest2==1.1.0
|
unittest2==1.1.0
|
||||||
vine==1.1.4
|
vine==1.1.4
|
||||||
|
|
|
@ -16,5 +16,5 @@ simplejson>=3.5.1 # MIT
|
||||||
six>=1.10.0 # MIT
|
six>=1.10.0 # MIT
|
||||||
osc-lib>=1.8.0 # Apache-2.0
|
osc-lib>=1.8.0 # Apache-2.0
|
||||||
websocket-client>=0.44.0 # LGPLv2+
|
websocket-client>=0.44.0 # LGPLv2+
|
||||||
tripleo-common>=8.5.0 # Apache-2.0
|
tripleo-common>=9.0.1 # Apache-2.0
|
||||||
cryptography>=2.1 # BSD/Apache-2.0
|
cryptography>=2.1 # BSD/Apache-2.0
|
||||||
|
|
|
@ -21,6 +21,7 @@ import yaml
|
||||||
from heatclient import exc as hc_exc
|
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.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
|
||||||
|
@ -298,6 +299,8 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
|
|
||||||
self.assertEqual(environment, expected_env)
|
self.assertEqual(environment, expected_env)
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
|
'_create_working_dirs', autospec=True)
|
||||||
@mock.patch('tripleoclient.v1.undercloud_deploy.TripleoInventory',
|
@mock.patch('tripleoclient.v1.undercloud_deploy.TripleoInventory',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
|
@ -306,19 +309,17 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch('tripleoclient.v1.undercloud_deploy.sys.stdout.flush')
|
@mock.patch('tripleoclient.v1.undercloud_deploy.sys.stdout.flush')
|
||||||
@mock.patch('os.path.join', return_value='/twd/inventory.yaml')
|
@mock.patch('os.path.join', return_value='/twd/inventory.yaml')
|
||||||
@mock.patch('tempfile.mkdtemp', autospec=True, return_value='/twd')
|
def test_download_ansible_playbooks(self, mock_join, mock_flush,
|
||||||
def test_download_ansible_playbooks(self, mock_mktemp, mock_join,
|
|
||||||
mock_flush,
|
|
||||||
mock_stack_config, mock_launch_heat,
|
mock_stack_config, mock_launch_heat,
|
||||||
mock_importInv):
|
mock_importInv, createdir_mock):
|
||||||
|
|
||||||
fake_output_dir = '/twd'
|
fake_output_dir = '/twd'
|
||||||
extra_vars = {'Undercloud': {'ansible_connection': 'local'}}
|
extra_vars = {'Undercloud': {'ansible_connection': 'local'}}
|
||||||
mock_inventory = mock.Mock()
|
mock_inventory = mock.Mock()
|
||||||
mock_importInv.return_value = mock_inventory
|
mock_importInv.return_value = mock_inventory
|
||||||
|
self.cmd.output_dir = fake_output_dir
|
||||||
self.cmd._download_ansible_playbooks(mock_launch_heat,
|
self.cmd._download_ansible_playbooks(mock_launch_heat,
|
||||||
'undercloud',
|
'undercloud')
|
||||||
fake_output_dir)
|
|
||||||
self.assertEqual(mock_flush.call_count, 2)
|
self.assertEqual(mock_flush.call_count, 2)
|
||||||
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)
|
||||||
|
@ -337,13 +338,22 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
'-e', 'deploy_server_id=undercloud', '-e',
|
'-e', 'deploy_server_id=undercloud', '-e',
|
||||||
'bootstrap_server_id=undercloud'])
|
'bootstrap_server_id=undercloud'])
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
|
'_create_install_artifact', return_value='/tmp/foo.tar.bzip2')
|
||||||
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
|
'_launch_ansible')
|
||||||
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
|
'_cleanup_working_dirs')
|
||||||
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
|
'_create_working_dirs')
|
||||||
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
'_wait_local_port_ready', autospec=True)
|
'_wait_local_port_ready', autospec=True)
|
||||||
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
'_deploy_tripleo_heat_templates', autospec=True,
|
'_deploy_tripleo_heat_templates', autospec=True,
|
||||||
return_value='undercloud, 0')
|
return_value='undercloud, 0')
|
||||||
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
'_download_ansible_playbooks', autospec=True)
|
'_download_ansible_playbooks', autospec=True,
|
||||||
|
return_value='/foo')
|
||||||
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
'_launch_heat')
|
'_launch_heat')
|
||||||
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
|
@ -357,7 +367,9 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
return_value=('CREATE_COMPLETE', 0))
|
return_value=('CREATE_COMPLETE', 0))
|
||||||
def test_take_action(self, mock_poll, mock_environ, mock_geteuid,
|
def test_take_action(self, mock_poll, mock_environ, mock_geteuid,
|
||||||
mock_puppet, mock_killheat, mock_launchheat,
|
mock_puppet, mock_killheat, mock_launchheat,
|
||||||
mock_download, mock_tht, mock_wait_for_port):
|
mock_download, mock_tht, mock_wait_for_port,
|
||||||
|
mock_createdirs, mock_cleanupdirs,
|
||||||
|
mock_launchansible, mock_tarball):
|
||||||
|
|
||||||
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',
|
||||||
|
@ -373,12 +385,16 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
|
|
||||||
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_puppet.assert_called_once()
|
||||||
mock_launchheat.assert_called_with(parsed_args)
|
mock_launchheat.assert_called_with(parsed_args)
|
||||||
mock_tht.assert_called_once_with(self.cmd, fake_orchestration,
|
mock_tht.assert_called_once_with(self.cmd, fake_orchestration,
|
||||||
parsed_args)
|
parsed_args)
|
||||||
mock_download.assert_called_with(self.cmd, fake_orchestration,
|
mock_download.assert_called_with(self.cmd, fake_orchestration,
|
||||||
'undercloud', '/my')
|
'undercloud')
|
||||||
|
mock_launchansible.assert_called_once()
|
||||||
|
mock_tarball.assert_called_once()
|
||||||
|
mock_cleanupdirs.assert_called_once()
|
||||||
self.assertEqual(mock_killheat.call_count, 2)
|
self.assertEqual(mock_killheat.call_count, 2)
|
||||||
|
|
||||||
@mock.patch('tripleo_common.image.kolla_builder.'
|
@mock.patch('tripleo_common.image.kolla_builder.'
|
||||||
|
@ -403,3 +419,85 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
},
|
},
|
||||||
env
|
env
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@mock.patch('tempfile.NamedTemporaryFile', autospec=True)
|
||||||
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
|
def test_process_hierdata_overrides(self, mock_exists, mock_tmpfile):
|
||||||
|
data = "foo: bar"
|
||||||
|
mock_open = mock.mock_open(read_data=data)
|
||||||
|
with mock.patch('tripleoclient.v1.undercloud_deploy.open', mock_open):
|
||||||
|
self.assertEqual(mock_tmpfile.return_value.__enter__().name,
|
||||||
|
self.cmd._process_hieradata_overrides('/foobar'))
|
||||||
|
|
||||||
|
@mock.patch('os.path.exists', return_value=False)
|
||||||
|
def test_process_hierdata_overrides_bad_input(self, mock_exists):
|
||||||
|
self.assertRaises(exceptions.DeploymentError,
|
||||||
|
self.cmd._process_hieradata_overrides,
|
||||||
|
['/tmp/foobar'])
|
||||||
|
|
||||||
|
@mock.patch('tempfile.NamedTemporaryFile', autospec=True)
|
||||||
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
|
def test_process_hierdata_overrides_tht(self, mock_exists, mock_tmpfile):
|
||||||
|
data = "parameter_defaults:\n UndercloudExtraConfig:\n foo: bar"
|
||||||
|
mock_open = mock.mock_open(read_data=data)
|
||||||
|
with mock.patch('tripleoclient.v1.undercloud_deploy.open', mock_open):
|
||||||
|
self.assertEqual('/foobar',
|
||||||
|
self.cmd._process_hieradata_overrides('/foobar'))
|
||||||
|
|
||||||
|
@mock.patch('os.waitpid')
|
||||||
|
def test_kill_heat(self, wait_mock):
|
||||||
|
self.cmd.heat_pid = 1234
|
||||||
|
wait_mock.return_value = [0, 0]
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(self.cmd, [], [])
|
||||||
|
self.cmd._kill_heat(parsed_args)
|
||||||
|
wait_mock.assert_called_once_with(1234, 0)
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.v1.undercloud_deploy.DeployUndercloud.'
|
||||||
|
'_get_tar_filename',
|
||||||
|
return_value='/tmp/undercloud-install-1.tar.bzip2')
|
||||||
|
@mock.patch('tarfile.open', autospec=True)
|
||||||
|
def test_create_install_artifact(self, mock_open, mock_filename):
|
||||||
|
self.cmd.output_dir = '/tmp'
|
||||||
|
self.cmd.tmp_env_dir = '/tmp/foo'
|
||||||
|
self.cmd.tmp_env_file_name = '/tmp/bar'
|
||||||
|
self.cmd.tmp_ansible_dir = '/tmp/baz'
|
||||||
|
name = self.cmd._create_install_artifact()
|
||||||
|
self.cmd.output_dir = None
|
||||||
|
self.cmd.tmp_env_dir = None
|
||||||
|
self.cmd.tmp_env_file_name = None
|
||||||
|
self.cmd.tmp_ansible_dir = None
|
||||||
|
self.assertEqual(name, '/tmp/undercloud-install-1.tar.bzip2')
|
||||||
|
|
||||||
|
@mock.patch('tempfile.mkdtemp')
|
||||||
|
def test_create_working_dirs(self, mock_tempfile):
|
||||||
|
self.output_dir = None
|
||||||
|
self.cmd.tmp_ansible_dir = None
|
||||||
|
self.cmd.tmp_env_dir = None
|
||||||
|
self.cmd._create_working_dirs()
|
||||||
|
self.assertEqual(mock_tempfile.call_count, 2)
|
||||||
|
|
||||||
|
@mock.patch('os.remove')
|
||||||
|
@mock.patch('shutil.rmtree')
|
||||||
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
|
def test_cleanup_working_dirs(self, mock_exists, mock_rmtree, mock_remove):
|
||||||
|
self.cmd.tmp_env_dir = '/foo'
|
||||||
|
self.cmd.tmp_env_file_name = '/bar'
|
||||||
|
self.cmd.tmp_ansible_dir = '/baz'
|
||||||
|
self.cmd._cleanup_working_dirs()
|
||||||
|
self.assertEqual(mock_exists.call_count, 0)
|
||||||
|
mock_remove.assert_not_called()
|
||||||
|
self.assertEqual(mock_rmtree.call_count, 0)
|
||||||
|
|
||||||
|
@mock.patch('os.remove')
|
||||||
|
@mock.patch('shutil.rmtree')
|
||||||
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
|
def test_cleanup_working_dirs_cleanup(self, mock_exists, mock_rmtree,
|
||||||
|
mock_remove):
|
||||||
|
self.cmd.tmp_env_dir = '/foo'
|
||||||
|
self.cmd.tmp_env_file_name = '/bar'
|
||||||
|
self.cmd.tmp_ansible_dir = '/baz'
|
||||||
|
self.cmd._cleanup_working_dirs(True)
|
||||||
|
self.assertEqual(mock_exists.call_count, 2)
|
||||||
|
mock_remove.assert_called_once_with('/bar')
|
||||||
|
self.assertEqual(mock_rmtree.call_count, 2)
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import netaddr
|
import netaddr
|
||||||
|
@ -24,12 +22,14 @@ import shutil
|
||||||
import six
|
import six
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tarfile
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from cliff import command
|
from cliff import command
|
||||||
|
from datetime import datetime
|
||||||
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 openstackclient.i18n import _
|
from openstackclient.i18n import _
|
||||||
|
@ -59,8 +59,10 @@ class DeployUndercloud(command.Command):
|
||||||
auth_required = False
|
auth_required = False
|
||||||
heat_pid = None
|
heat_pid = None
|
||||||
tht_render = None
|
tht_render = None
|
||||||
|
output_dir = None
|
||||||
tmp_env_dir = None
|
tmp_env_dir = None
|
||||||
tmp_env_file_name = None
|
tmp_env_file_name = None
|
||||||
|
tmp_ansible_dir = None
|
||||||
|
|
||||||
def _symlink(self, src, dst, tmpd='/tmp'):
|
def _symlink(self, src, dst, tmpd='/tmp'):
|
||||||
self.log.debug("Symlinking %s to %s, via temp dir %s" %
|
self.log.debug("Symlinking %s to %s, via temp dir %s" %
|
||||||
|
@ -78,6 +80,79 @@ class DeployUndercloud(command.Command):
|
||||||
finally:
|
finally:
|
||||||
shutil.rmtree(tmp, ignore_errors=True)
|
shutil.rmtree(tmp, ignore_errors=True)
|
||||||
|
|
||||||
|
def _get_tar_filename(self):
|
||||||
|
"""Return tarball name for the install artifacts"""
|
||||||
|
return '%s/undercloud-install-%s.tar.bzip2' % \
|
||||||
|
(self.output_dir,
|
||||||
|
datetime.utcnow().strftime('%Y%m%d%H%M%S'))
|
||||||
|
|
||||||
|
def _create_install_artifact(self):
|
||||||
|
"""Create a tarball of the temporary folders used"""
|
||||||
|
self.log.debug("Preserving deployment artifacts")
|
||||||
|
|
||||||
|
def remove_output_dir(info):
|
||||||
|
"""Tar filter to remove output dir from path"""
|
||||||
|
# leading path to tar is home/stack/ rather than /home/stack
|
||||||
|
leading_path = self.output_dir[1:] + '/'
|
||||||
|
info.name = info.name.replace(leading_path, '')
|
||||||
|
return info
|
||||||
|
|
||||||
|
# tar up working data and put in
|
||||||
|
# output_dir/undercloud-install-TS.tar.bzip2
|
||||||
|
tar_filename = self._get_tar_filename()
|
||||||
|
try:
|
||||||
|
tf = tarfile.open(tar_filename, 'w:bz2')
|
||||||
|
tf.add(self.tmp_env_dir, recursive=True, filter=remove_output_dir)
|
||||||
|
if self.tmp_env_file_name:
|
||||||
|
tf.add(self.tmp_env_file_name, filter=remove_output_dir)
|
||||||
|
tf.add(self.tmp_ansible_dir, recursive=True,
|
||||||
|
filter=remove_output_dir)
|
||||||
|
tf.close()
|
||||||
|
except Exception as ex:
|
||||||
|
self.log.error("Unable to create artifact tarball, %s"
|
||||||
|
% ex.message)
|
||||||
|
return tar_filename
|
||||||
|
|
||||||
|
def _create_working_dirs(self):
|
||||||
|
"""Creates temporary working directories"""
|
||||||
|
if self.output_dir and not os.path.exists(self.output_dir):
|
||||||
|
os.mkdir(self.output_dir)
|
||||||
|
if not self.tmp_env_dir:
|
||||||
|
self.tmp_env_dir = tempfile.mkdtemp(prefix='undercloud-templates-',
|
||||||
|
dir=self.output_dir)
|
||||||
|
if not self.tmp_ansible_dir:
|
||||||
|
self.tmp_ansible_dir = tempfile.mkdtemp(
|
||||||
|
prefix='undercloud-ansible-', dir=self.output_dir)
|
||||||
|
|
||||||
|
def _cleanup_working_dirs(self, cleanup=False):
|
||||||
|
"""Cleanup temporary working directories
|
||||||
|
|
||||||
|
:param cleanup: Set to true if you DO want to cleanup the dirs
|
||||||
|
"""
|
||||||
|
if cleanup:
|
||||||
|
if self.tmp_env_dir and os.path.exists(self.tmp_env_dir):
|
||||||
|
shutil.rmtree(self.tmp_env_dir, ignore_errors=True)
|
||||||
|
# tht_render is a sub-dir of tmp_env_dir
|
||||||
|
self.tht_render = None
|
||||||
|
self.tmp_env_dir = None
|
||||||
|
if self.tmp_env_file_name:
|
||||||
|
try:
|
||||||
|
os.remove(self.tmp_env_file_name)
|
||||||
|
self.tmp_env_file_name = None
|
||||||
|
except Exception as ex:
|
||||||
|
if 'No such file or directory' in six.text_type(ex):
|
||||||
|
pass
|
||||||
|
if self.tmp_ansible_dir and os.path.exists(self.tmp_ansible_dir):
|
||||||
|
shutil.rmtree(self.tmp_ansible_dir)
|
||||||
|
self.tmp_ansible_dir = None
|
||||||
|
else:
|
||||||
|
self.log.warning("Not cleaning temporary directory %s"
|
||||||
|
% self.tmp_env_dir)
|
||||||
|
self.log.warning("Not removing temporary environment file %s"
|
||||||
|
% self.tmp_env_file_name)
|
||||||
|
self.log.warning("Not cleaning ansible directory %s"
|
||||||
|
% self.tmp_ansible_dir)
|
||||||
|
|
||||||
def _get_hostname(self):
|
def _get_hostname(self):
|
||||||
p = subprocess.Popen(["hostname", "-s"], stdout=subprocess.PIPE)
|
p = subprocess.Popen(["hostname", "-s"], stdout=subprocess.PIPE)
|
||||||
return p.communicate()[0].rstrip()
|
return p.communicate()[0].rstrip()
|
||||||
|
@ -239,34 +314,12 @@ class DeployUndercloud(command.Command):
|
||||||
when cleanup is requested.
|
when cleanup is requested.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not parsed_args.cleanup and self.tmp_env_dir:
|
|
||||||
self.log.warning("Not cleaning temporary directory %s"
|
|
||||||
% self.tmp_env_dir)
|
|
||||||
elif self.tht_render:
|
|
||||||
shutil.rmtree(self.tmp_env_dir, ignore_errors=True)
|
|
||||||
# tht_render is a sub-dir of tmp_env_dir
|
|
||||||
self.tht_render = None
|
|
||||||
self.tmp_env_dir = None
|
|
||||||
|
|
||||||
if self.tmp_env_file_name:
|
|
||||||
try:
|
|
||||||
os.remove(self.tmp_env_file_name)
|
|
||||||
self.tmp_env_file_name = None
|
|
||||||
except Exception as ex:
|
|
||||||
if 'No such file or directory' in six.text_type(ex):
|
|
||||||
pass
|
|
||||||
|
|
||||||
if self.heat_pid:
|
if self.heat_pid:
|
||||||
self.heat_launch.kill_heat(self.heat_pid)
|
self.heat_launch.kill_heat(self.heat_pid)
|
||||||
pid, ret = os.waitpid(self.heat_pid, 0)
|
pid, ret = os.waitpid(self.heat_pid, 0)
|
||||||
self.heat_pid = None
|
self.heat_pid = None
|
||||||
|
|
||||||
def _launch_heat(self, parsed_args):
|
def _launch_heat(self, parsed_args):
|
||||||
|
|
||||||
if not os.path.isdir(parsed_args.output_dir):
|
|
||||||
os.mkdir(parsed_args.output_dir)
|
|
||||||
|
|
||||||
# we do this as root to chown config files properly for docker, etc.
|
# we do this as root to chown config files properly for docker, etc.
|
||||||
if parsed_args.heat_native:
|
if parsed_args.heat_native:
|
||||||
self.heat_launch = heat_launcher.HeatNativeLauncher(
|
self.heat_launch = heat_launcher.HeatNativeLauncher(
|
||||||
|
@ -321,8 +374,7 @@ class DeployUndercloud(command.Command):
|
||||||
overcloud-resource-registry-puppet.yaml and passwords files.
|
overcloud-resource-registry-puppet.yaml and passwords files.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.tmp_env_dir = tempfile.mkdtemp(prefix='tripleoclient-',
|
self._create_working_dirs()
|
||||||
dir=parsed_args.output_dir)
|
|
||||||
self.tht_render = os.path.join(self.tmp_env_dir, 'templates')
|
self.tht_render = os.path.join(self.tmp_env_dir, 'templates')
|
||||||
shutil.copytree(parsed_args.templates, self.tht_render, symlinks=True)
|
shutil.copytree(parsed_args.templates, self.tht_render, symlinks=True)
|
||||||
|
|
||||||
|
@ -347,7 +399,7 @@ class DeployUndercloud(command.Command):
|
||||||
environments.insert(0, resource_registry_path)
|
environments.insert(0, resource_registry_path)
|
||||||
|
|
||||||
# this will allow the user to overwrite passwords with custom envs
|
# this will allow the user to overwrite passwords with custom envs
|
||||||
pw_file = self._update_passwords_env(parsed_args.output_dir)
|
pw_file = self._update_passwords_env(self.output_dir)
|
||||||
environments.insert(1, pw_file)
|
environments.insert(1, pw_file)
|
||||||
|
|
||||||
undercloud_env_path = os.path.join(
|
undercloud_env_path = os.path.join(
|
||||||
|
@ -396,7 +448,8 @@ class DeployUndercloud(command.Command):
|
||||||
environments.append(self.tmp_env_file_name)
|
environments.append(self.tmp_env_file_name)
|
||||||
|
|
||||||
if parsed_args.hieradata_override:
|
if parsed_args.hieradata_override:
|
||||||
environments.append(self._process_hieradata_overrides(parsed_args))
|
environments.append(self._process_hieradata_overrides(
|
||||||
|
parsed_args.hieradata_override))
|
||||||
|
|
||||||
return environments
|
return environments
|
||||||
|
|
||||||
|
@ -462,30 +515,30 @@ class DeployUndercloud(command.Command):
|
||||||
|
|
||||||
return "%s/%s" % (stack_name, stack_id)
|
return "%s/%s" % (stack_name, stack_id)
|
||||||
|
|
||||||
def _download_ansible_playbooks(self, client, stack_name, output_dir):
|
def _download_ansible_playbooks(self, client, stack_name):
|
||||||
stack_config = config.Config(client)
|
stack_config = config.Config(client)
|
||||||
|
self._create_working_dirs()
|
||||||
|
|
||||||
self.log.warning('** Downloading undercloud ansible.. **')
|
self.log.warning('** Downloading undercloud ansible.. **')
|
||||||
# python output buffering is making this seem to take forever..
|
# python output buffering is making this seem to take forever..
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
ansible_dir = tempfile.mkdtemp(prefix='tripleo-',
|
stack_config.write_config(stack_config.fetch_config('undercloud'),
|
||||||
suffix='-config',
|
'undercloud',
|
||||||
dir=output_dir)
|
self.tmp_ansible_dir)
|
||||||
stack_config.download_config('undercloud', ansible_dir)
|
|
||||||
|
|
||||||
inventory = TripleoInventory(
|
inventory = TripleoInventory(
|
||||||
hclient=client,
|
hclient=client,
|
||||||
plan_name=stack_name,
|
plan_name=stack_name,
|
||||||
ansible_ssh_user='root')
|
ansible_ssh_user='root')
|
||||||
|
|
||||||
inv_path = os.path.join(ansible_dir, 'inventory.yaml')
|
inv_path = os.path.join(self.tmp_ansible_dir, 'inventory.yaml')
|
||||||
extra_vars = {'Undercloud': {'ansible_connection': 'local'}}
|
extra_vars = {'Undercloud': {'ansible_connection': 'local'}}
|
||||||
inventory.write_static_inventory(inv_path, extra_vars)
|
inventory.write_static_inventory(inv_path, extra_vars)
|
||||||
|
|
||||||
self.log.info('** Downloaded undercloud ansible to %s **' %
|
self.log.info('** Downloaded undercloud ansible to %s **' %
|
||||||
ansible_dir)
|
self.tmp_ansible_dir)
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
return ansible_dir
|
return self.tmp_ansible_dir
|
||||||
|
|
||||||
# Never returns, calls exec()
|
# Never returns, calls exec()
|
||||||
def _launch_ansible(self, ansible_dir):
|
def _launch_ansible(self, ansible_dir):
|
||||||
|
@ -598,7 +651,10 @@ class DeployUndercloud(command.Command):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--cleanup',
|
'--cleanup',
|
||||||
action='store_true', default=False,
|
action='store_true', default=False,
|
||||||
help=_('Cleanup temporary files')
|
help=_('Cleanup temporary files. Using this flag will '
|
||||||
|
'remove the temporary files used during deployment in '
|
||||||
|
'after the command is run.'),
|
||||||
|
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--hieradata-override', nargs='?',
|
'--hieradata-override', nargs='?',
|
||||||
|
@ -613,40 +669,46 @@ class DeployUndercloud(command.Command):
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def _process_hieradata_overrides(self, parsed_args):
|
def _process_hieradata_overrides(self, override_file=None):
|
||||||
"""Count in hiera data overrides including legacy formats
|
"""Count in hiera data overrides including legacy formats
|
||||||
|
|
||||||
Return a file name that points to processed hiera data overrides file
|
Return a file name that points to processed hiera data overrides file
|
||||||
"""
|
"""
|
||||||
target = parsed_args.hieradata_override
|
if not override_file or not os.path.exists(override_file):
|
||||||
|
# we should never get here because there's a check in
|
||||||
|
# undercloud_conf but stranger things have happened.
|
||||||
|
msg = 'hieradata_override file could not be found %s' %\
|
||||||
|
override_file
|
||||||
|
self.log.error(msg)
|
||||||
|
raise exceptions.DeploymentError(msg)
|
||||||
|
|
||||||
|
target = override_file
|
||||||
data = open(target, 'r').read()
|
data = open(target, 'r').read()
|
||||||
hiera_data = yaml.safe_load(data)
|
hiera_data = yaml.safe_load(data)
|
||||||
if not hiera_data:
|
if not hiera_data:
|
||||||
msg = 'Unsupported data format in hieradata override %s' % target
|
msg = 'Unsupported data format in hieradata override %s' % target
|
||||||
self.log.error(msg)
|
self.log.error(msg)
|
||||||
raise exceptions.DeploymentError(msg)
|
raise exceptions.DeploymentError(msg)
|
||||||
|
self._create_working_dirs()
|
||||||
|
|
||||||
# NOTE(bogdando): In t-h-t, hiera data should come in wrapped as
|
# NOTE(bogdando): In t-h-t, hiera data should come in wrapped as
|
||||||
# {parameter_defaults: {UndercloudExtraConfig: ... }}
|
# {parameter_defaults: {UndercloudExtraConfig: ... }}
|
||||||
if ('UndercloudExtraConfig' not in
|
if ('UndercloudExtraConfig' not in hiera_data.get('parameter_defaults',
|
||||||
hiera_data.get('parameter_defaults', {})):
|
{})):
|
||||||
with tempfile.NamedTemporaryFile(
|
template_location = os.path.join(self.tmp_env_dir, 'templates')
|
||||||
dir=parsed_args.output_dir,
|
with tempfile.NamedTemporaryFile(dir=template_location,
|
||||||
delete=parsed_args.cleanup,
|
prefix='hieradata-override',
|
||||||
prefix='tripleoclient-',
|
suffix='.yaml',
|
||||||
suffix=os.path.splitext(
|
delete=False) as override:
|
||||||
os.path.basename(target))[0]) as f:
|
|
||||||
self.log.info('Converting hiera overrides for t-h-t from '
|
self.log.info('Converting hiera overrides for t-h-t from '
|
||||||
'legacy format into a tempfile %s' % f.name)
|
'legacy format into a tempfile %s' %
|
||||||
with open(f.name, 'w') as tht_data:
|
override.name)
|
||||||
yaml.safe_dump(
|
yaml.safe_dump(
|
||||||
{'parameter_defaults': {
|
{'parameter_defaults': {
|
||||||
'UndercloudExtraConfig': hiera_data}},
|
'UndercloudExtraConfig': hiera_data}},
|
||||||
tht_data,
|
override,
|
||||||
default_flow_style=False)
|
default_flow_style=False)
|
||||||
|
target = override.name
|
||||||
target = f.name
|
|
||||||
|
|
||||||
return target
|
return target
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
|
@ -666,6 +728,10 @@ class DeployUndercloud(command.Command):
|
||||||
if os.geteuid() != 0:
|
if os.geteuid() != 0:
|
||||||
raise exceptions.DeploymentError("Please run as root.")
|
raise exceptions.DeploymentError("Please run as root.")
|
||||||
|
|
||||||
|
# prepare working spaces
|
||||||
|
self.output_dir = os.path.abspath(parsed_args.output_dir)
|
||||||
|
self._create_working_dirs()
|
||||||
|
|
||||||
# configure puppet
|
# configure puppet
|
||||||
self._configure_puppet()
|
self._configure_puppet()
|
||||||
|
|
||||||
|
@ -688,8 +754,7 @@ class DeployUndercloud(command.Command):
|
||||||
# download the ansible playbooks and execute them.
|
# download the ansible playbooks and execute them.
|
||||||
ansible_dir = \
|
ansible_dir = \
|
||||||
self._download_ansible_playbooks(orchestration_client,
|
self._download_ansible_playbooks(orchestration_client,
|
||||||
parsed_args.stack,
|
parsed_args.stack)
|
||||||
parsed_args.output_dir)
|
|
||||||
# Kill heat, we're done with it now.
|
# Kill heat, we're done with it now.
|
||||||
self._kill_heat(parsed_args)
|
self._kill_heat(parsed_args)
|
||||||
if not parsed_args.output_only:
|
if not parsed_args.output_only:
|
||||||
|
@ -701,6 +766,11 @@ class DeployUndercloud(command.Command):
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
self._kill_heat(parsed_args)
|
self._kill_heat(parsed_args)
|
||||||
|
tar_filename = self._create_install_artifact()
|
||||||
|
self._cleanup_working_dirs(cleanup=parsed_args.cleanup)
|
||||||
|
if tar_filename:
|
||||||
|
self.log.warning('Install artifact is located at %s' %
|
||||||
|
tar_filename)
|
||||||
if not parsed_args.output_only and rc != 0:
|
if not parsed_args.output_only and rc != 0:
|
||||||
# We only get here on error.
|
# We only get here on error.
|
||||||
self.log.error('ERROR: Heat log files: %s' %
|
self.log.error('ERROR: Heat log files: %s' %
|
||||||
|
|
Loading…
Reference in New Issue