Support using ephemeral Heat

This patch begins to add support for using the ephemeral Heat process
to the overcloud deploy command.

The functionality is enabled with the newly added --heat-type cli arg,
which can be used to select the heat type to be used by the deployment
playbooks and modules.  The options are:

installed: current behavior using Heat installed on the undercloud
pod: ephemeral Heat podman pod (heat-api/engine)
container: ephemeral Heat podman container (heat-all)
native: ephemeral Heat native heat-all process

The default is "installed" initially to preserve the existing
functionality.

Signed-off-by: James Slagle <jslagle@redhat.com>
Implements: blueprint ephemeral-heat-overcloud
Change-Id: I8fb6ca088b1052488ff4f9ada4d3ab29c0be4735
This commit is contained in:
James Slagle 2021-04-20 09:37:05 -04:00
parent 7a1c4e2b0b
commit e8f53ae778
8 changed files with 319 additions and 191 deletions

View File

@ -0,0 +1,7 @@
---
features:
- |
A new cli argument, --heat-type is added to openstack overcloud deploy.
Available options are "installed", "pod", "container", and "native". The
default is "installed". The argument specifies the type of Heat process to
use for the deployment.

View File

@ -54,6 +54,7 @@ DEFAULT_HEAT_ENGINE_CONTAINER = ('{}/{}/openstack-heat-engine:{}'.format(
USER_PARAMETERS = 'user-environments/tripleoclient-parameters.yaml'
PASSWORDS_ENV_FORMAT = '{}-passwords.yaml'
# This directory may contain additional environments to use during deploy
DEFAULT_ENV_DIRECTORY = os.path.join(os.environ.get('HOME', '~/'),

View File

@ -125,6 +125,7 @@ class HeatBaseLauncher(object):
user='heat',
heat_dir='/var/log/heat-launcher',
use_tmp_dir=True,
use_root=False,
rm_heat=False,
skip_heat_pull=False):
self.api_port = api_port
@ -197,11 +198,12 @@ class HeatBaseLauncher(object):
self._write_fake_keystone_token(self.api_port, self.token_file)
self._write_heat_config()
self._write_api_paste_config()
uid = int(self.get_heat_uid())
gid = int(self.get_heat_gid())
os.chown(self.install_dir, uid, gid)
os.chown(self.config_file, uid, gid)
os.chown(self.paste_file, uid, gid)
if use_root:
uid = int(self.get_heat_uid())
gid = int(self.get_heat_gid())
os.chown(self.install_dir, uid, gid)
os.chown(self.config_file, uid, gid)
os.chown(self.paste_file, uid, gid)
def _write_heat_config(self):
# TODO(ksambor) It will be nice to have possibilities to configure heat

View File

@ -259,10 +259,11 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'_validate_args')
@mock.patch('heatclient.common.template_utils.get_template_contents',
autospec=True)
@mock.patch('os.chmod', autospec=True)
@mock.patch('os.chdir', autospec=True)
@mock.patch('tempfile.mkdtemp', autospec=True)
@mock.patch('tripleoclient.utils.makedirs')
def test_tht_deploy(self, mock_md, mock_tmpdir, mock_cd,
def test_tht_deploy(self, mock_md, mock_tmpdir, mock_cd, mock_chmod,
mock_get_template_contents, mock_validate_args,
mock_breakpoints_cleanup, mock_postconfig,
mock_invoke_plan_env_wf,
@ -505,8 +506,10 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_copy.assert_called_once()
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_tripleo_heat_templates', autospec=True)
def test_jinja2_env_path(self, mock_deploy_tht):
'create_params_and_env_files', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'deploy_tripleo_heat_templates', autospec=True)
def test_jinja2_env_path(self, mock_deploy_tht, mock_create_env):
arglist = ['--templates', '-e', 'bad_path.j2.yaml', '-e', 'other.yaml',
'-e', 'bad_path2.j2.yaml']
@ -533,9 +536,11 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'_deploy_postconfig', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_update_parameters', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'create_params_and_env_files', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_heat_deploy', autospec=True)
def test_environment_dirs(self, mock_deploy_heat,
def test_environment_dirs(self, mock_deploy_heat, mock_create_env,
mock_update_parameters, mock_post_config,
mock_stack_network_check, mock_ceph_fsid,
mock_copy, mock_nic_ansible,
@ -593,6 +598,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_rc_params.return_value = {'password': 'password',
'region': 'region1'}
mock_deploy_heat.side_effect = _fake_heat_deploy
mock_create_env.return_value = ({}, [])
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_copy.assert_called_once()
@ -700,8 +706,10 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'_get_undercloud_host_entry', autospec=True,
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_tripleo_heat_templates', autospec=True)
def test_dry_run(self, mock_create_tempest_deployer_input,
'create_params_and_env_files', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'deploy_tripleo_heat_templates', autospec=True)
def test_dry_run(self, mock_deploy, mock_create_env,
mock_get_undercloud_host_entry):
utils_fixture = deployment.UtilsOvercloudFixture()
self.useFixture(utils_fixture)
@ -715,10 +723,11 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
('dry_run', True),
]
mock_create_env.return_value = ({}, [])
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.assertFalse(utils_fixture.mock_deploy_tht.called)
self.assertFalse(mock_create_tempest_deployer_input.called)
self.assertFalse(mock_deploy.called)
@mock.patch('tripleoclient.utils.check_service_vips_migrated_to_service')
@mock.patch('tripleoclient.utils.get_rc_params', autospec=True)
@ -1017,8 +1026,10 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'_get_undercloud_host_entry', autospec=True,
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_tripleo_heat_templates_tmpdir', autospec=True)
def test_deployed_server(self, mock_deploy_tmpdir,
'create_params_and_env_files', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'deploy_tripleo_heat_templates', autospec=True)
def test_deployed_server(self, mock_deploy, mock_create_env,
mock_get_undercloud_host_entry,
mock_copy, mock_rc_params):
fixture = deployment.DeploymentWorkflowFixture()
@ -1039,9 +1050,10 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
clients.compute = mock.Mock()
orchestration_client = clients.orchestration
orchestration_client.stacks.get.return_value = fakes.create_tht_stack()
mock_create_env.return_value = ({}, [])
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.assertTrue(mock_deploy_tmpdir.called)
self.assertTrue(mock_deploy.called)
self.assertNotCalled(clients.baremetal)
self.assertNotCalled(clients.compute)
self.assertTrue(utils_fixture.mock_deploy_tht.called)
@ -1053,9 +1065,11 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'_get_undercloud_host_entry', autospec=True,
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_tripleo_heat_templates_tmpdir', autospec=True)
'create_params_and_env_files', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'deploy_tripleo_heat_templates', autospec=True)
def test_config_download(
self, mock_deploy_tmpdir,
self, mock_deploy, mock_create_env,
mock_get_undercloud_host_entry,
mock_copy, mock_rc_params):
fixture = deployment.DeploymentWorkflowFixture()
@ -1074,8 +1088,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_rc_params.return_value = {'password': 'password',
'region': 'region1'}
mock_create_env.return_value = ({}, [])
self.cmd.take_action(parsed_args)
self.assertTrue(mock_deploy_tmpdir.called)
self.assertTrue(mock_deploy.called)
self.assertTrue(fixture.mock_get_hosts_and_enable_ssh_admin.called)
self.assertTrue(fixture.mock_config_download.called)
self.assertTrue(fixture.mock_set_deployment_status.called)
@ -1091,9 +1106,11 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'_get_undercloud_host_entry', autospec=True,
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_tripleo_heat_templates_tmpdir', autospec=True)
'create_params_and_env_files', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'deploy_tripleo_heat_templates', autospec=True)
def test_config_download_setup_only(
self, mock_deploy_tmpdir,
self, mock_deploy, mock_create_env,
mock_get_undercloud_host_entry,
mock_copy, mock_rc_params):
fixture = deployment.DeploymentWorkflowFixture()
@ -1103,6 +1120,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
clients = self.app.client_manager
orchestration_client = clients.orchestration
orchestration_client.stacks.get.return_value = fakes.create_tht_stack()
mock_create_env.return_value = ({}, [])
arglist = ['--templates', '--config-download', '--setup-only']
verifylist = [
@ -1114,7 +1132,6 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_rc_params.return_value = {'password': 'password',
'region': 'region1'}
self.cmd.take_action(parsed_args)
self.assertTrue(mock_deploy_tmpdir.called)
self.assertTrue(fixture.mock_get_hosts_and_enable_ssh_admin.called)
self.assertTrue(fixture.mock_config_download.called)
self.assertTrue(fixture.mock_set_deployment_status.called)
@ -1124,17 +1141,20 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
)
mock_copy.assert_called_once()
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'create_params_and_env_files', autospec=True)
@mock.patch('tripleoclient.utils.get_rc_params', autospec=True)
@mock.patch('tripleoclient.utils.copy_clouds_yaml')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_undercloud_host_entry', autospec=True,
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_tripleo_heat_templates_tmpdir', autospec=True)
'deploy_tripleo_heat_templates', autospec=True)
def test_config_download_only(
self, mock_deploy_tmpdir,
self, mock_deploy,
mock_get_undercloud_host_entry,
mock_copy, mock_rc_params):
mock_copy, mock_rc_params,
mock_create_parameters_env):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
utils_fixture = deployment.UtilsOvercloudFixture()
@ -1142,6 +1162,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
clients = self.app.client_manager
orchestration_client = clients.orchestration
orchestration_client.stacks.get.return_value = fakes.create_tht_stack()
mock_create_parameters_env.return_value = (mock.Mock(), mock.Mock())
arglist = ['--templates', '--config-download-only']
verifylist = [
@ -1153,7 +1174,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'region': 'region1'}
self.cmd.take_action(parsed_args)
self.assertFalse(mock_deploy_tmpdir.called)
self.assertFalse(mock_deploy.called)
self.assertFalse(fixture.mock_get_hosts_and_enable_ssh_admin.called)
self.assertTrue(fixture.mock_config_download.called)
self.assertTrue(fixture.mock_set_deployment_status.called)
@ -1162,6 +1183,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
fixture.mock_set_deployment_status.call_args[-1]['status'])
mock_copy.assert_called_once()
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'create_params_and_env_files', autospec=True)
@mock.patch('tripleoclient.utils.get_rc_params', autospec=True)
@mock.patch('tripleoclient.utils.copy_clouds_yaml')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
@ -1171,18 +1194,20 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
autospec=True)
@mock.patch('tripleoclient.utils.get_overcloud_endpoint', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_tripleo_heat_templates_tmpdir', autospec=True)
'deploy_tripleo_heat_templates', autospec=True)
def test_config_download_fails(
self, mock_deploy_tmpdir,
self, mock_deploy,
mock_overcloud_endpoint,
mock_create_tempest_deployer_input,
mock_get_undercloud_host_entry,
mock_copy, mock_rc_params):
mock_copy, mock_rc_params,
mock_create_parameters_env):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
clients = self.app.client_manager
orchestration_client = clients.orchestration
orchestration_client.stacks.get.return_value = fakes.create_tht_stack()
mock_create_parameters_env.return_value = (mock.Mock(), mock.Mock())
arglist = ['--templates', '--config-download-only']
verifylist = [
@ -1199,7 +1224,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
exceptions.DeploymentError,
self.cmd.take_action,
parsed_args)
self.assertFalse(mock_deploy_tmpdir.called)
self.assertFalse(mock_deploy.called)
self.assertTrue(fixture.mock_config_download.called)
self.assertTrue(fixture.mock_set_deployment_status.called)
self.assertEqual(
@ -1212,9 +1237,11 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'_get_undercloud_host_entry', autospec=True,
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_tripleo_heat_templates_tmpdir', autospec=True)
'create_params_and_env_files', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'deploy_tripleo_heat_templates', autospec=True)
def test_override_ansible_cfg(
self, mock_deploy_tmpdir,
self, mock_deploy, mock_create_env,
mock_get_undercloud_host_entry,
mock_copy, mock_rc_params):
fixture = deployment.DeploymentWorkflowFixture()
@ -1235,11 +1262,14 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_rc_params.return_value = {'password': 'password',
'region': 'region1'}
mock_create_env.return_value = ({}, [])
self.cmd.take_action(parsed_args)
self.assertTrue(fixture.mock_get_hosts_and_enable_ssh_admin.called)
self.assertTrue(fixture.mock_config_download.called)
mock_copy.assert_called_once()
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'create_params_and_env_files', autospec=True)
@mock.patch('tripleoclient.utils.check_service_vips_migrated_to_service')
@mock.patch('tripleo_common.utils.plan.default_image_params',
autospec=True)
@ -1273,7 +1303,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_process_env, mock_roles_data,
mock_container_prepare, mock_generate_password,
mock_rc_params, mock_default_image_params,
mock_check_service_vip_migr):
mock_check_service_vip_migr,
mock_create_parameters_env):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
utils_fixture = deployment.UtilsOvercloudFixture()
@ -1281,6 +1312,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
clients = self.app.client_manager
orchestration_client = clients.orchestration
orchestration_client.stacks.get.return_value = fakes.create_tht_stack()
mock_create_parameters_env.return_value = ({}, [])
arglist = ['--templates', '--overcloud-ssh-port-timeout', '42',
'--timeout', '451']
@ -1294,18 +1326,13 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_rc_params.return_value = {'password': 'password',
'region': 'region1'}
# assuming heat deploy consumed a 3m out of total 451m timeout
with mock.patch('time.time', side_effect=[0, 1585820346,
0, 12345678, 0,
with mock.patch('time.time', side_effect=[1585820346,
12345678, 0, 0,
1585820526]):
self.cmd.take_action(parsed_args)
self.assertIn(
[mock.call(mock.ANY, mock.ANY, 'overcloud', mock.ANY,
{'StackAction': 'UPDATE',
'DeployIdentifier': 12345678,
'RootStackName': 'overcloud',
'UndercloudHostsEntries':
['192.168.0.1 uc.ctlplane.localhost uc.ctlplane'],
'CtlplaneNetworkAttributes': {}}, mock.ANY,
{'StackAction': 'UPDATE'}, mock.ANY,
451, mock.ANY, mock.ANY, False, None,
deployment_options={}, env_files_tracker=mock.ANY)],
mock_hd.mock_calls)
@ -1333,9 +1360,11 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'_get_undercloud_host_entry', autospec=True,
return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_tripleo_heat_templates_tmpdir', autospec=True)
'create_params_and_env_files', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'deploy_tripleo_heat_templates', autospec=True)
def test_config_download_only_timeout(
self, mock_deploy_tmpdir,
self, mock_deploy, mock_create_env,
mock_get_undercloud_host_entry, mock_update,
mock_copyi, mock_rc_params, mock_cd_dir):
utils_fixture = deployment.UtilsOvercloudFixture()
@ -1362,6 +1391,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_rc_params.return_value = {'password': 'password',
'region': 'region1'}
mock_create_env.return_value = ({}, [])
self.cmd.take_action(parsed_args)
playbook = os.path.join(os.environ.get(
'HOME'), self.cmd.working_dir,

View File

@ -52,7 +52,6 @@ from heatclient.common import template_utils
from heatclient.common import utils as heat_utils
from heatclient.exc import HTTPNotFound
from osc_lib import exceptions as oscexc
from osc_lib import utils as osc_lib_utils
from osc_lib.i18n import _
from oslo_concurrency import processutils
from six.moves import configparser
@ -71,6 +70,14 @@ from tripleoclient import constants
from tripleoclient import exceptions
from tripleoclient import heat_launcher
try:
# TODO(slagle): the try/except can be removed once tripleo_common is
# released with
# https://review.opendev.org/c/openstack/tripleo-common/+/787819
from tripleo_common.utils import heat as tc_heat_utils
except ImportError:
tc_heat_utils = None
LOG = logging.getLogger(__name__ + ".utils")
_local_orchestration_client = None
@ -2566,6 +2573,11 @@ def write_user_environment(env_map, abs_env_path, tht_root,
def launch_heat(launcher=None, restore_db=False):
if not tc_heat_utils:
msg = "tripleo-common too old to use ephemeral Heat"
LOG.error(msg)
raise Exception(msg)
global _local_orchestration_client
global _heat_pid
@ -2589,7 +2601,7 @@ def launch_heat(launcher=None, restore_db=False):
heat_api_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
test_heat_api_port(heat_api_socket, launcher.host, int(launcher.api_port))
_local_orchestration_client = local_orchestration_client(
_local_orchestration_client = tc_heat_utils.local_orchestration_client(
launcher.host, launcher.api_port)
return _local_orchestration_client
@ -2609,41 +2621,6 @@ def get_heat_launcher(heat_type, *args, **kwargs):
return heat_launcher.HeatPodLauncher(*args, **kwargs)
def local_orchestration_client(host="127.0.0.1", api_port=8006):
"""Returns a local orchestration service client"""
API_VERSIONS = {
'1': 'heatclient.v1.client.Client',
}
heat_client = osc_lib_utils.get_client_class(
'tripleoclient',
'1',
API_VERSIONS)
LOG.debug('Instantiating local_orchestration client for '
'host %s, port %s: %s',
host, api_port, heat_client)
endpoint = 'http://%s:%s/v1/admin' % (host, api_port)
client = heat_client(
endpoint=endpoint,
username='admin',
password='fake',
region_name='regionOne',
token='fake',
)
for v in ('OS_USER_DOMAIN_NAME',
'OS_PROJECT_DOMAIN_NAME',
'OS_PROJECT_NAME'):
os.environ.pop(v, None)
os.environ['OS_AUTH_TYPE'] = "none"
os.environ['OS_ENDPOINT'] = endpoint
return client
def kill_heat(launcher, backup_db=True):
global _heat_pid
if _heat_pid:

View File

@ -77,59 +77,44 @@ class DeployOvercloud(command.Command):
answers['environments'].extend(args.environment_files)
args.environment_files = answers['environments']
def _update_parameters(self, args, stack, tht_root, user_tht_root):
parameters = {}
stack_is_new = stack is None
def _update_parameters(self, args, parameters,
tht_root, user_tht_root):
parameters['RootStackName'] = args.stack
if not args.skip_deploy_identifier:
parameters['DeployIdentifier'] = int(time.time())
else:
parameters['DeployIdentifier'] = ''
parameters['StackAction'] = 'CREATE' if stack_is_new else 'UPDATE'
if args.heat_type != 'installed':
heat = None
else:
heat = self.orchestration_client
# We need the processed env for the image parameters atm
env_files = []
env_files.append(
os.path.join(tht_root, constants.DEFAULT_RESOURCE_REGISTRY))
if args.environment_directories:
env_files.extend(utils.load_environment_directories(
args.environment_directories))
if args.environment_files:
env_files.extend(args.environment_files)
_, env = utils.process_multiple_environments(
env_files, tht_root, user_tht_root,
cleanup=(not args.no_cleanup))
default_image_params = plan_utils.default_image_params()
image_params = {}
if not args.disable_container_prepare:
image_params = kolla_builder.container_images_prepare_multi(
env, roles.get_roles_data(args.roles_file,
tht_root), dry_run=True)
if image_params:
default_image_params.update(image_params)
parameters.update(default_image_params)
# Check for existing passwords file
password_params_path = os.path.join(
self.working_dir,
constants.PASSWORDS_ENV_FORMAT.format(args.stack))
if os.path.exists(password_params_path):
with open(password_params_path, 'r') as f:
passwords_env = yaml.safe_load(f.read())
else:
passwords_env = None
password_params = plan_utils.generate_passwords(
None, self.orchestration_client,
args.stack)
None, heat, args.stack, passwords_env=passwords_env)
# Save generated passwords file
with open(password_params_path, 'w') as f:
f.write(yaml.safe_dump(dict(parameter_defaults=password_params)))
os.chmod(password_params_path, 0o600)
parameters.update(password_params)
param_args = (
('NtpServer', 'ntp_server'),
('NovaComputeLibvirtType', 'libvirt_type'),
)
if stack_is_new:
new_stack_args = (
('NovaComputeLibvirtType', 'libvirt_type'),
)
param_args = param_args + new_stack_args
# Update parameters from commandline
for param, arg in param_args:
if getattr(args, arg, None) is not None:
@ -245,7 +230,7 @@ class DeployOvercloud(command.Command):
template, files, env_files_tracker,
self.log, self.working_dir)
def _deploy_tripleo_heat_templates_tmpdir(self, stack, parsed_args):
def create_template_dirs(self, parsed_args):
tht_root = os.path.abspath(parsed_args.templates)
new_tht_root = "%s/tripleo-heat-templates" % self.working_dir
self.log.debug("Creating working templates tree in %s"
@ -257,38 +242,61 @@ class DeployOvercloud(command.Command):
parsed_args.roles_file,
parsed_args.networks_file,
new_tht_root)
self._deploy_tripleo_heat_templates(stack, parsed_args,
new_tht_root, tht_root)
def _deploy_tripleo_heat_templates(self, stack, parsed_args,
tht_root, user_tht_root):
"""Deploy the fixed templates in TripleO Heat Templates"""
self.log.info("Processing templates in the directory {0}".format(
os.path.abspath(tht_root)))
return new_tht_root, tht_root
def create_params_and_env_files(self, new_tht_root, user_tht_root,
parsed_args):
self.log.debug("Creating Environment files")
created_env_files = []
created_env_files.append(
os.path.join(tht_root, constants.DEFAULT_RESOURCE_REGISTRY))
created_env_files.extend(
self._provision_baremetal(parsed_args, tht_root))
os.path.join(new_tht_root, constants.DEFAULT_RESOURCE_REGISTRY))
if parsed_args.environment_directories:
created_env_files.extend(utils.load_environment_directories(
parsed_args.environment_directories))
created_env_files.extend(
self._provision_baremetal(parsed_args, new_tht_root))
_, env = utils.process_multiple_environments(
created_env_files, new_tht_root, user_tht_root,
cleanup=(not parsed_args.no_cleanup))
default_image_params = plan_utils.default_image_params()
image_params = {}
if not parsed_args.disable_container_prepare:
image_params = kolla_builder.container_images_prepare_multi(
env, roles.get_roles_data(parsed_args.roles_file,
new_tht_root), dry_run=True)
parameters = {}
parameters.update(self._update_parameters(
parsed_args, stack, tht_root, user_tht_root))
if image_params:
default_image_params.update(image_params)
parameters.update(default_image_params)
self._update_parameters(
parsed_args, parameters, new_tht_root, user_tht_root)
return parameters, created_env_files
def deploy_tripleo_heat_templates(self, stack, parsed_args,
new_tht_root, user_tht_root,
parameters, created_env_files):
"""Deploy the fixed templates in TripleO Heat Templates"""
self.log.info("Processing templates in the directory {0}".format(
os.path.abspath(new_tht_root)))
stack_is_new = stack is None
parameters['StackAction'] = 'CREATE' if stack_is_new else 'UPDATE'
param_env = utils.create_parameters_env(
parameters, tht_root, parsed_args.stack)
parameters, new_tht_root, parsed_args.stack)
created_env_files.extend(param_env)
if parsed_args.deployed_server:
created_env_files.append(
os.path.join(tht_root, constants.DEPLOYED_SERVER_ENVIRONMENT))
os.path.join(
new_tht_root,
constants.DEPLOYED_SERVER_ENVIRONMENT))
if parsed_args.environment_files:
created_env_files.extend(parsed_args.environment_files)
@ -300,22 +308,22 @@ class DeployOvercloud(command.Command):
if stack:
env_path = utils.create_breakpoint_cleanup_env(
tht_root, parsed_args.stack)
new_tht_root, parsed_args.stack)
created_env_files.extend(env_path)
self.log.debug("Processing environment files %s" % created_env_files)
env_files_tracker = []
env_files, env = utils.process_multiple_environments(
created_env_files, tht_root, user_tht_root,
created_env_files, new_tht_root, user_tht_root,
env_files_tracker=env_files_tracker,
cleanup=(not parsed_args.no_cleanup))
# Invokes the workflows specified in plan environment file
if parsed_args.plan_environment_file:
output_path = utils.build_user_env_path(
'derived_parameters.yaml', tht_root)
'derived_parameters.yaml', new_tht_root)
workflow_params.build_derived_params_environment(
self.clients, parsed_args.stack, tht_root, env_files,
self.clients, parsed_args.stack, new_tht_root, env_files,
env_files_tracker, parsed_args.roles_file,
parsed_args.plan_environment_file,
output_path, utils.playbook_verbosity(self=self))
@ -323,12 +331,12 @@ class DeployOvercloud(command.Command):
created_env_files.append(output_path)
env_files_tracker = []
env_files, env = utils.process_multiple_environments(
created_env_files, tht_root, user_tht_root,
created_env_files, new_tht_root, user_tht_root,
env_files_tracker=env_files_tracker,
cleanup=(not parsed_args.no_cleanup))
# Copy the env_files to tmp folder for archiving
self._copy_env_files(env_files, tht_root)
self._copy_env_files(env_files, new_tht_root)
if parsed_args.limit:
# check if skip list is defined while using --limit and throw a
@ -365,7 +373,7 @@ class DeployOvercloud(command.Command):
'(with HA).')
self._try_overcloud_deploy_with_compat_yaml(
tht_root, stack,
new_tht_root, stack,
parsed_args.stack, parameters, env_files,
parsed_args.timeout, env,
parsed_args.run_validations,
@ -911,6 +919,35 @@ class DeployOvercloud(command.Command):
'input, output, and generated files will be stored.\n'
'Defaults to "$HOME/overcloud-deploy/<stack>"')
)
parser.add_argument(
'--heat-type',
action='store',
default='installed',
choices=['system', 'pod', 'container', 'native'],
help=_('The type of Heat process to use to execute '
'the deployment.\n'
'installed (Default): Use the system installed '
'Heat.\n'
'pod: Use an ephemeral Heat pod.\n'
'container: Use an ephemeral Heat container.\n'
'native: Use an ephemeral Heat process.')
)
parser.add_argument(
'--rm-heat',
action='store_true',
default=False,
help=_('If specified and --heat-type is container or pod '
'any existing container or pod of a previous '
'ephemeral Heat process will be deleted first. '
'Ignored if --heat-type is native.')
)
parser.add_argument(
'--skip-heat-pull',
action='store_true',
default=False,
help=_('When --heat-type is pod or container, assume '
'the container image has already been pulled ')
)
return parser
def take_action(self, parsed_args):
@ -950,21 +987,64 @@ class DeployOvercloud(command.Command):
parsed_args.environment_files)
self._update_args_from_answers_file(parsed_args)
stack = utils.get_stack(self.orchestration_client, parsed_args.stack)
stack_create = stack is None
if stack_create:
self.log.info("No stack found, will be doing a stack create")
else:
self.log.info("Stack found, will be doing a stack update")
if parsed_args.dry_run:
self.log.info("Validation Finished")
return
self.heat_launcher = None
stack = None
stack_create = None
start = time.time()
if not parsed_args.config_download_only:
self._deploy_tripleo_heat_templates_tmpdir(stack, parsed_args)
new_tht_root, user_tht_root = \
self.create_template_dirs(parsed_args)
parameters, created_env_files = \
self.create_params_and_env_files(
new_tht_root, user_tht_root, parsed_args)
if parsed_args.heat_type != 'installed':
self.log.info("Using tripleo-deploy with "
"ephemeral heat-all for stack operation")
api_container_image = parameters['ContainerHeatApiImage']
engine_container_image = \
parameters['ContainerHeatEngineImage']
self.heat_launcher = utils.get_heat_launcher(
parsed_args.heat_type,
api_container_image=api_container_image,
engine_container_image=engine_container_image,
heat_dir=os.path.join(self.working_dir,
'heat-launcher'),
use_tmp_dir=False,
rm_heat=parsed_args.rm_heat,
skip_heat_pull=parsed_args.skip_heat_pull)
self.orchestration_client = \
utils.launch_heat(self.heat_launcher)
self.clients.orchestration = self.orchestration_client
try:
if parsed_args.heat_type == 'installed':
stack = utils.get_stack(self.orchestration_client,
parsed_args.stack)
stack_create = stack is None
if stack_create:
self.log.info("No stack found, "
"will be doing a stack create")
else:
self.log.info("Stack found, "
"will be doing a stack update")
if not (parsed_args.config_download_only or
parsed_args.setup_only):
self.deploy_tripleo_heat_templates(
stack, parsed_args, new_tht_root, user_tht_root,
parameters, created_env_files)
except Exception:
if parsed_args.heat_type != 'installed' and self.heat_launcher:
self.log.info("Stopping ephemeral heat.")
utils.kill_heat(self.heat_launcher)
utils.rm_heat(self.heat_launcher)
raise
# Get a new copy of the stack after stack update/create. If it was
# a create then the previous stack object would be None.
@ -980,7 +1060,8 @@ class DeployOvercloud(command.Command):
stack.get()
overcloud_endpoint = utils.get_overcloud_endpoint(stack)
horizon_url = deployment.get_horizon_url(
stack=stack.stack_name)
stack=stack.stack_name,
heat_type=parsed_args.heat_type)
rc_params = utils.get_rc_params(
self.orchestration_client,
parsed_args.stack)
@ -993,6 +1074,9 @@ class DeployOvercloud(command.Command):
stack, rc_params, parsed_args.no_proxy,
self.working_dir)
config_download_dir = parsed_args.output_dir or \
os.path.join(self.working_dir, "config-download")
if parsed_args.config_download or parsed_args.setup_only:
self.log.info("Deploying overcloud configuration")
deployment.set_deployment_status(
@ -1009,7 +1093,8 @@ class DeployOvercloud(command.Command):
parsed_args.overcloud_ssh_user,
self.get_key_pair(parsed_args),
parsed_args.overcloud_ssh_port_timeout,
verbosity=utils.playbook_verbosity(self=self)
verbosity=utils.playbook_verbosity(self=self),
heat_type=parsed_args.heat_type
)
if parsed_args.config_download_timeout:
@ -1026,8 +1111,6 @@ class DeployOvercloud(command.Command):
deployment_options['ansible_python_interpreter'] = \
parsed_args.deployment_python_interpreter
config_download_dir = parsed_args.output_dir or \
os.path.join(self.working_dir, "config-download")
deployment.make_config_download_dir(config_download_dir,
parsed_args.stack)
@ -1082,7 +1165,8 @@ class DeployOvercloud(command.Command):
parsed_args.overcloud_ssh_user,
self.get_key_pair(parsed_args),
parsed_args.overcloud_ssh_port_timeout,
verbosity=utils.playbook_verbosity(self=self)
verbosity=utils.playbook_verbosity(self=self),
heat_type=parsed_args.heat_type
)
deployment.set_deployment_status(
@ -1117,6 +1201,11 @@ class DeployOvercloud(command.Command):
rcpath, old_rcpath))
print("Overcloud Deployed {0}".format(deploy_message))
if parsed_args.heat_type != 'installed':
self.log.info("Stopping ephemeral heat.")
utils.kill_heat(self.heat_launcher)
utils.rm_heat(self.heat_launcher)
if parsed_args.output_dir:
ansible_dir = config_download_dir
else:

View File

@ -477,7 +477,8 @@ class Deploy(command.Command):
self.heat_launch = heat_launcher.HeatNativeLauncher(
parsed_args.heat_api_port,
parsed_args.heat_container_image,
parsed_args.heat_user)
parsed_args.heat_user,
use_root=True)
# NOTE(dprince): we launch heat with fork exec because
# we don't want it to inherit our args. Launching heat

View File

@ -28,6 +28,13 @@ from tripleoclient.constants import DEFAULT_WORK_DIR
from tripleoclient import exceptions
from tripleoclient import utils
try:
# TODO(slagle): the try/except can be removed once tripleo_common is
# released with
# https://review.opendev.org/c/openstack/tripleo-common/+/787819
from tripleo_common.utils import heat as tc_heat_utils
except ImportError:
tc_heat_utils = None
_WORKFLOW_TIMEOUT = 360 # 6 * 60 seconds
@ -136,7 +143,7 @@ def get_overcloud_hosts(stack, ssh_network):
def get_hosts_and_enable_ssh_admin(stack, overcloud_ssh_network,
overcloud_ssh_user, overcloud_ssh_key,
overcloud_ssh_port_timeout,
verbosity=0):
verbosity=0, heat_type='installed'):
"""Enable ssh admin access.
Get a list of hosts from a given stack and enable admin ssh across all of
@ -169,7 +176,8 @@ def get_hosts_and_enable_ssh_admin(stack, overcloud_ssh_network,
overcloud_ssh_user,
overcloud_ssh_key,
overcloud_ssh_port_timeout,
verbosity=verbosity
verbosity=verbosity,
heat_type=heat_type
)
else:
raise exceptions.DeploymentError(
@ -181,7 +189,7 @@ def get_hosts_and_enable_ssh_admin(stack, overcloud_ssh_network,
def enable_ssh_admin(stack, hosts, ssh_user, ssh_key, timeout,
verbosity=0):
verbosity=0, heat_type='installed'):
"""Run enable ssh admin access playbook.
:param stack: Stack data.
@ -213,22 +221,28 @@ def enable_ssh_admin(stack, hosts, ssh_user, ssh_key, timeout,
ssh_key
)
)
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
playbook='cli-enable-ssh-admin.yaml',
inventory=','.join(hosts),
workdir=tmp,
playbook_dir=ANSIBLE_TRIPLEO_PLAYBOOKS,
key=ssh_key,
ssh_user=ssh_user,
verbosity=verbosity,
extra_vars={
"ssh_user": ssh_user,
"ssh_servers": hosts,
'tripleo_cloud_name': stack.stack_name
},
ansible_timeout=timeout
)
try:
if heat_type != 'installed' and tc_heat_utils.heatclient:
tc_heat_utils.heatclient.save_environment()
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
playbook='cli-enable-ssh-admin.yaml',
inventory=','.join(hosts),
workdir=tmp,
playbook_dir=ANSIBLE_TRIPLEO_PLAYBOOKS,
key=ssh_key,
ssh_user=ssh_user,
verbosity=verbosity,
extra_vars={
"ssh_user": ssh_user,
"ssh_servers": hosts,
'tripleo_cloud_name': stack.stack_name
},
ansible_timeout=timeout
)
finally:
if heat_type != 'installed' and tc_heat_utils.heatclient:
tc_heat_utils.heatclient.restore_environment()
print("Enabling ssh admin - COMPLETE.")
@ -467,7 +481,8 @@ def config_download(log, clients, stack, ssh_network='ctlplane',
repo.git.commit("--amend", "--no-edit")
def get_horizon_url(stack, verbosity=0):
def get_horizon_url(stack, verbosity=0,
heat_type='installed'):
"""Return horizon URL string.
:params stack: Stack name
@ -475,22 +490,28 @@ def get_horizon_url(stack, verbosity=0):
:returns: string
"""
with utils.TempDirs() as tmp:
horizon_tmp_file = os.path.join(tmp, 'horizon_url')
utils.run_ansible_playbook(
playbook='cli-undercloud-get-horizon-url.yaml',
inventory='localhost,',
workdir=tmp,
playbook_dir=ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=verbosity,
extra_vars={
'stack_name': stack,
'horizon_url_output_file': horizon_tmp_file
}
)
try:
if heat_type != 'installed' and tc_heat_utils.heatclient:
tc_heat_utils.heatclient.save_environment()
with utils.TempDirs() as tmp:
horizon_tmp_file = os.path.join(tmp, 'horizon_url')
utils.run_ansible_playbook(
playbook='cli-undercloud-get-horizon-url.yaml',
inventory='localhost,',
workdir=tmp,
playbook_dir=ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=verbosity,
extra_vars={
'stack_name': stack,
'horizon_url_output_file': horizon_tmp_file
}
)
with open(horizon_tmp_file) as f:
return f.read().strip()
with open(horizon_tmp_file) as f:
return f.read().strip()
finally:
if heat_type != 'installed' and tc_heat_utils.heatclient:
tc_heat_utils.heatclient.restore_environment()
def get_deployment_status(clients, stack_name, working_dir):