Don't use swift plan for existing passwords

We'll use the existing stack environment instead.
When we move to ephemeral heat stack, we'll use
a file. Rotate passwords would generate an environment
with passwords which can then be used in subsequent
deployments.

Also removes the legacy heat resource passwords as we
probably allow ffw upgrades from queens to
wallaby and these passwords would already be in stack
environment.

Depends-On: https://review.opendev.org/c/openstack/python-tripleoclient/+/765808
Change-Id: I7e081a1831bc00e91cd6967c4c404c0037c85d17
This commit is contained in:
ramishra 2021-01-14 15:02:52 +05:30
parent 86d0e65a29
commit fd6df93944
4 changed files with 35 additions and 171 deletions

View File

@ -137,14 +137,6 @@ PASSWORD_PARAMETER_NAMES = (
'SwiftPassword',
'ZaqarPassword',
)
# List of legacy resource names from overcloud.yaml
LEGACY_HEAT_PASSWORD_RESOURCE_NAMES = (
'HeatAuthEncryptionKey',
'HorizonSecret',
'MysqlRootPassword',
'PcsdPassword',
'RabbitCookie',
)
# List of passwords that should not be rotated by default using the
# GeneratePasswordAction because they require some special handling

View File

@ -17,9 +17,7 @@ import json
import os
import sys
from unittest import mock
import yaml
import six
from swiftclient import exceptions as swiftexceptions
@ -501,15 +499,6 @@ class PlanTest(base.TestCase):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': 'overcast',
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
}, default_flow_style=False)
swift.get_object.return_value = ({}, mock_env)
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {}
@ -519,23 +508,14 @@ class PlanTest(base.TestCase):
'endpoint_map': {
'PlacementPublic': {}
},
'value': 'existing_value'
}
mock_orchestration.resources.get.return_value = mock_resource
result = plan_utils.generate_passwords(swift, mock_orchestration)
result = plan_utils.generate_passwords(None, mock_orchestration)
for password_param_name in constants.PASSWORD_PARAMETER_NAMES:
self.assertTrue(password_param_name in result,
"%s is not in %s" % (password_param_name, result))
if password_param_name in \
constants.LEGACY_HEAT_PASSWORD_RESOURCE_NAMES:
self.assertEqual(result[password_param_name], 'existing_value')
else:
self.assertNotEqual(result[password_param_name],
'existing_value')
@mock.patch('tripleo_common.utils.passwords.'
'create_ssh_keypair')
@mock.patch('tripleo_common.utils.passwords.'
@ -552,30 +532,19 @@ class PlanTest(base.TestCase):
mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
'/tmp/bar': {'content': 'Bar'}}
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': constants.DEFAULT_CONTAINER_NAME,
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'passwords': _EXISTING_PASSWORDS.copy()
}, default_flow_style=False)
swift.get_object.return_value = ({}, mock_env)
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {}
'parameter_defaults': _EXISTING_PASSWORDS.copy()
}
mock_resource = mock.MagicMock()
mock_resource.attributes = {
'endpoint_map': {
'PlacementPublic': {}
},
'value': None
}
mock_orchestration.resources.get.return_value = mock_resource
result = plan_utils.generate_passwords(swift, mock_orchestration)
result = plan_utils.generate_passwords(None, mock_orchestration)
# ensure old passwords used and no new generation
self.assertEqual(_EXISTING_PASSWORDS, result)
@ -599,29 +568,16 @@ class PlanTest(base.TestCase):
passwords = _EXISTING_PASSWORDS.copy()
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': constants.DEFAULT_CONTAINER_NAME,
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'passwords': passwords
}, default_flow_style=False)
swift.get_object.return_value = ({}, mock_env)
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {}
'parameter_defaults': passwords
}
mock_resource = mock.MagicMock()
mock_resource.attributes = {
'endpoint_map': None,
'value': None
'endpoint_map': {},
}
mock_orchestration.resources.get.return_value = mock_resource
result = plan_utils.generate_passwords(swift, mock_orchestration)
result = plan_utils.generate_passwords(None, mock_orchestration)
self.assertEqual(
passwords['NovaPassword'],
result['PlacementPassword']
@ -642,20 +598,9 @@ class PlanTest(base.TestCase):
'private_key': 'Bar'}
mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
'/tmp/bar': {'content': 'Bar'}}
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': constants.DEFAULT_CONTAINER_NAME,
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'passwords': _EXISTING_PASSWORDS.copy()
}, default_flow_style=False)
swift.get_object.return_value = ({}, mock_env)
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {}
'parameter_defaults': _EXISTING_PASSWORDS.copy()
}
mock_resource = mock.MagicMock()
@ -663,11 +608,10 @@ class PlanTest(base.TestCase):
'endpoint_map': {
'PlacementPublic': {}
},
'value': 'existing_value'
}
mock_orchestration.resources.get.return_value = mock_resource
result = plan_utils.generate_passwords(swift, mock_orchestration,
result = plan_utils.generate_passwords(None, mock_orchestration,
rotate_passwords=True)
# ensure passwords in the DO_NOT_ROTATE_LIST are not modified
@ -697,19 +641,9 @@ class PlanTest(base.TestCase):
mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
'/tmp/bar': {'content': 'Bar'}}
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': constants.DEFAULT_CONTAINER_NAME,
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'passwords': _EXISTING_PASSWORDS.copy()
}, default_flow_style=False)
swift.get_object.return_value = ({}, mock_env)
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {}
'parameter_defaults': _EXISTING_PASSWORDS.copy()
}
mock_resource = mock.MagicMock()
@ -717,10 +651,8 @@ class PlanTest(base.TestCase):
'endpoint_map': {
'PlacementPublic': {}
},
'value': 'existing_value'
}
mock_orchestration.resources.get.return_value = mock_resource
rotate_list = [
'MistralPassword',
'BarbicanPassword',
@ -731,7 +663,7 @@ class PlanTest(base.TestCase):
'MysqlRootPassword'
]
result = plan_utils.generate_passwords(swift, mock_orchestration,
result = plan_utils.generate_passwords(None, mock_orchestration,
rotate_passwords=True,
rotate_pw_list=rotate_list)
@ -760,24 +692,11 @@ class PlanTest(base.TestCase):
'/tmp/bar': {'content': 'Bar'}}
existing_passwords = _EXISTING_PASSWORDS.copy()
existing_passwords.pop("AdminPassword")
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': constants.DEFAULT_CONTAINER_NAME,
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'passwords': existing_passwords.copy()
}, default_flow_style=False)
swift.get_object.return_value = ({}, mock_env)
existing_passwords["AdminPassword"] = 'ExistingPasswordInHeat'
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {
'AdminPassword': 'ExistingPasswordInHeat',
}
'parameter_defaults': existing_passwords
}
mock_resource = mock.MagicMock()
@ -785,14 +704,10 @@ class PlanTest(base.TestCase):
'endpoint_map': {
'PlacementPublic': {}
},
'value': 'existing_value'
}
mock_orchestration.resources.get.return_value = mock_resource
result = plan_utils.generate_passwords(swift, mock_orchestration)
existing_passwords["AdminPassword"] = "ExistingPasswordInHeat"
# ensure old passwords used and no new generation
result = plan_utils.generate_passwords(None, mock_orchestration)
self.assertEqual(existing_passwords, result)
@mock.patch("tripleo_common.utils.plan.get_role_data")

