Move DeployStackAction functionality to utils

Change-Id: I5b1fa484010f88212ddb312f963a8f530e5a78d9
This commit is contained in:
Rabi Mishra 2020-03-17 16:45:17 +05:30
parent 08fe86531b
commit f6d55d57eb
4 changed files with 407 additions and 427 deletions

View File

@ -21,15 +21,15 @@ import yaml
from heatclient.common import deployment_utils
from heatclient import exc as heat_exc
from mistral_lib import actions
import six
from swiftclient import exceptions as swiftexceptions
from tripleo_common.actions import base
from tripleo_common import constants
from tripleo_common import update
from tripleo_common.utils import overcloudrc
from tripleo_common.utils import plan as plan_utils
from tripleo_common.utils import stack as stack_utils
from tripleo_common.utils import swift as swiftutils
from tripleo_common.utils import template as template_utils
LOG = logging.getLogger(__name__)
@ -143,123 +143,15 @@ class DeployStackAction(base.TripleOAction):
def run(self, context):
# check to see if the stack exists
heat = self.get_orchestration_client(context)
try:
stack = heat.stacks.get(self.container, resolve_outputs=False)
except heat_exc.HTTPNotFound:
stack = None
stack_is_new = stack is None
# update StackAction, DeployIdentifier and UpdateIdentifier
swift = self.get_object_client(context)
parameters = dict()
if not self.skip_deploy_identifier:
parameters['DeployIdentifier'] = int(time.time())
else:
parameters['DeployIdentifier'] = ''
parameters['UpdateIdentifier'] = ''
parameters['StackAction'] = 'CREATE' if stack_is_new else 'UPDATE'
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)
self.set_tls_parameters(parameters, env)
try:
plan_utils.update_in_env(swift, env, 'parameter_defaults',
parameters)
except swiftexceptions.ClientException as err:
err_msg = ("Error updating environment for plan %s: %s" % (
self.container, err))
LOG.exception(err_msg)
return actions.Result(error=err_msg)
if not stack_is_new:
try:
LOG.debug('Checking for compatible neutron mechanism drivers')
msg = update.check_neutron_mechanism_drivers(env, stack,
swift,
self.container)
if msg:
return actions.Result(error=msg)
except swiftexceptions.ClientException as err:
err_msg = ("Error getting template %s: %s" % (
self.container, err))
LOG.exception(err_msg)
return actions.Result(error=err_msg)
# process all plan files and create or update a stack
processed_data = template_utils.process_templates(
swift, heat, container=self.container,
prune_services=True
)
stack_args = processed_data.copy()
stack_args['timeout_mins'] = self.timeout_mins
if stack_is_new:
try:
swift.copy_object(
"%s-swift-rings" % self.container, "swift-rings.tar.gz",
"%s-swift-rings/%s-%d" % (
self.container, "swift-rings.tar.gz", time.time()))
swift.delete_object(
"%s-swift-rings" % self.container, "swift-rings.tar.gz")
except swiftexceptions.ClientException:
pass
LOG.info("Perfoming Heat stack create")
try:
return heat.stacks.create(**stack_args)
except heat_exc.HTTPException as err:
err_msg = "Error during stack creation: %s" % (err,)
LOG.exception(err_msg)
return actions.Result(error=err_msg)
LOG.info("Performing Heat stack update")
stack_args['existing'] = 'true'
try:
return heat.stacks.update(stack.id, **stack_args)
except heat_exc.HTTPException as err:
err_msg = "Error during stack update: %s" % (err,)
LOG.exception(err_msg)
return actions.Result(error=err_msg)
def set_tls_parameters(self, parameters, env,
local_ca_path=constants.LOCAL_CACERT_PATH):
cacert_string = self._get_local_cacert(local_ca_path)
if cacert_string:
parameters['CAMap'] = self._get_updated_camap_entry(
'undercloud-ca', cacert_string, self._get_camap(env))
def _get_local_cacert(self, local_ca_path):
# Since the undercloud has TLS by default, we'll add the undercloud's
# CA to be trusted by the overcloud.
try:
with open(local_ca_path, 'rb') as ca_file:
return ca_file.read().decode('utf-8')
except IOError:
# If the file wasn't found it means that the undercloud's TLS
# was explicitly disabled or another CA is being used. So we'll
# let the user handle this.
return None
except Exception:
raise
def _get_camap(self, env):
return env['parameter_defaults'].get('CAMap', {})
def _get_updated_camap_entry(self, entry_name, cacert, orig_camap):
ca_map_entry = {
entry_name: {
'content': cacert
}
}
orig_camap.update(ca_map_entry)
return orig_camap
stack_utils.deploy_stack(swift, heat, self.container,
self.skip_deploy_identifier,
self.timeout_mins)
except Exception as err:
LOG.exception(six.text_type(err))
return actions.Result(six.text_type(err))
class OvercloudRcAction(base.TripleOAction):

