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:
parent
86d0e65a29
commit
fd6df93944
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue