Move GeneratePasswordsAction functionality to utils

Moves the functionality to utils so that it can be used
from ansible module.

Change-Id: Idc7ef827060942bc64f63a05a7283ce65c979456
This commit is contained in:
Rabi Mishra 2020-03-07 23:08:44 +05:30
parent 163d4b3b4b
commit 6f668e350b
4 changed files with 509 additions and 403 deletions

View File

@ -16,7 +16,6 @@
import json
import logging
from heatclient import exc as heat_exc
from mistral_lib import actions
import six
from swiftclient import exceptions as swiftexceptions
@ -25,7 +24,6 @@ from tripleo_common.actions import base
from tripleo_common import constants
from tripleo_common import exception
from tripleo_common.utils import parameters as parameter_utils
from tripleo_common.utils import passwords as password_utils
from tripleo_common.utils import plan as plan_utils
from tripleo_common.utils import stack_parameters as stack_param_utils
from tripleo_common.utils import template as template_utils
@ -130,74 +128,13 @@ class GeneratePasswordsAction(base.TripleOAction):
mistral = self.get_workflow_client(context)
try:
env = plan_utils.get_env(swift, self.container)
except swiftexceptions.ClientException as err:
err_msg = ("Error retrieving environment for plan %s: %s" % (
self.container, err))
LOG.exception(err_msg)
return actions.Result(error=err_msg)
try:
stack_env = heat.stacks.environment(
stack_id=self.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(self.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
passwords = password_utils.generate_passwords(
mistralclient=mistral,
stack_env=stack_env,
rotate_passwords=self.rotate_passwords
)
# if passwords don't yet exist in plan environment
if 'passwords' not in env:
env['passwords'] = {}
# NOTE(ansmith): if rabbit password previously generated and
# stored, facilitate upgrade and use for oslo messaging in plan env
if 'RabbitPassword' in env['passwords']:
for i in ('RpcPassword', 'NotifyPassword'):
if i not in env['passwords']:
env['passwords'][i] = env['passwords']['RabbitPassword']
# 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 self.rotate_passwords:
if len(self.rotate_pw_list) > 0:
for name in self.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:
plan_utils.put_env(swift, env)
except swiftexceptions.ClientException as err:
err_msg = "Error uploading to container: %s" % err
LOG.exception(err_msg)
return actions.Result(error=err_msg)
plan_utils.cache_delete(swift,
self.container,
"tripleo.parameters.get")
return env['passwords']
return plan_utils.generate_passwords(
swift, heat, mistral, container=self.container,
rotate_passwords=self.rotate_passwords,
rotate_pw_list=self.rotate_pw_list)
except Exception as err:
LOG.exception(six.text_type(err))
return actions.Result(six.text_type(err))
class GetPasswordsAction(base.TripleOAction):

View File

@ -156,339 +156,6 @@ _EXISTING_PASSWORDS = {
}
class GeneratePasswordsActionTest(base.TestCase):
@mock.patch('tripleo_common.utils.plan.'
'cache_delete')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_workflow_client', return_value="TestPassword")
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_run(self, mock_get_object_client,
mock_get_workflow_client,
mock_get_snmpd_readonly_user_password,
mock_get_orchestration_client, mock_cache):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
mock_ctx = mock.MagicMock()
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_get_object_client.return_value = swift
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {}
}
mock_resource = mock.MagicMock()
mock_resource.attributes = {
'value': 'existing_value'
}
mock_orchestration.resources.get.return_value = mock_resource
mock_get_orchestration_client.return_value = mock_orchestration
action = parameters.GeneratePasswordsAction()
result = action.run(mock_ctx)
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_cache.assert_called_once_with(
swift,
"overcloud",
"tripleo.parameters.get"
)
@mock.patch('tripleo_common.utils.plan.'
'cache_delete')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
@mock.patch('tripleo_common.utils.passwords.'
'create_ssh_keypair')
@mock.patch('tripleo_common.utils.passwords.'
'create_fernet_keys_repo_structure_and_keys')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_run_passwords_exist(self, mock_get_object_client,
mock_get_workflow_client,
mock_get_snmpd_readonly_user_password,
mock_fernet_keys_setup,
mock_create_ssh_keypair,
mock_get_orchestration_client,
mock_cache):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
'private_key': 'Bar'}
mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
'/tmp/bar': {'content': 'Bar'}}
mock_ctx = mock.MagicMock()
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_get_object_client.return_value = swift
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {}
}
mock_get_orchestration_client.return_value = mock_orchestration
action = parameters.GeneratePasswordsAction()
result = action.run(mock_ctx)
# ensure old passwords used and no new generation
self.assertEqual(_EXISTING_PASSWORDS, result)
mock_cache.assert_called_once_with(
swift,
"overcloud",
"tripleo.parameters.get"
)
@mock.patch('tripleo_common.utils.plan.'
'cache_delete')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
@mock.patch('tripleo_common.utils.passwords.'
'create_ssh_keypair')
@mock.patch('tripleo_common.utils.passwords.'
'create_fernet_keys_repo_structure_and_keys')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_run_rotate_no_rotate_list(self, mock_get_object_client,
mock_get_workflow_client,
mock_get_snmpd_readonly_user_password,
mock_fernet_keys_setup,
mock_create_ssh_keypair,
mock_get_orchestration_client,
mock_cache):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
'private_key': 'Bar'}
mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
'/tmp/bar': {'content': 'Bar'}}
mock_ctx = mock.MagicMock()
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_get_object_client.return_value = swift
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {}
}
mock_resource = mock.MagicMock()
mock_resource.attributes = {
'value': 'existing_value'
}
mock_orchestration.resources.get.return_value = mock_resource
mock_get_orchestration_client.return_value = mock_orchestration
action = parameters.GeneratePasswordsAction(rotate_passwords=True)
result = action.run(mock_ctx)
# ensure passwords in the DO_NOT_ROTATE_LIST are not modified
for name in constants.DO_NOT_ROTATE_LIST:
self.assertEqual(_EXISTING_PASSWORDS[name], result[name])
# ensure all passwords are generated
for name in constants.PASSWORD_PARAMETER_NAMES:
self.assertTrue(name in result, "%s is not in %s" % (name, result))
# ensure new passwords have been generated
self.assertNotEqual(_EXISTING_PASSWORDS, result)
mock_cache.assert_called_once_with(
swift,
"overcloud",
"tripleo.parameters.get"
)
@mock.patch('tripleo_common.utils.plan.'
'cache_delete')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
@mock.patch('tripleo_common.utils.passwords.'
'create_ssh_keypair')
@mock.patch('tripleo_common.utils.passwords.'
'create_fernet_keys_repo_structure_and_keys')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_run_rotate_with_rotate_list(self, mock_get_object_client,
mock_get_workflow_client,
mock_get_snmpd_readonly_user_password,
mock_fernet_keys_setup,
mock_create_ssh_keypair,
mock_get_orchestration_client,
mock_cache):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
'private_key': 'Bar'}
mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
'/tmp/bar': {'content': 'Bar'}}
mock_ctx = mock.MagicMock()
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_get_object_client.return_value = swift
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {}
}
mock_resource = mock.MagicMock()
mock_resource.attributes = {
'value': 'existing_value'
}
mock_orchestration.resources.get.return_value = mock_resource
mock_get_orchestration_client.return_value = mock_orchestration
rotate_list = [
'MistralPassword',
'BarbicanPassword',
'AdminPassword',
'CeilometerMeteringSecret',
'ZaqarPassword',
'NovaPassword',
'MysqlRootPassword'
]
action = parameters.GeneratePasswordsAction(
rotate_passwords=True,
rotate_pw_list=rotate_list
)
result = action.run(mock_ctx)
# ensure only specified passwords are regenerated
for name in constants.PASSWORD_PARAMETER_NAMES:
self.assertTrue(name in result, "%s is not in %s" % (name, result))
if name in rotate_list:
self.assertNotEqual(_EXISTING_PASSWORDS[name], result[name])
else:
self.assertEqual(_EXISTING_PASSWORDS[name], result[name])
mock_cache.assert_called_once_with(
swift,
"overcloud",
"tripleo.parameters.get"
)
@mock.patch('tripleo_common.utils.plan.'
'cache_delete')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_orchestration_client')
@mock.patch('tripleo_common.utils.passwords.'
'create_ssh_keypair')
@mock.patch('tripleo_common.utils.passwords.'
'create_fernet_keys_repo_structure_and_keys')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_workflow_client')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
def test_passwords_exist_in_heat(self, mock_get_object_client,
mock_get_workflow_client,
mock_get_snmpd_readonly_user_password,
mock_fernet_keys_setup,
mock_create_ssh_keypair,
mock_get_orchestration_client,
mock_cache):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
'private_key': 'Bar'}
mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
'/tmp/bar': {'content': 'Bar'}}
existing_passwords = _EXISTING_PASSWORDS.copy()
existing_passwords.pop("AdminPassword")
mock_ctx = mock.MagicMock()
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_get_object_client.return_value = swift
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {
'AdminPassword': 'ExistingPasswordInHeat',
}
}
mock_get_orchestration_client.return_value = mock_orchestration
action = parameters.GeneratePasswordsAction()
result = action.run(mock_ctx)
existing_passwords["AdminPassword"] = "ExistingPasswordInHeat"
# ensure old passwords used and no new generation
self.assertEqual(existing_passwords, result)
mock_cache.assert_called_once_with(
swift,
"overcloud",
"tripleo.parameters.get"
)
class GetPasswordsActionTest(base.TestCase):
@mock.patch('tripleo_common.actions.base.TripleOAction.'

View File

@ -16,10 +16,12 @@
import json
import mock
import os
import yaml
import zlib
from swiftclient import exceptions as swiftexceptions
from tripleo_common import constants
from tripleo_common.tests import base
from tripleo_common.utils import passwords as password_utils
from tripleo_common.utils import plan as plan_utils
@ -77,6 +79,139 @@ CAPABILITIES_DICT = {
}]
}
_EXISTING_PASSWORDS = {
'PlacementPassword': 'VFJeqBKbatYhQm9jja67hufft',
'MistralPassword': 'VFJeqBKbatYhQm9jja67hufft',
'BarbicanPassword': 'MGGQBtgKT7FnywvkcdMwE9nhx',
'BarbicanSimpleCryptoKek': 'dGhpcnR5X3R3b19ieXRlX2tleWJsYWhibGFoYmxhaGg=',
'AdminPassword': 'jFmY8FTpvtF2e4d4ReXvmUP8k',
'CeilometerMeteringSecret': 'CbHTGK4md4Cc8P8ZyzTns6wry',
'ZaqarPassword': 'bbFgCTFbAH8vf9n3xvZCP8aMR',
'NovaPassword': '7dZATgVPwD7Ergs9kTTDMCr7F',
'MysqlRootPassword': 'VqJYpEdKks',
'RabbitCookie': 'BqJYpEdKksAqJYpEdKks',
'HeatAuthEncryptionKey': '9xZXehsKc2HbmFFMKjuqxTJHn',
'PcsdPassword': 'KjEzeitus8eu751a',
'HorizonSecret': 'mjEzeitus8eu751B',
'NovajoinPassword': '7dZATgVPwD7Ergs9kTTDMCr7F',
'IronicPassword': '4hFDgn9ANeVfuqk84pHpD4ksa',
'RedisPassword': 'xjj3QZDcUQmU6Q7NzWBHRUhGd',
'SaharaPassword': 'spFvYGezdFwnTk7NPxgYTbUPh',
'AdminToken': 'jq6G6HyZtj7dcZEvuyhAfjutM',
'CinderPassword': 'dcxC3xyUcrmvzfrrxpAd3REcm',
'CongressPassword': 'DwcKvMqXMuNYYFU4zTCuG4234',
'GlancePassword': 'VqJYNEdKKsGZtgnHct77XBtrV',
'RabbitPassword': 'ahuHRXdPMx9rzCdjD9CJJNCgA',
'RpcPassword': 'ahuHRXdPMx9rzCdjD9CJJNCgA',
'NotifyPassword': 'ahuHRXdPMx9rzCdjD9CJJNCgA',
'CephAdminKey': b'AQCQXtlXAAAAABAAT4Gk+U8EqqStL+JFa9bp1Q==',
'HAProxyStatsPassword': 'P8tbdK6n4YUkTaUyy8XgEVTe6',
'CeilometerPassword': 'RRdpwK6qf2pbKz2UtzxqauAdk',
'GnocchiPassword': 'cRYHcUkMuJeK3vyU9pCaznUZc',
'HeatStackDomainAdminPassword': 'GgTRyWzKYsxK4mReTJ4CM6sMc',
'CephRgwKey': b'AQCQXtlXAAAAABAAUKcqUMu6oMjAXMjoUV4/3A==',
'AodhPassword': '8VZXehsKc2HbmFFMKYuqxTJHn',
'PankoPassword': 'cVZXehsSc2KdmFFMKDudxTLKn',
'OctaviaHeartbeatKey': 'oct-heartbeat-key',
'OctaviaPassword': 'NMl7j3nKk1VVwMxUZC8Cgw==',
'OctaviaServerCertsKeyPassphrase': 'aW5zZWN1cmUta2V5LWRvLW5vdC11c2U=',
'OctaviaCaKeyPassphrase': 'SLj4c3uCk4DDxPwQOG1Heb==',
'ManilaPassword': 'NYJN86Fua3X8AVFWmMhQa2zTH',
'NeutronMetadataProxySharedSecret': 'Q2YgUCwmBkYdqsdhhCF4hbghu',
'CephMdsKey': b'AQCQXtlXAAAAABAAT4Gk+U8EqqStL+JFa9bp1Q==',
'CephManilaClientKey': b'AQANOFFY1NW6AxAAu6jWI3YSOsp2QWusb5Y3DQ==',
'CephMonKey': b'AQCQXtlXAAAAABAA9l+59N3yH+C49Y0JiKeGFg==',
'CephGrafanaAdminPassword': 'NYJN86Fua3X8AVFWmMhQa2zTH',
'CephDashboardAdminPassword': 'NYJN86Fua3X8AVFWmMhQa2zTH',
'SwiftHashSuffix': 'td8mV6k7TYEGKCDvjVBwckpn9',
'SnmpdReadonlyUserPassword': 'TestPassword',
'SwiftPassword': 'z6EWAVfW7CuxvKdzjWTdrXCeg',
'HeatPassword': 'bREnsXtMHKTHxt8XW6NXAYr48',
'MysqlClustercheckPassword': 'jN4RMMWWJ4sycaRwh7UvrAtfX',
'CephClientKey': b'AQCQXtlXAAAAABAAKyc+8St8i9onHyu2mPk+vg==',
'NeutronPassword': 'ZxAjdU2UXCV4GM3WyPKrzAZXD',
'DesignatePassword': 'wHYj7rftFzHMpJKnGxbjjR9CW',
'DesignateRndcKey': 'hB8XaZRd2Tf00jKsyoXpyw==',
'KeystoneCredential0': 'ftJNQ_XlDUK7Lgvv1kdWf3SyqVsrvNDgoNV4kJg3yzw=',
'KeystoneCredential1': 'c4MFq82TQLFLKpiiUjrKkp15dafE2ALcD3jbaIu3rfE=',
'KeystoneFernetKey0': 'O8NSPxr4zXBBAoGIj-5aUmtE7-Jk5a4ptVsEhzJ8Vd8=',
'KeystoneFernetKey1': 'AueoL37kd6eLjV29AG-Ruxu5szW47osgXx6aPOqtI6I=',
'KeystoneFernetKeys': {
'/etc/keystone/fernet-keys/0': {'content': 'IAMAVERYSAFEKEY'},
'/etc/keystone/fernet-keys/1': {'content': 'IALSOAMAVERYSAFEKEY'}
},
'CephClusterFSID': u'97c16f44-b62c-11e6-aed3-185e0f73fdc5',
'Ec2ApiPassword': 'FPvz2WiWxrHVWrmSSvv44bqmr',
'EtcdInitialClusterToken': 'fcVZXehsSc2KdmFFMKDudxTLKa',
'PacemakerRemoteAuthkey':
'bCfHQx4fX7FqENVBbDfBnKvf6FTH6mPfVdNjfzakEjuF4UbmZJHAxWdheEr6feEyZmtM'
'XEd4w3qM8nMVrzjnDCmqAFDmMDQfKcuNgTnqGnkbVUDGpym67Ry4vNCPHyp9tGGyfjNX'
't66csYZTYUHPv6jdJk4HWBjE66v8B3nRpc3FePQ8DRMWX4hcGFNNxapJu7v2frKwq4tD'
'78cc7aPPMGPn8kR3mj7kMP8Ah8VVGXJEtybEvRg4sQ67zEkAzfKggrpXYPK2Qvv9sHKp'
't2VjwZBHTvWKarJjyeMTqbzJyW6JTbm62gqZCr9afZRFQug62pPRduvkUNfUYNPNpqjy'
'yznmeAZPxVseU3jJVxKrxdrgzavKEMtW6BbTmw86j8wuUdaWgRccRGVUQvtQ4p9kXHAy'
'eXVduZvpvxFtbKvfNTvf6qCuJ8qeQp2TwJQPHUYHkxZYrpAA7fZUzNCZR2tFFdZzWGt2'
'PEnYvYts4m7Fp9XEmNm7Jyme38CBfnaVERmTMRvHkq3EE2Amsc72aDdzeVRjR3xRgMNJ'
'2cEEWqatZXveHxJr6VmBNWJUyvPrfmVegwtKCGJND8d3Ysruy7GCn6zcrNY7d84aDk3P'
'q7NyZfRYrGcNDKJuzNWH8UNwGP68uQsUUrV9NVTVpB2sRPG2tJm3unYqekUg3KYXu46J'
'mANxqgrqDv6vPx6NCPdUXZTXFaesQatKRkkf3nZFqZQJXZVbkudTmrPYyRQAjvWuAmrY'
'6RcFFmygeFnhAxhwXNdge9tEfsfPeQ4GMxa8Amj2fMjmNvQXFfQ8uxMUnusDmhbwCRKM'
'CvN2dNE92MaQge34vtxsueyDEmbuVE9sNRD3EQBRwx8nktgRwKHfRZJ3BX8f9XMaQe2e'
'ZfGjtUNkbgKdCyYgEwEybXKPfevDnxFvbZMpJx4fqqCAbAZud9RnAuvqHgFbKHXcVEE4'
'nRmgJmdqJsRsTkYPpYkKN9rssEDCXr9HFjbenkxXcUe8afrTvKAzwBvbDWcjYBEQKbuY'
'6Ptm9VJrjutUHCPmW2sh66qvq4C9vPhVEey7FpCZDEyYUPrjRfhKjxEFNBKWpcZzvmT2'
'nRmgJmdqJsRsTkYPpYkKN9rssEDCXr9HFjbenkxXcUe8afrTvKAzwBvbDWcjYBEQKbuY'
'2cEEWqatZXveHxJr6VmBNWJUyvPrfmVegwtKCGJND8d3Ysruy7GCn6zcrNY7d84aDk3P'
'VRE4aqMfuY72xFacxXHjvWagEGQEYtkMtQnsh7XAMGuazT3pkppeUTyDbKTY2Dz7Quc3'
'8UKaw8ece6fTXWpjX2EYrsd4qzvhC6eEPdgnpmzjqmuG8YqEAUZ7dYADgAhTkBQsNct8'
'btQsQDYD4PBjxG2KWAZ9vgTsvBpjjEVcrPfWgwZKJTAZWfWq2u7nT4N2t39EYmQEzbEf'
'8UKaw8ece6fTXWpjX2EYrsd4qzvhC6eEPdgnpmzjqmuG8YqEAUZ7dYADgAhTkBQsNct8'
'DkCF3DJ49jjZm9N4EKnKGGXD7XkFE79AFRGPUw4gXpeQCtUXyEugUErqMjqgJjC7ykdg'
'zz7txnzYfRaKHNVs4r4GwNEHRHt7VcTuT3WBcbE4skQgjMnttgP7hts7dMU7PA8kRrfq'
'BKdkPkUwqQ9Xn4zrysY4GvJQHWXxD6Tyqf9PZaz4xbUmsvtuY7NAz27U2aT3EA9XCgfn'
'2cEEWqatZXveHxJr6VmBNWJUyvPrfmVegwtKCGJND8d3Ysruy7GCn6zcrNY7d84aDk3P'
'CEfTJQz342nwRMY4DCuhawz4cnrWwxgsnVPCbeXYH4RcgswVsk9edxKkYMkpTwpcKf6n'
'nRmgJmdqJsRsTkYPpYkKN9rssEDCXr9HFjbenkxXcUe8afrTvKAzwBvbDWcjYBEQKbuY'
'6Ptm9VJrjutUHCPmW2sh66qvq4C9vPhVEey7FpCZDEyYUPrjRfhKjxEFNBKWpcZzvmT2'
'VRE4aqMfuY72xFacxXHjvWagEGQEYtkMtQnsh7XAMGuazT3pkppeUTyDbKTY2Dz7Quc3'
'8UKaw8ece6fTXWpjX2EYrsd4qzvhC6eEPdgnpmzjqmuG8YqEAUZ7dYADgAhTkBQsNct8'
'btQsQDYD4PBjxG2KWAZ9vgTsvBpjjEVcrPfWgwZKJTAZWfWq2u7nT4N2t39EYmQEzbEf'
'DkCF3DJ49jjZm9N4EKnKGGXD7XkFE79AFRGPUw4gXpeQCtUXyEugUErqMjqgJjC7ykdg'
'zz7txnzYfRaKHNVs4r4GwNEHRHt7VcTuT3WBcbE4skQgjMnttgP7hts7dMU7PA8kRrfq'
'BKdkPkUwqQ9Xn4zrysY4GvJQHWXxD6Tyqf9PZaz4xbUmsvtuY7NAz27U2aT3EA9XCgfn'
'2cEEWqatZXveHxJr6VmBNWJUyvPrfmVegwtKCGJND8d3Ysruy7GCn6zcrNY7d84aDk3P'
'CEfTJQz342nwRMY4DCuhawz4cnrWwxgsnVPCbeXYH4RcgswVsk9edxKkYMkpTwpcKf6n'
'E2dhquqdKVTAYf7YKbTfFVsRwqykkPduKXuPwVDjbCqdEJPcmnRJAJkwkQCWgukpvzzm'
'DKFVYxncxmzKgEN27VtgfpsXWBJ2jaxMeQCXb2rbjkVcaypyaETQ3Wkw98EptNAKRcjM'
'E2dhquqdKVTAYf7YKbTfFVsRwqykkPduKXuPwVDjbCqdEJPcmnRJAJkwkQCWgukpvzzm'
'zZJ2xFdfNYh7RZ7EgAAbY8Tqy3j2c9c6HNmXwAVV6dzPTrE4FHcKZGg76anGchczF9ev'
'AG8RHQ7ea2sJhXqBmGsmEj6Q84TN9E7pgmtAtmVAA38AYsQBNZUMYdMcmBdpV9w7G3NZ'
'mEU8R8uWqx6w3NzzqsMg78bnhCR7sdWDkhuEp2M8fYWmqujYFNYvzz6BcHNKQyrWETRD'
'E2dhquqdKVTAYf7YKbTfFVsRwqykkPduKXuPwVDjbCqdEJPcmnRJAJkwkQCWgukpvzzm'
'zaTdNWgM7wsXGkvgYVNdTWnReCPXJUN3yQwrvApZzdaF86QaeYwXW7qqEJrqmwpUUbw2'
'JHkmvJB4AWtVhDc9etzUqfuTaqMyXwxFEWvht3RDTDx8dfQ3Ek8BD4QP4BtUQeQJpfsG'
'FEJeQQYVcBxqVuK26xJrERUDmeNw8KWKBCrYPPy48cjCFdgZHz3cNet6bwJMdsgKMpZT'
'erdYy9nqBw6FRZ37rRMtxmrcB4VsWHbf4HjdPRpu4xyJTqMThnXWa8nPDde3C9wCuKkQ'
'23k2zDYsMeHc6KD93vm7Ky48v3veYEuJvNNxQPyyCZ9XNnpGsWrqsVduCswR4MQpp6yJ'
'RBmwbMYbuEjwJy9UuZxa9bQV4GqYFnVuETC6bXaT9uauWdaa2TrbuuXx3WWdmRGd4Rqh'
'Z3NA9Kqx9pTQHe3KGZ2tFejsJqNvjJvFX94eVeMGDgHjtJzDdxp9NWYtG6v9zABGRzVF'
'MqJX6nhhBPbsvjpswcgJq3ZXxzmWFJmvjECghGrbG6bKawtv4aYhMeaHagfMP8W6KrTy'
'uGxWUhcEhfygjE4truAkjfKCtzzVtTcBArbWMny6HWMp6TAen3f6hEB6kBb7pgvKxkND'
'3JxueYBZvDeq4WWtRzUjcFF2qhEjwrtuCJhy3WMXX3MN6nFDtYRTHZGdPqyatW9Jcc8t'
'7gCMWMVzYyNuXZ2A6rwX6Umv8g3mBuwnrwKXEFTZkPCAZMxk3A6MTmMcJCVy3hw6MmRM'
'eXKyhFxRcKWraysTQG7hd9kP8DeJZNDurYDJwqrh6cwDwaMhBfTgnxTBeyjwpbCJK2FD'
'Jg2vFWPmTJ37gDMdwxWCMRQ9kyqz9PJZ4Xn2MPxMhNqT3Hb39YshryqnbvBagHbqYx9M'
'r4ZKJpKya34JMaPambzg2pKRDd2WdFCZcdHTFyqxxzJbjXM2gjfBZ2strUNqWvQYNTw8'
'QttkuxyeQTgHupKNaZF6y7rDyf7mbNR9DaPXpBQuZ7un6KDj2Dfh7yvfhPk8cHG7n9pb'
'KEKD3sgbbKnQ8d9MsGhUtCQVed7dtjpYKsmGJmbYMvZjpGpqsfsHQfFRdCgJHnW3FdQ6'
'sGhUtCQVed7dtj12',
'MigrationSshKey': {
'private_key': 'private_key',
'public_key': 'public_key'
},
'LibvirtTLSPassword': 'xCdt9yeamKz8Fb6EGba9u82XU',
}
class PlanTest(base.TestCase):
def setUp(self):
@ -465,3 +600,282 @@ class PlanTest(base.TestCase):
max_keys = 3
keys_map = plan_utils.purge_excess_keys(max_keys, keys_map)
self.assertEqual(2, len(keys_map))
@mock.patch('tripleo_common.utils.plan.'
'cache_delete')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
def test_generate_password(self, mock_get_snmpd_readonly_user_password,
mock_cache):
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': {}
}
mock_resource = mock.MagicMock()
mock_resource.attributes = {
'value': 'existing_value'
}
mock_orchestration.resources.get.return_value = mock_resource
mock_workflow = mock.MagicMock()
result = plan_utils.generate_passwords(swift, mock_orchestration,
mock_workflow)
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_cache.assert_called_once_with(
swift,
"overcloud",
"tripleo.parameters.get"
)
@mock.patch('tripleo_common.utils.plan.'
'cache_delete')
@mock.patch('tripleo_common.utils.passwords.'
'create_ssh_keypair')
@mock.patch('tripleo_common.utils.passwords.'
'create_fernet_keys_repo_structure_and_keys')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
def test_run_passwords_exist(self, mock_get_snmpd_readonly_user_password,
mock_fernet_keys_setup,
mock_create_ssh_keypair,
mock_cache):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
'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': {}
}
mock_workflow = mock.MagicMock()
result = plan_utils.generate_passwords(swift, mock_orchestration,
mock_workflow)
# ensure old passwords used and no new generation
self.assertEqual(_EXISTING_PASSWORDS, result)
mock_cache.assert_called_once_with(
swift,
"overcloud",
"tripleo.parameters.get"
)
@mock.patch('tripleo_common.utils.plan.'
'cache_delete')
@mock.patch('tripleo_common.utils.passwords.'
'create_ssh_keypair')
@mock.patch('tripleo_common.utils.passwords.'
'create_fernet_keys_repo_structure_and_keys')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
def test_run_rotate_no_rotate_list(
self, mock_get_snmpd_readonly_user_password,
mock_fernet_keys_setup, mock_create_ssh_keypair,
mock_cache):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
'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': {}
}
mock_resource = mock.MagicMock()
mock_resource.attributes = {
'value': 'existing_value'
}
mock_orchestration.resources.get.return_value = mock_resource
mock_workflow = mock.MagicMock()
result = plan_utils.generate_passwords(swift, mock_orchestration,
mock_workflow,
rotate_passwords=True)
# ensure passwords in the DO_NOT_ROTATE_LIST are not modified
for name in constants.DO_NOT_ROTATE_LIST:
self.assertEqual(_EXISTING_PASSWORDS[name], result[name])
# ensure all passwords are generated
for name in constants.PASSWORD_PARAMETER_NAMES:
self.assertTrue(name in result, "%s is not in %s" % (name, result))
# ensure new passwords have been generated
self.assertNotEqual(_EXISTING_PASSWORDS, result)
mock_cache.assert_called_once_with(
swift,
"overcloud",
"tripleo.parameters.get"
)
@mock.patch('tripleo_common.utils.plan.'
'cache_delete')
@mock.patch('tripleo_common.utils.passwords.'
'create_ssh_keypair')
@mock.patch('tripleo_common.utils.passwords.'
'create_fernet_keys_repo_structure_and_keys')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
def test_run_rotate_with_rotate_list(
self, mock_get_snmpd_readonly_user_password,
mock_fernet_keys_setup, mock_create_ssh_keypair,
mock_cache):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
'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': {}
}
mock_resource = mock.MagicMock()
mock_resource.attributes = {
'value': 'existing_value'
}
mock_orchestration.resources.get.return_value = mock_resource
rotate_list = [
'MistralPassword',
'BarbicanPassword',
'AdminPassword',
'CeilometerMeteringSecret',
'ZaqarPassword',
'NovaPassword',
'MysqlRootPassword'
]
mock_workflow = mock.MagicMock()
result = plan_utils.generate_passwords(swift, mock_orchestration,
mock_workflow,
rotate_passwords=True,
rotate_pw_list=rotate_list)
# ensure only specified passwords are regenerated
for name in constants.PASSWORD_PARAMETER_NAMES:
self.assertTrue(name in result, "%s is not in %s" % (name, result))
if name in rotate_list:
self.assertNotEqual(_EXISTING_PASSWORDS[name], result[name])
else:
self.assertEqual(_EXISTING_PASSWORDS[name], result[name])
mock_cache.assert_called_once_with(
swift,
"overcloud",
"tripleo.parameters.get"
)
@mock.patch('tripleo_common.utils.plan.'
'cache_delete')
@mock.patch('tripleo_common.utils.passwords.'
'create_ssh_keypair')
@mock.patch('tripleo_common.utils.passwords.'
'create_fernet_keys_repo_structure_and_keys')
@mock.patch('tripleo_common.utils.passwords.'
'get_snmpd_readonly_user_password')
def test_passwords_exist_in_heat(
self, mock_get_snmpd_readonly_user_password,
mock_fernet_keys_setup, mock_create_ssh_keypair,
mock_cache):
mock_get_snmpd_readonly_user_password.return_value = "TestPassword"
mock_create_ssh_keypair.return_value = {'public_key': 'Foo',
'private_key': 'Bar'}
mock_fernet_keys_setup.return_value = {'/tmp/foo': {'content': 'Foo'},
'/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)
mock_orchestration = mock.MagicMock()
mock_orchestration.stacks.environment.return_value = {
'parameter_defaults': {
'AdminPassword': 'ExistingPasswordInHeat',
}
}
mock_workflow = mock.MagicMock()
result = plan_utils.generate_passwords(swift, mock_orchestration,
mock_workflow)
existing_passwords["AdminPassword"] = "ExistingPasswordInHeat"
# ensure old passwords used and no new generation
self.assertEqual(existing_passwords, result)
mock_cache.assert_called_once_with(
swift,
"overcloud",
"tripleo.parameters.get"
)

View File

@ -24,6 +24,7 @@ import yaml
import zlib
from heatclient.common import template_utils
from heatclient import exc as heat_exc
import six
from swiftclient import exceptions as swiftexceptions
@ -375,6 +376,93 @@ def update_plan_environment_with_image_parameters(
return env
def generate_passwords(swift, heat, mistral,
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.
If rotate_passwords is set to True, then passwords will be replaced as
follows:
- if password names are specified in the rotate_pw_list, then only those
passwords will be replaced.
- otherwise, all passwords not in the DO_NOT_ROTATE list (as they require
special handling, like KEKs and Fernet keys) will be replaced.
"""
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))
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
passwords = password_utils.generate_passwords(
mistralclient=mistral,
stack_env=stack_env,
rotate_passwords=rotate_passwords
)
# if passwords don't yet exist in plan environment
if 'passwords' not in env:
env['passwords'] = {}
# NOTE(ansmith): if rabbit password previously generated and
# stored, facilitate upgrade and use for oslo messaging in plan env
if 'RabbitPassword' in env['passwords']:
for i in ('RpcPassword', 'NotifyPassword'):
if i not in env['passwords']:
env['passwords'][i] = env['passwords']['RabbitPassword']
# 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)
cache_delete(swift, container, "tripleo.parameters.get")
return env['passwords']
def update_plan_rotate_fernet_keys(swift,
container=constants.DEFAULT_CONTAINER_NAME):
try: