Refactor --stack-only, --setup-only, --config-download-only

Refactors the --stack-only, --setup-only, --config-download-only, to work
with ephemeral-heat. The arguments now do the expected behavior and work
with how they previously worked prior to ephemeral heat.

--stack-only: create the stack, download the config. no overcloud node
              changes
--setup-only: ssh admin authorization setup.
--config-download-only: run config-download playbook(s) to configure the
                        overcloud.

Signed-off-by: James Slagle <jslagle@redhat.com>
Depends-On: Ie47abe2d7c5d7891aceb88d9008d45d7922dea15
Change-Id: Ia765c2c2ea548a1dcd20f592eda8741f40a1cf16
This commit is contained in:
James Slagle 2021-08-03 17:53:04 -04:00 committed by Alex Schultz
parent 276a6def10
commit 1cd6f2829f
7 changed files with 165 additions and 152 deletions

View File

@ -0,0 +1,10 @@
---
features:
- |
The cli arguments that control what parts of the deployment to execute
have been refactored to better align with the user expected intention,
--stack-only: create the stack, download the config. no overcloud node
changes
--setup-only: ssh admin authorization setup.
--config-download-only: run config-download playbook(s) to configure the
overcloud.

View File

@ -1396,7 +1396,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'region': 'region1'} 'region': 'region1'}
# assuming heat deploy consumed a 3m out of total 451m timeout # assuming heat deploy consumed a 3m out of total 451m timeout
with mock.patch('time.time', side_effect=[1585820346, with mock.patch('time.time', side_effect=[1585820346,
12345678, 0, 0, 12345678, 0,
1585820526, 0, 1585820526, 0,
0, 0, 0]): 0, 0, 0]):
self.cmd.take_action(parsed_args) self.cmd.take_action(parsed_args)

View File

@ -87,7 +87,7 @@ class TestOvercloudUpgradePrepare(fakes.TestOvercloudUpgradePrepare):
mock_overcloud_deploy.assert_called_once_with(parsed_args) mock_overcloud_deploy.assert_called_once_with(parsed_args)
args, kwargs = mock_overcloud_deploy.call_args args, kwargs = mock_overcloud_deploy.call_args
# Check config_download arg is set to False # Check config_download arg is set to False
self.assertEqual(args[0].config_download, False) self.assertEqual(args[0].stack_only, True)
mock_enable_ssh_admin.assert_called_once_with( mock_enable_ssh_admin.assert_called_once_with(
mock_stack, mock_stack,
parsed_args.overcloud_ssh_network, parsed_args.overcloud_ssh_network,

View File

@ -135,7 +135,7 @@ class TestDeploymentWorkflows(utils.TestCommand):
'ssh_key', 'ssh_networks', 'output_dir', False, 'ssh_key', 'ssh_networks', 'output_dir', False,
'timeout') 'timeout')
self.assertEqual(3, mock_playbook.call_count) self.assertEqual(2, mock_playbook.call_count)
def test_config_download_dirs(self): def test_config_download_dirs(self):
stack = 'teststack' stack = 'teststack'

View File