View File

@ -14,15 +14,12 @@
# under the License.
import json
import mock
import tempfile
import yaml
from heatclient import exc as heat_exc
from mistral_lib import actions
from swiftclient import exceptions as swiftexceptions
from tripleo_common.actions import deployment
from tripleo_common import constants
from tripleo_common.tests import base
@ -204,314 +201,6 @@ class OrchestrationDeployActionTest(base.TestCase):
get_obj_client_mock.delete_container.called_once_with('container')
class DeployStackActionTest(base.TestCase):
@mock.patch('tripleo_common.actions.deployment.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch(
'tripleo_common.actions.base.TripleOAction.get_orchestration_client')
def test_run(self, get_orchestration_client_mock,
mock_get_object_client, mock_get_template_contents,
mock_process_multiple_environments_and_files,
mock_time):
mock_ctx = mock.MagicMock()
# setup swift
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
mock_get_object_client.return_value = swift
heat = mock.MagicMock()
heat.stacks.get.return_value = None
get_orchestration_client_mock.return_value = heat
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
action = deployment.DeployStackAction(1, 'overcloud')
action.run(mock_ctx)
# verify parameters are as expected
expected_defaults = {'DeployIdentifier': 1473366264,
'StackAction': 'CREATE',
'UpdateIdentifier': '',
'random_existing_data': 'a_value'}
mock_env_updated = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'parameter_defaults': expected_defaults,
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}]
}, default_flow_style=False)
swift.put_object.assert_called_once_with(
'overcloud',
constants.PLAN_ENVIRONMENT,
mock_env_updated
)
heat.stacks.create.assert_called_once_with(
environment={},
files={},
stack_name='overcloud',
template={'heat_template_version': '2016-04-30'},
timeout_mins=1,
)
swift.delete_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz")
swift.copy_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz",
"overcloud-swift-rings/swift-rings.tar.gz-%d" % 1473366264)
@mock.patch('tripleo_common.actions.deployment.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch(
'tripleo_common.actions.base.TripleOAction.get_orchestration_client')
def test_run_skip_deploy_identifier(
self, get_orchestration_client_mock,
mock_get_object_client, mock_get_template_contents,
mock_process_multiple_environments_and_files,
mock_time):
mock_ctx = mock.MagicMock()
# setup swift
swift = mock.MagicMock(url="http://test.com")
mock_get_object_client.return_value = swift
heat = mock.MagicMock()
heat.stacks.get.return_value = None
get_orchestration_client_mock.return_value = heat
mock_env = yaml.safe_dump({
'name': constants.DEFAULT_CONTAINER_NAME,
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
action = deployment.DeployStackAction(1, 'overcloud',
skip_deploy_identifier=True)
action.run(mock_ctx)
# verify parameters are as expected
mock_env_updated = yaml.safe_dump({
'name': constants.DEFAULT_CONTAINER_NAME,
'temp_environment': 'temp_environment',
'parameter_defaults': {'StackAction': 'CREATE',
'DeployIdentifier': '',
'UpdateIdentifier': '',
'random_existing_data': 'a_value'},
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}]
}, default_flow_style=False)
swift.put_object.assert_called_once_with(
constants.DEFAULT_CONTAINER_NAME,
constants.PLAN_ENVIRONMENT,
mock_env_updated
)
heat.stacks.create.assert_called_once_with(
environment={},
files={},
stack_name='overcloud',
template={'heat_template_version': '2016-04-30'},
timeout_mins=1,
)
swift.delete_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz")
swift.copy_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz",
"overcloud-swift-rings/swift-rings.tar.gz-%d" % 1473366264)
@mock.patch('tripleo_common.actions.deployment.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch(
'tripleo_common.actions.base.TripleOAction.get_orchestration_client')
def test_run_create_failed(
self, get_orchestration_client_mock, mock_get_object_client,
mock_get_template_contents,
mock_process_multiple_environments_and_files, mock_time):
mock_ctx = mock.MagicMock()
# setup swift
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
mock_get_object_client.return_value = swift
heat = mock.MagicMock()
heat.stacks.get.return_value = None
heat.stacks.create.side_effect = heat_exc.HTTPException("Oops")
get_orchestration_client_mock.return_value = heat
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
action = deployment.DeployStackAction(1, 'overcloud')
expected = actions.Result(
error="Error during stack creation: ERROR: Oops\n")
self.assertEqual(expected, action.run(mock_ctx))
@mock.patch('tripleo_common.update.check_neutron_mechanism_drivers')
@mock.patch('tripleo_common.actions.deployment.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
@mock.patch('tripleo_common.actions.base.TripleOAction.get_object_client')
@mock.patch(
'tripleo_common.actions.base.TripleOAction.get_orchestration_client')
def test_run_update_failed(
self, get_orchestration_client_mock, mock_get_object_client,
mock_get_template_contents,
mock_process_multiple_environments_and_files, mock_time,
mock_check_neutron_drivers):
mock_ctx = mock.MagicMock()
# setup swift
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
mock_get_object_client.return_value = swift
heat = mock.MagicMock()
heat.stacks.get.return_value = mock.Mock()
heat.stacks.update.side_effect = heat_exc.HTTPException("Oops")
get_orchestration_client_mock.return_value = heat
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
mock_check_neutron_drivers.return_value = None
action = deployment.DeployStackAction(1, 'overcloud')
expected = actions.Result(
error="Error during stack update: ERROR: Oops\n")
self.assertEqual(expected, action.run(mock_ctx))
def test_set_tls_parameters_no_ca_found(self):
action = deployment.DeployStackAction(1, 'overcloud',
skip_deploy_identifier=True)
my_params = {}
my_env = {'parameter_defaults': {}}
action.set_tls_parameters(parameters=my_params, env=my_env,
local_ca_path='/tmp/my-unexistent-file.txt')
self.assertEqual(my_params, {})
def test_set_tls_parameters_ca_found_no_camap_provided(self):
action = deployment.DeployStackAction(1, 'overcloud',
skip_deploy_identifier=True)
my_params = {}
my_env = {'parameter_defaults': {}}
with tempfile.NamedTemporaryFile() as ca_file:
# Write test data
ca_file.write(b'FAKE CA CERT')
ca_file.flush()
# Test
action.set_tls_parameters(parameters=my_params, env=my_env,
local_ca_path=ca_file.name)
self.assertIn('CAMap', my_params)
self.assertIn('undercloud-ca', my_params['CAMap'])
self.assertIn('content', my_params['CAMap']['undercloud-ca'])
self.assertEqual('FAKE CA CERT',
my_params['CAMap']['undercloud-ca']['content'])
def test_set_tls_parameters_ca_found_camap_provided(self):
action = deployment.DeployStackAction(1, 'overcloud',
skip_deploy_identifier=True)
my_params = {}
my_env = {
'parameter_defaults': {
'CAMap': {'overcloud-ca': {'content': 'ANOTER FAKE CERT'}}}}
with tempfile.NamedTemporaryFile() as ca_file:
# Write test data
ca_file.write(b'FAKE CA CERT')
ca_file.flush()
# Test
action.set_tls_parameters(parameters=my_params, env=my_env,
local_ca_path=ca_file.name)
self.assertIn('CAMap', my_params)
self.assertIn('undercloud-ca', my_params['CAMap'])
self.assertIn('content', my_params['CAMap']['undercloud-ca'])
self.assertEqual('FAKE CA CERT',
my_params['CAMap']['undercloud-ca']['content'])
self.assertIn('overcloud-ca', my_params['CAMap'])
self.assertIn('content', my_params['CAMap']['overcloud-ca'])
self.assertEqual('ANOTER FAKE CERT',
my_params['CAMap']['overcloud-ca']['content'])
class OvercloudRcActionTestCase(base.TestCase):
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_object_client')

View File

@ -12,9 +12,14 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import tempfile
import yaml
from heatclient import exc as heat_exc
import mock
from swiftclient import exceptions as swiftexceptions
from tripleo_common import constants
from tripleo_common.tests import base
from tripleo_common.utils import stack
@ -104,3 +109,274 @@ class UpdateStackTest(base.TestCase):
})
heat.stacks.update.assert_called_once_with('stack_id')
class DeployStackTest(base.TestCase):
@mock.patch('tripleo_common.utils.stack.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
def test_stack_deploy(
self, mock_get_template_contents,
mock_process_multiple_environments_and_files,
mock_time):
# setup swift
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
heat = mock.MagicMock()
heat.stacks.get.return_value = None
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
stack.deploy_stack(swift, heat, 'overcloud')
# verify parameters are as expected
expected_defaults = {'DeployIdentifier': 1473366264,
'StackAction': 'CREATE',
'random_existing_data': 'a_value'}
mock_env_updated = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'parameter_defaults': expected_defaults,
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}]
}, default_flow_style=False)
swift.put_object.assert_called_once_with(
'overcloud',
constants.PLAN_ENVIRONMENT,
mock_env_updated
)
heat.stacks.create.assert_called_once_with(
environment={},
files={},
stack_name='overcloud',
template={'heat_template_version': '2016-04-30'},
timeout_mins=240,
)
swift.delete_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz")
swift.copy_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz",
"overcloud-swift-rings/swift-rings.tar.gz-%d" % 1473366264)
@mock.patch('tripleo_common.utils.stack.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
def test_run_skip_deploy_identifier(
self, mock_get_template_contents,
mock_process_multiple_environments_and_files,
mock_time):
# setup swift
swift = mock.MagicMock(url="http://test.com")
heat = mock.MagicMock()
heat.stacks.get.return_value = None
mock_env = yaml.safe_dump({
'name': constants.DEFAULT_CONTAINER_NAME,
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
stack.deploy_stack(swift, heat,
'overcloud', skip_deploy_identifier=True)
# verify parameters are as expected
mock_env_updated = yaml.safe_dump({
'name': constants.DEFAULT_CONTAINER_NAME,
'temp_environment': 'temp_environment',
'parameter_defaults': {'StackAction': 'CREATE',
'DeployIdentifier': '',
'random_existing_data': 'a_value'},
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}]
}, default_flow_style=False)
swift.put_object.assert_called_once_with(
constants.DEFAULT_CONTAINER_NAME,
constants.PLAN_ENVIRONMENT,
mock_env_updated
)
heat.stacks.create.assert_called_once_with(
environment={},
files={},
stack_name='overcloud',
template={'heat_template_version': '2016-04-30'},
timeout_mins=240,
)
swift.delete_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz")
swift.copy_object.assert_called_once_with(
"overcloud-swift-rings", "swift-rings.tar.gz",
"overcloud-swift-rings/swift-rings.tar.gz-%d" % 1473366264)
@mock.patch('tripleo_common.utils.stack.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
def test_run_create_failed(
self, mock_get_template_contents,
mock_process_multiple_environments_and_files,
mock_time):
# setup swift
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
heat = mock.MagicMock()
heat.stacks.get.return_value = None
heat.stacks.create.side_effect = heat_exc.HTTPException("Oops")
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
self.assertRaises(RuntimeError, stack.deploy_stack,
swift, heat, 'overcloud')
@mock.patch('tripleo_common.update.check_neutron_mechanism_drivers')
@mock.patch('tripleo_common.utils.stack.time')
@mock.patch('heatclient.common.template_utils.'
'process_multiple_environments_and_files')
@mock.patch('heatclient.common.template_utils.get_template_contents')
def test_run_update_failed(
self, mock_get_template_contents,
mock_process_multiple_environments_and_files, mock_time,
mock_check_neutron_drivers):
# setup swift
swift = mock.MagicMock(url="http://test.com")
mock_env = yaml.safe_dump({
'name': 'overcloud',
'temp_environment': 'temp_environment',
'template': 'template',
'environments': [{u'path': u'environments/test.yaml'}],
'parameter_defaults': {'random_existing_data': 'a_value'},
}, default_flow_style=False)
swift.get_object.side_effect = (
({}, mock_env),
({}, mock_env),
swiftexceptions.ClientException('atest2')
)
heat = mock.MagicMock()
heat.stacks.get.return_value = mock.Mock()
heat.stacks.update.side_effect = heat_exc.HTTPException("Oops")
mock_get_template_contents.return_value = ({}, {
'heat_template_version': '2016-04-30'
})
mock_process_multiple_environments_and_files.return_value = ({}, {})
# freeze time at datetime.datetime(2016, 9, 8, 16, 24, 24)
mock_time.time.return_value = 1473366264
mock_check_neutron_drivers.return_value = None
self.assertRaises(RuntimeError, stack.deploy_stack,
swift, heat, 'overcloud')
def test_set_tls_parameters_no_ca_found(self):
my_params = {}
my_env = {'parameter_defaults': {}}
stack.set_tls_parameters(
parameters=my_params, env=my_env,
local_ca_path='/tmp/my-unexistent-file.txt')
self.assertEqual(my_params, {})
def test_set_tls_parameters_ca_found_no_camap_provided(self):
my_params = {}
my_env = {'parameter_defaults': {}}
with tempfile.NamedTemporaryFile() as ca_file:
# Write test data
ca_file.write(b'FAKE CA CERT')
ca_file.flush()
# Test
stack.set_tls_parameters(
parameters=my_params, env=my_env,
local_ca_path=ca_file.name)
self.assertIn('CAMap', my_params)
self.assertIn('undercloud-ca', my_params['CAMap'])
self.assertIn('content', my_params['CAMap']['undercloud-ca'])
self.assertEqual(
'FAKE CA CERT',
my_params['CAMap']['undercloud-ca']['content'])
def test_set_tls_parameters_ca_found_camap_provided(self):
my_params = {}
my_env = {
'parameter_defaults': {
'CAMap': {'overcloud-ca': {'content': 'ANOTER FAKE CERT'}}}}
with tempfile.NamedTemporaryFile() as ca_file:
# Write test data
ca_file.write(b'FAKE CA CERT')
ca_file.flush()
# Test
stack.set_tls_parameters(
parameters=my_params, env=my_env,
local_ca_path=ca_file.name)
self.assertIn('CAMap', my_params)
self.assertIn('undercloud-ca', my_params['CAMap'])
self.assertIn('content', my_params['CAMap']['undercloud-ca'])
self.assertEqual('FAKE CA CERT',
my_params['CAMap']['undercloud-ca']['content'])
self.assertIn('overcloud-ca', my_params['CAMap'])
self.assertIn('content', my_params['CAMap']['overcloud-ca'])
self.assertEqual('ANOTER FAKE CERT',
my_params['CAMap']['overcloud-ca']['content'])

View File

@ -23,6 +23,7 @@ from heatclient import exc as heat_exc
from swiftclient import exceptions as swiftexceptions
from tripleo_common import constants
from tripleo_common import update
from tripleo_common.utils import plan as plan_utils
from tripleo_common.utils import template as templates
@ -154,3 +155,125 @@ def validate_stack_and_flatten_parameters(heat, processed_data, env):
processed_data['heat_resource_tree'])
processed_data['heat_resource_tree'] = flattened
return processed_data
def deploy_stack(swift, heat, container, skip_deploy_identifier=False,
timeout_mins=240):
try:
stack = heat.stacks.get(container, resolve_outputs=False)
except heat_exc.HTTPNotFound:
stack = None
stack_is_new = stack is None
# update StackAction, DeployIdentifier and UpdateIdentifier
parameters = dict()
if not skip_deploy_identifier:
parameters['DeployIdentifier'] = int(time.time())
else:
parameters['DeployIdentifier'] = ''
parameters['StackAction'] = 'CREATE' if stack_is_new else 'UPDATE'
try:
env = plan_utils.get_env(swift, container)
except swiftexceptions.ClientException as err:
err_msg = ("Error retrieving environment for plan %s: %s" % (
container, err))
LOG.exception(err_msg)
raise RuntimeError(err_msg)
set_tls_parameters(parameters, env)
try:
plan_utils.update_in_env(swift, env, 'parameter_defaults',
parameters)
except swiftexceptions.ClientException as err:
err_msg = ("Error updating environment for plan %s: %s" % (
container, err))
LOG.exception(err_msg)
raise RuntimeError(err_msg)
if not stack_is_new:
try:
LOG.debug('Checking for compatible neutron mechanism drivers')
msg = update.check_neutron_mechanism_drivers(env, stack,
swift,
container)
if msg:
raise RuntimeError(msg)
except swiftexceptions.ClientException as err:
err_msg = ("Error getting template %s: %s" % (
container, err))
LOG.exception(err_msg)
raise RuntimeError(err_msg)
# process all plan files and create or update a stack
processed_data = templates.process_templates(
swift, heat, container=container,
prune_services=True
)
stack_args = processed_data.copy()
stack_args['timeout_mins'] = timeout_mins
if stack_is_new:
try:
swift.copy_object(
"%s-swift-rings" % container, "swift-rings.tar.gz",
"%s-swift-rings/%s-%d" % (
container, "swift-rings.tar.gz", time.time()))
swift.delete_object(
"%s-swift-rings" % container, "swift-rings.tar.gz")
except swiftexceptions.ClientException:
pass
LOG.info("Perfoming Heat stack create")
try:
return heat.stacks.create(**stack_args)
except heat_exc.HTTPException as err:
err_msg = "Error during stack creation: %s" % (err,)
LOG.exception(err_msg)
raise RuntimeError(err_msg)
LOG.info("Performing Heat stack update")
stack_args['existing'] = 'true'
try:
return heat.stacks.update(stack.id, **stack_args)
except heat_exc.HTTPException as err:
err_msg = "Error during stack update: %s" % (err,)
LOG.exception(err_msg)
raise RuntimeError(err_msg)
def set_tls_parameters(parameters, env,
local_ca_path=constants.LOCAL_CACERT_PATH):
def get_camap():
return env['parameter_defaults'].get('CAMap', {})
def get_updated_camap_entry(entry_name, cacert, orig_camap):
ca_map_entry = {
entry_name: {
'content': cacert
}
}
orig_camap.update(ca_map_entry)
return orig_camap
cacert_string = get_local_cacert(local_ca_path)
if cacert_string:
parameters['CAMap'] = get_updated_camap_entry(
'undercloud-ca', cacert_string, get_camap())
def get_local_cacert(local_ca_path):
# Since the undercloud has TLS by default, we'll add the undercloud's
# CA to be trusted by the overcloud.
try:
with open(local_ca_path, 'rb') as ca_file:
return ca_file.read().decode('utf-8')
except IOError:
# If the file wasn't found it means that the undercloud's TLS
# was explicitly disabled or another CA is being used. So we'll
# let the user handle this.
return None
except Exception:
raise