diff --git a/tripleoclient/heat_launcher.py b/tripleoclient/heat_launcher.py index eef7cd43c..1138078e7 100644 --- a/tripleoclient/heat_launcher.py +++ b/tripleoclient/heat_launcher.py @@ -22,6 +22,7 @@ import logging import multiprocessing import os import pwd +import shutil import signal import subprocess import tarfile @@ -150,38 +151,33 @@ class HeatBaseLauncher(object): self.skip_heat_pull = skip_heat_pull self.zipped_db_suffix = '.tar.bzip2' self.log_dir = os.path.join(self.heat_dir, 'log') + self.use_tmp_dir = use_tmp_dir - if os.path.isdir(self.heat_dir): - if use_root: - # This one may fail but it's just cleanup. - p = subprocess.Popen(['umount', self.heat_dir], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - cmd_stdout, cmd_stderr = p.communicate() - retval = p.returncode - if retval != 0: - log.info('Cleanup unmount of %s failed (probably because ' - 'it was not mounted): %s' % - (self.heat_dir, cmd_stderr)) - else: - log.info('umount of %s success' % (self.heat_dir)) - else: + if not os.path.isdir(self.heat_dir): # Create the directory if it doesn't exist. try: - os.makedirs(self.heat_dir, mode=0o700) + os.makedirs(self.heat_dir, mode=0o755) except Exception as e: log.error('Creating temp directory "%s" failed: %s' % (self.heat_dir, e)) raise Exception('Could not create temp directory %s: %s' % (self.heat_dir, e)) + if self.use_tmp_dir: + self.install_dir = tempfile.mkdtemp( + prefix='%s/tripleo_deploy-' % self.heat_dir) + else: + self.install_dir = self.heat_dir + if use_root: + self.umount_install_dir() + + if use_root and use_tmp_dir: # As an optimization we mount the tmp directory in a tmpfs (in # memory) filesystem. Depending on your system this can cut the # heat deployment times by half. p = subprocess.Popen(['mount', '-t', 'tmpfs', '-o', 'size=500M', - 'tmpfs', self.heat_dir], + 'tmpfs', self.install_dir], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) @@ -194,12 +190,6 @@ class HeatBaseLauncher(object): 'database %s: %s' % (self.heat_dir, cmd_stderr)) - if use_tmp_dir: - self.install_dir = tempfile.mkdtemp( - prefix='%s/undercloud_deploy-' % self.heat_dir) - else: - self.install_dir = self.heat_dir - self.log_file = self._get_log_file_path() self.sql_db = os.path.join(self.install_dir, 'heat.sqlite') self.config_file = os.path.join(self.install_dir, 'heat.conf') @@ -223,6 +213,21 @@ class HeatBaseLauncher(object): self.kill_heat(None) self.rm_heat() + def umount_install_dir(self): + # This one may fail but it's just cleanup. + p = subprocess.Popen(['umount', self.install_dir], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + cmd_stdout, cmd_stderr = p.communicate() + retval = p.returncode + if retval != 0: + log.info('Cleanup unmount of %s failed (probably because ' + 'it was not mounted): %s' % + (self.heat_dir, cmd_stderr)) + else: + log.info('umount of %s success' % (self.heat_dir)) + def _get_log_file_path(self): return os.path.join(self.install_dir, 'heat.log') @@ -439,12 +444,19 @@ class HeatNativeLauncher(HeatBaseLauncher): def launch_heat(self): os.execvp('heat-all', ['heat-all', '--config-file', self.config_file]) - def heat_db_sync(self): + def heat_db_sync(self, restore_db=False): subprocess.check_call(['heat-manage', '--config-file', self.config_file, 'db_sync']) def kill_heat(self, pid): os.kill(pid, signal.SIGKILL) + if self.use_tmp_dir: + shutil.copytree( + self.install_dir, + os.path.join(self.heat_dir, + 'tripleo_deploy-%s' % self.timestamp)) + self.umount_install_dir() + shutil.rmtree(self.install_dir) class HeatPodLauncher(HeatContainerLauncher): diff --git a/tripleoclient/tests/test_heat_launcher.py b/tripleoclient/tests/test_heat_launcher.py index a9d7e71a3..12fe2b0c9 100644 --- a/tripleoclient/tests/test_heat_launcher.py +++ b/tripleoclient/tests/test_heat_launcher.py @@ -553,6 +553,7 @@ class TestHeatPodLauncherUtils(base.TestCase): utils._local_orchestration_client = None mock_launcher = mock.Mock() mock_launcher.api_port = 1234 + mock_launcher.heat_type = 'pod' mock_get_heat_launcher.return_value = mock_launcher mock_socket.return_value = 'socket' utils.launch_heat() @@ -608,10 +609,12 @@ class TestHeatNativeLauncher(base.TestCase): mock_mkdtemp.return_value = self.tmp_dir def test_install_dir(): - mock_mkdtemp.assert_not_called() + mock_mkdtemp.assert_called() return ("", "") - # Test that tempfile.mkdtemp is *not* called before the tmpfs is setup, - # otherwise the tmpfs will cause the temp dir to be lost + # Test that tempfile.mkdtemp is called before the tmpfs is setup, + # so that the tmpfs mount is created at the temp dir. self.mock_popen.communicate.side_effect = test_install_dir self.get_launcher() + self.assertEqual(['mount', '-t', 'tmpfs'], + self.popen.call_args_list[1][0][0][0:3]) diff --git a/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py b/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py index 9de230426..cd072b3eb 100644 --- a/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py +++ b/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py @@ -782,7 +782,7 @@ class TestDeployUndercloud(TestPluginV1): 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, self.cmd.output_dir) mock_tht.assert_called_once_with(self.cmd, fake_orchestration, parsed_args) mock_download.assert_called_with(self.cmd, fake_orchestration, @@ -878,7 +878,7 @@ class TestDeployUndercloud(TestPluginV1): 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, self.cmd.output_dir) mock_tht.assert_called_once_with(self.cmd, fake_orchestration, parsed_args) mock_download.assert_called_with(self.cmd, fake_orchestration, @@ -958,7 +958,7 @@ class TestDeployUndercloud(TestPluginV1): 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, self.cmd.output_dir) mock_tht.assert_not_called() mock_download.assert_not_called() mock_tarball.assert_called_once() diff --git a/tripleoclient/utils.py b/tripleoclient/utils.py index a2254040f..469ba42ec 100644 --- a/tripleoclient/utils.py +++ b/tripleoclient/utils.py @@ -2714,7 +2714,7 @@ def write_user_environment(env_map, abs_env_path, tht_root, return user_env_path -def launch_heat(launcher=None, restore_db=False): +def launch_heat(launcher=None, restore_db=False, heat_type='pod'): global _local_orchestration_client global _heat_pid @@ -2724,7 +2724,7 @@ def launch_heat(launcher=None, restore_db=False): return _local_orchestration_client if not launcher: - launcher = get_heat_launcher() + launcher = get_heat_launcher(heat_type) _heat_pid = 0 if launcher.heat_type == 'native': @@ -2738,7 +2738,8 @@ def launch_heat(launcher=None, restore_db=False): # Wait for the API to be listening heat_api_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) test_heat_api_port(heat_api_socket, launcher.host, int(launcher.api_port)) - launcher.wait_for_message_queue() + if launcher.heat_type == 'pod': + launcher.wait_for_message_queue() _local_orchestration_client = tc_heat_utils.local_orchestration_client( launcher.host, launcher.api_port) diff --git a/tripleoclient/v1/tripleo_deploy.py b/tripleoclient/v1/tripleo_deploy.py index 17ed29c4a..e572581f7 100644 --- a/tripleoclient/v1/tripleo_deploy.py +++ b/tripleoclient/v1/tripleo_deploy.py @@ -465,19 +465,27 @@ class Deploy(command.Command): pid, ret = os.waitpid(self.heat_pid, 0) self.heat_pid = None - def _launch_heat(self, parsed_args): + def _launch_heat(self, parsed_args, output_dir): # we do this as root to chown config files properly for docker, etc. + heat_launcher_path = os.path.join(output_dir, 'heat_launcher') + + if parsed_args.heat_user: + heat_user = parsed_args.heat_user + else: + heat_user = parsed_args.deployment_user + if parsed_args.heat_native is not None and \ parsed_args.heat_native.lower() == "false": self.heat_launch = heat_launcher.HeatContainerLauncher( - parsed_args.heat_api_port, - parsed_args.heat_container_image, - parsed_args.heat_user) + api_port=parsed_args.heat_api_port, + all_container_image=parsed_args.heat_container_image, + user=heat_user, + heat_dir=heat_launcher_path) else: self.heat_launch = heat_launcher.HeatNativeLauncher( - parsed_args.heat_api_port, - parsed_args.heat_container_image, - parsed_args.heat_user, + api_port=parsed_args.heat_api_port, + user=heat_user, + heat_dir=heat_launcher_path, use_root=True) # NOTE(dprince): we launch heat with fork exec because @@ -490,12 +498,12 @@ class Deploy(command.Command): if parsed_args.heat_native is not None and \ parsed_args.heat_native.lower() == "true": try: - uid = pwd.getpwnam(parsed_args.heat_user).pw_uid - gid = pwd.getpwnam(parsed_args.heat_user).pw_gid + uid = pwd.getpwnam(heat_user).pw_uid + gid = pwd.getpwnam(heat_user).pw_gid except KeyError: msg = _( "Please create a %s user account before " - "proceeding.") % parsed_args.heat_user + "proceeding.") % heat_user self.log.error(msg) raise exceptions.DeploymentError(msg) os.setgid(gid) @@ -966,9 +974,8 @@ class Deploy(command.Command): parser.add_argument( '--heat-user', metavar='', dest='heat_user', - default='heat', help=_('User to execute the non-privileged heat-all process. ' - 'Defaults to heat.') + 'Defaults to the value of --deployment-user.') ) # TODO(cjeanner) drop that once using oslo.privsep parser.add_argument( @@ -1256,7 +1263,7 @@ class Deploy(command.Command): self._set_stack_action(parsed_args) # Launch heat. - orchestration_client = self._launch_heat(parsed_args) + orchestration_client = self._launch_heat(parsed_args, output_dir) # Wait for heat to be ready. utils.wait_api_port_ready(parsed_args.heat_api_port) # Deploy TripleO Heat templates.