@ -603,9 +603,6 @@ class DeployOvercloud(command.Command):
def setup_ephemeral_heat(self, parsed_args): def setup_ephemeral_heat(self, parsed_args):
self.log.info("Using ephemeral heat for stack operation") self.log.info("Using ephemeral heat for stack operation")
restore_db = (parsed_args.setup_only or
parsed_args.config_download_only)
# Skip trying to pull the images if they are set to the default # Skip trying to pull the images if they are set to the default
# as they can't be pulled since they are tagged as localhost. # as they can't be pulled since they are tagged as localhost.
# If the images are missing for some reason, podman will still pull # If the images are missing for some reason, podman will still pull
@ -627,8 +624,7 @@ class DeployOvercloud(command.Command):
use_tmp_dir=False, use_tmp_dir=False,
rm_heat=parsed_args.rm_heat, rm_heat=parsed_args.rm_heat,
skip_heat_pull=skip_heat_pull) skip_heat_pull=skip_heat_pull)
self.orchestration_client = \ self.orchestration_client = utils.launch_heat(self.heat_launcher)
utils.launch_heat(self.heat_launcher, restore_db=restore_db)
self.clients.orchestration = self.orchestration_client self.clients.orchestration = self.orchestration_client
def get_parser(self, prog_name): def get_parser(self, prog_name):
@ -828,35 +824,38 @@ class DeployOvercloud(command.Command):
'--config-download', '--config-download',
action='store_true', action='store_true',
default=True, default=True,
help=_('Run deployment via config-download mechanism. This is ' help=_('DEPRECATED: Run deployment via config-download mechanism. '
'now the default, and this CLI options may be removed in ' 'This is now the default, and this CLI options has no '
'the future.') 'effect.')
) )
parser.add_argument( parser.add_argument(
'--no-config-download', '--no-config-download',
'--stack-only', '--stack-only',
action='store_false', action='store_true',
default=False, default=False,
dest='config_download', dest='stack_only',
help=_('Disable the config-download workflow and only create ' help=_('Disable the config-download workflow and only create '
'the stack and associated OpenStack resources. No ' 'the stack and download the config. No software '
'software configuration will be applied.') 'configuration, setup, or any changes will be applied '
'to overcloud nodes.')
) )
parser.add_argument( parser.add_argument(
'--config-download-only', '--config-download-only',
action='store_true', action='store_true',
default=False, default=False,
help=_('Disable the stack create/update, and only run the ' help=_('Disable the stack create and setup, and only run the '
'config-download workflow to apply the software ' 'config-download workflow to apply the software '
'configuration.') 'configuration. Requires that config-download setup '
'was previously completed, either with --stack-only '
'and --setup-only or a full deployment')
) )
parser.add_argument( parser.add_argument(
'--setup-only', '--setup-only',
action='store_true', action='store_true',
default=False, default=False,
help=_('option will automate the setup and download steps ' help=_('Disable the stack and config-download workflow to apply '
'required to prepare the environment for manual ' 'the software configuration and only run the setup to '
'ansible execution.') 'enable ssh connectivity.')
) )
parser.add_argument( parser.add_argument(
'--config-dir', '--config-dir',
@ -1134,15 +1133,38 @@ class DeployOvercloud(command.Command):
stack, parsed_args, new_tht_root, user_tht_root) stack, parsed_args, new_tht_root, user_tht_root)
if parsed_args.heat_type != 'installed': if parsed_args.heat_type != 'installed':
self.setup_ephemeral_heat(parsed_args) ephemeral_heat = True
else: else:
ephemeral_heat = False
self.log.warning( self.log.warning(
("DEPRECATED: Using --heat-type=installed is deprecated " ("DEPRECATED: Using --heat-type=installed is deprecated "
"and will be removed in a future release.")) "and will be removed in a future release."))
# full_deploy means we're doing a full deployment
# e.g., no --*-only args were passed
full_deploy = not (parsed_args.stack_only or parsed_args.setup_only or
parsed_args.config_download_only)
# do_stack is True when:
# --stack-only
# a full deployment
do_stack = (parsed_args.stack_only or full_deploy)
# do_setup is True when:
# --setup-only OR
# a full deployment
do_setup = parsed_args.setup_only or full_deploy
# do_config_download is True when:
# --config-download-only OR
# a full deployment
do_config_download = parsed_args.config_download_only or full_deploy
if ephemeral_heat and do_stack:
self.setup_ephemeral_heat(parsed_args)
config_download_dir = parsed_args.output_dir or \
os.path.join(self.working_dir, "config-download")
try: try:
if not (parsed_args.config_download_only or if do_stack:
parsed_args.setup_only):
self.deploy_tripleo_heat_templates( self.deploy_tripleo_heat_templates(
stack, parsed_args, new_tht_root, stack, parsed_args, new_tht_root,
user_tht_root, created_env_files) user_tht_root, created_env_files)
@ -1151,6 +1173,39 @@ class DeployOvercloud(command.Command):
self.orchestration_client, parsed_args.stack) self.orchestration_client, parsed_args.stack)
utils.save_stack_outputs( utils.save_stack_outputs(
self.orchestration_client, stack, self.working_dir) self.orchestration_client, stack, self.working_dir)
# Download config
config_dir = parsed_args.config_dir or config_download_dir
config_type = parsed_args.config_type
preserve_config_dir = parsed_args.preserve_config_dir
key_file = utils.get_key(parsed_args.stack)
extra_vars = {
'plan': parsed_args.stack,
'config_dir': config_dir,
'preserve_config': preserve_config_dir,
'output_dir': config_download_dir,
'ansible_ssh_user': parsed_args.overcloud_ssh_user,
'ansible_ssh_private_key_file': key_file,
'ssh_network': parsed_args.overcloud_ssh_network,
'python_interpreter':
parsed_args.deployment_python_interpreter,
}
if parsed_args.config_type:
extra_vars['config_type'] = config_type
playbook = 'cli-config-download.yaml'
ansible_work_dir = os.path.join(
self.working_dir, os.path.splitext(playbook)[0])
utils.run_ansible_playbook(
playbook='cli-config-download.yaml',
inventory='localhost,',
workdir=ansible_work_dir,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
reproduce_command=True,
verbosity=utils.playbook_verbosity(self=self),
extra_vars=extra_vars
)
except Exception: except Exception:
if parsed_args.heat_type != 'installed' and self.heat_launcher: if parsed_args.heat_type != 'installed' and self.heat_launcher:
self.log.info("Stopping ephemeral heat.") self.log.info("Stopping ephemeral heat.")
@ -1158,59 +1213,45 @@ class DeployOvercloud(command.Command):
utils.rm_heat(self.heat_launcher, backup_db=True) utils.rm_heat(self.heat_launcher, backup_db=True)
raise 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.
stack = utils.get_stack(
self.orchestration_client, parsed_args.stack)
overcloud_endpoint = None overcloud_endpoint = None
old_rcpath = None old_rcpath = None
rcpath = None rcpath = None
horizon_url = None horizon_url = None
try: try:
# Force fetching of attributes if stack:
stack.get() # Force fetching of attributes
overcloud_endpoint = utils.get_overcloud_endpoint(stack) stack.get()
horizon_url = deployment.get_horizon_url( overcloud_endpoint = utils.get_overcloud_endpoint(stack)
stack=stack.stack_name, horizon_url = deployment.get_horizon_url(
heat_type=parsed_args.heat_type, stack=stack.stack_name,
working_dir=self.working_dir) heat_type=parsed_args.heat_type,
rc_params = utils.get_rc_params( working_dir=self.working_dir)
self.orchestration_client, rc_params = utils.get_rc_params(
parsed_args.stack) self.orchestration_client,
parsed_args.stack)
# For backwards compatibility, we will also write overcloudrc to # For backwards compatibility, we will also write overcloudrc
# $HOME and then self.working_dir. # to $HOME and then self.working_dir.
old_rcpath = deployment.create_overcloudrc( old_rcpath = deployment.create_overcloudrc(
stack, rc_params, parsed_args.no_proxy) stack, rc_params, parsed_args.no_proxy)
rcpath = deployment.create_overcloudrc( rcpath = deployment.create_overcloudrc(
stack, rc_params, parsed_args.no_proxy, stack, rc_params, parsed_args.no_proxy,
self.working_dir) self.working_dir)
config_download_dir = parsed_args.output_dir or \ if do_setup:
os.path.join(self.working_dir, "config-download") deployment.get_hosts_and_enable_ssh_admin(
parsed_args.stack,
if parsed_args.config_download: parsed_args.overcloud_ssh_network,
self.log.info("Deploying overcloud configuration") parsed_args.overcloud_ssh_user,
deployment.set_deployment_status( self.get_key_pair(parsed_args),
stack.stack_name, parsed_args.overcloud_ssh_port_timeout,
status='DEPLOYING', self.working_dir,
working_dir=self.working_dir verbosity=utils.playbook_verbosity(self=self),
heat_type=parsed_args.heat_type
) )
if not parsed_args.config_download_only: if do_config_download:
deployment.get_hosts_and_enable_ssh_admin(
parsed_args.stack,
parsed_args.overcloud_ssh_network,
parsed_args.overcloud_ssh_user,
self.get_key_pair(parsed_args),
parsed_args.overcloud_ssh_port_timeout,
self.working_dir,
verbosity=utils.playbook_verbosity(self=self),
heat_type=parsed_args.heat_type
)
if parsed_args.config_download_timeout: if parsed_args.config_download_timeout:
timeout = parsed_args.config_download_timeout timeout = parsed_args.config_download_timeout
else: else:
@ -1220,61 +1261,45 @@ class DeployOvercloud(command.Command):
raise exceptions.DeploymentError( raise exceptions.DeploymentError(
'Deployment timed out after %sm' % used) 'Deployment timed out after %sm' % used)
if not parsed_args.setup_only: self.log.info("Deploying overcloud configuration")
deployment_options = {}
if parsed_args.deployment_python_interpreter:
deployment_options['ansible_python_interpreter'] = \
parsed_args.deployment_python_interpreter
deployment.make_config_download_dir(config_download_dir,
parsed_args.stack)
deployment.config_download(
self.log,
self.clients,
parsed_args.stack,
parsed_args.overcloud_ssh_network,
config_download_dir,
parsed_args.override_ansible_cfg,
timeout=parsed_args.overcloud_ssh_port_timeout,
verbosity=utils.playbook_verbosity(self=self),
deployment_options=deployment_options,
in_flight_validations=parsed_args.inflight,
deployment_timeout=timeout,
tags=parsed_args.tags,
skip_tags=parsed_args.skip_tags,
limit_hosts=utils.playbook_limit_parse(
limit_nodes=parsed_args.limit
),
forks=parsed_args.ansible_forks,
denyed_hostnames=utils.get_stack_saved_output_item(
'BlacklistedHostnames', self.working_dir))
deployment.set_deployment_status( deployment.set_deployment_status(
stack.stack_name, parsed_args.stack,
status=deploy_status, status='DEPLOYING',
working_dir=self.working_dir) working_dir=self.working_dir
elif not parsed_args.config_download or parsed_args.setup_only: )
# Download config
config_dir = parsed_args.config_dir or config_download_dir
config_type = parsed_args.config_type
preserve_config_dir = parsed_args.preserve_config_dir
extra_vars = {
'plan': stack.stack_name,
'config_dir': config_dir,
'preserve_config': preserve_config_dir
}
if parsed_args.config_type:
extra_vars['config_type'] = config_type
with utils.TempDirs() as tmp: deployment_options = {}
utils.run_ansible_playbook( if parsed_args.deployment_python_interpreter:
playbook='cli-config-download-export.yaml', deployment_options['ansible_python_interpreter'] = \
inventory='localhost,', parsed_args.deployment_python_interpreter
workdir=tmp,
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS, deployment.make_config_download_dir(config_download_dir,
verbosity=utils.playbook_verbosity(self=self), parsed_args.stack)
extra_vars=extra_vars
) deployment.config_download(
self.log,
self.clients,
parsed_args.stack,
parsed_args.overcloud_ssh_network,
config_download_dir,
parsed_args.override_ansible_cfg,
timeout=parsed_args.overcloud_ssh_port_timeout,
verbosity=utils.playbook_verbosity(self=self),
deployment_options=deployment_options,
in_flight_validations=parsed_args.inflight,
deployment_timeout=timeout,
tags=parsed_args.tags,
skip_tags=parsed_args.skip_tags,
limit_hosts=utils.playbook_limit_parse(
limit_nodes=parsed_args.limit
),
forks=parsed_args.ansible_forks,
denyed_hostnames=utils.get_stack_saved_output_item(
'BlacklistedHostnames', self.working_dir))
deployment.set_deployment_status(
parsed_args.stack,
status=deploy_status,
working_dir=self.working_dir)
except (BaseException, Exception): except (BaseException, Exception):
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
deploy_status = 'DEPLOY_FAILED' deploy_status = 'DEPLOY_FAILED'
@ -1286,7 +1311,7 @@ class DeployOvercloud(command.Command):
finally: finally:
try: try:
# Run postconfig on create or force # Run postconfig on create or force
if (stack_create or parsed_args.force_postconfig if (stack or parsed_args.force_postconfig
and not parsed_args.skip_postconfig): and not parsed_args.skip_postconfig):
self._deploy_postconfig(stack, parsed_args) self._deploy_postconfig(stack, parsed_args)
except Exception as e: except Exception as e:
@ -1327,11 +1352,13 @@ class DeployOvercloud(command.Command):
self.log.error('Exception creating overcloud export.') self.log.error('Exception creating overcloud export.')
self.log.error(e) self.log.error(e)
print("Overcloud Endpoint: {0}".format(overcloud_endpoint)) if do_config_download:
print("Overcloud Horizon Dashboard URL: {0}".format(horizon_url)) print("Overcloud Endpoint: {0}".format(overcloud_endpoint))
print("Overcloud rc file: {} and {}".format( print("Overcloud Horizon Dashboard URL: {0}".format(
rcpath, old_rcpath)) horizon_url))
print("Overcloud Deployed {0}".format(deploy_message)) print("Overcloud rc file: {} and {}".format(
rcpath, old_rcpath))
print("Overcloud Deployed {0}".format(deploy_message))
try: try:
if parsed_args.heat_type != 'installed': if parsed_args.heat_type != 'installed':

View File

@ -71,9 +71,9 @@ class UpgradePrepare(DeployOvercloud):
clients = self.app.client_manager clients = self.app.client_manager
# In case of update and upgrade we need to force the # In case of update and upgrade we need to force the
# config_download to false. The heat stack update will be performed # stack_only to true. The heat stack update will be performed
# by DeployOvercloud class but skipping the config download part. # by DeployOvercloud class but skipping the config download part.
parsed_args.config_download = False parsed_args.stack_only = True
# Add the template attribute environment to set noops etc # Add the template attribute environment to set noops etc
templates_dir = (parsed_args.templates or templates_dir = (parsed_args.templates or
constants.TRIPLEO_HEAT_TEMPLATES) constants.TRIPLEO_HEAT_TEMPLATES)

View File

@ -385,30 +385,6 @@ def config_download(log, clients, stack_name, ssh_network='ctlplane',
':'.join(['!{}'.format(i) for i in denyed_hostnames ':'.join(['!{}'.format(i) for i in denyed_hostnames
if i])) if i]))
key_file = utils.get_key(stack_name)
python_interpreter = deployment_options.get('ansible_python_interpreter')
playbook = 'cli-config-download.yaml'
ansible_work_dir = os.path.join(
working_dir, os.path.splitext(playbook)[0])
utils.run_ansible_playbook(
playbook=playbook,
inventory='localhost,',
workdir=ansible_work_dir,
playbook_dir=ANSIBLE_TRIPLEO_PLAYBOOKS,
verbosity=verbosity,
reproduce_command=True,
extra_vars={
'plan': stack_name,
'output_dir': output_dir,
'ansible_ssh_user': ssh_user,
'ansible_ssh_private_key_file': key_file,
'ssh_network': ssh_network,
'python_interpreter': python_interpreter,
'inventory_path': inventory_path
}
)
_log_and_print( _log_and_print(
message='Executing deployment playbook for stack: {}'.format( message='Executing deployment playbook for stack: {}'.format(
stack_name stack_name
@ -438,7 +414,7 @@ def config_download(log, clients, stack_name, ssh_network='ctlplane',
ansible_cfg=override_ansible_cfg, ansible_cfg=override_ansible_cfg,
verbosity=verbosity, verbosity=verbosity,
ssh_user=ssh_user, ssh_user=ssh_user,
key=key_file, key=utils.get_key(stack_name),
limit_hosts=limit_hosts, limit_hosts=limit_hosts,
ansible_timeout=timeout, ansible_timeout=timeout,
reproduce_command=True, reproduce_command=True,