View File

@ -35,7 +35,8 @@ LOG = logging.getLogger(__name__)
def generate_passwords(stack_env=None,
rotate_passwords=False):
rotate_passwords=False,
rotate_pw_list=None):
"""Create the passwords needed for deploying OpenStack via t-h-t.
This will create the set of passwords required by the undercloud and
@ -44,12 +45,13 @@ def generate_passwords(stack_env=None,
"""
passwords = {}
for name in constants.PASSWORD_PARAMETER_NAMES:
# Support users upgrading from Mitaka or otherwise creating a plan for
# a Heat stack that already exists.
if (stack_env and name in stack_env.get('parameter_defaults', {}) and
not rotate_passwords):
no_rotate = (not rotate_passwords or (
rotate_pw_list and name not in rotate_pw_list)
or name in constants.DO_NOT_ROTATE_LIST)
if no_rotate and (
stack_env and name in stack_env.get(
'parameter_defaults', {})):
passwords[name] = stack_env['parameter_defaults'][name]
elif name in ('CephClientKey', 'CephManilaClientKey', 'CephRgwKey'):
# CephX keys aren't random strings

View File

@ -312,14 +312,13 @@ def update_plan_environment_with_image_parameters(
return env
def generate_passwords(swift, heat, mistral=None,
def generate_passwords(swift=None, heat=None, mistral=None,
container=constants.DEFAULT_CONTAINER_NAME,
rotate_passwords=False, rotate_pw_list=None):
"""Generates passwords needed for Overcloud deployment
This method generates passwords and ensures they are stored in the
plan environment. By default, this method respects previously
generated passwords and adds new passwords as necessary.
This method generates passwords. By default, this method respects
previously generated passwords and in stack environment.
If rotate_passwords is set to True, then passwords will be replaced as
follows:
@ -328,31 +327,14 @@ def generate_passwords(swift, heat, mistral=None,
- otherwise, all passwords not in the DO_NOT_ROTATE list (as they require
special handling, like KEKs and Fernet keys) will be replaced.
"""
if heat is None:
raise RuntimeError('Should provide orchestration client to fetch'
'stack environment')
if rotate_pw_list is None:
rotate_pw_list = []
try:
env = get_env(swift, container)
except swiftexceptions.ClientException as err:
err_msg = ("Error retrieving environment for plan %s: %s" % (
container, err)) # pylint: disable=logging-not-lazy
LOG.exception(err_msg)
return RuntimeError(err_msg)
try:
stack_env = heat.stacks.environment(
stack_id=container)
# legacy heat resource names from overcloud.yaml
# We don't modify these to avoid changing defaults
for pw_res in constants.LEGACY_HEAT_PASSWORD_RESOURCE_NAMES:
try:
res = heat.resources.get(container, pw_res)
param_defaults = stack_env.get('parameter_defaults', {})
param_defaults[pw_res] = res.attributes['value']
except heat_exc.HTTPNotFound:
LOG.debug('Heat resouce not found: %s', pw_res)
pass
except heat_exc.HTTPNotFound:
stack_env = None
@ -370,52 +352,25 @@ def generate_passwords(swift, heat, mistral=None,
passwords = password_utils.generate_passwords(
stack_env=stack_env,
rotate_passwords=rotate_passwords
)
# if passwords don't yet exist in plan environment
if 'passwords' not in env:
env['passwords'] = {}
rotate_passwords=rotate_passwords,
rotate_pw_list=rotate_pw_list)
# NOTE(ansmith): if rabbit password previously generated and
# stored, facilitate upgrade and use for oslo messaging in plan env
if 'RabbitPassword' in env['passwords']:
if 'RabbitPassword' in passwords:
for i in ('RpcPassword', 'NotifyPassword'):
if i not in env['passwords']:
env['passwords'][i] = env['passwords']['RabbitPassword']
if i not in passwords:
passwords[i] = passwords['RabbitPassword']
# NOTE(owalsh): placement previously used NovaPassword
# Default to the same password for PlacementPassword if it is an
# upgrade (i.e NovaPassword is set) so we do not need to update the
# password in keystone
if not placement_extracted and 'NovaPassword' in env['passwords']:
if not placement_extracted and 'NovaPassword' in passwords:
LOG.debug('Setting PlacementPassword to NovaPassword')
env['passwords']['PlacementPassword'] = \
env['passwords']['NovaPassword']
passwords['PlacementPassword'] = passwords['NovaPassword']
# ensure all generated passwords are present in plan env,
# but respect any values previously generated and stored
for name, password in passwords.items():
if name not in env['passwords']:
env['passwords'][name] = password
if rotate_passwords:
if len(rotate_pw_list) > 0:
for name in rotate_pw_list:
env['passwords'][name] = passwords[name]
else:
for name, password in passwords.items():
if name not in constants.DO_NOT_ROTATE_LIST:
env['passwords'][name] = password
try:
put_env(swift, env)
except swiftexceptions.ClientException as err:
err_msg = "Error uploading to container: %s" % err
LOG.exception(err_msg)
raise RuntimeError(err_msg)
return env['passwords']
return passwords
def update_plan_rotate_fernet_keys(swift,