From b7ddde426aa3594fad106b8d8a6910965bfa1c8d Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Tue, 12 Sep 2017 21:50:55 +0000 Subject: [PATCH] Action to always populate container image parameters Currently the UI cannot deploy with containers because the mandatory container parameters are not set. Also a CLI will not deploy unless passed an environment generated by the "images prepare" call. This change adds an action in the create_deployment_plan and update_deployment_plan workflows which does a container prepare with the default options, so that the mandatory image parameters are always populated with values which will work. Change-Id: Ibce7658468c1b3689a7481deb94dd43e1f3ead52 Closes-Bug: #1716778 --- setup.cfg | 1 + tripleo_common/actions/container_images.py | 83 ++++++++++++ tripleo_common/constants.py | 4 + .../tests/actions/test_container_images.py | 118 ++++++++++++++++++ workbooks/plan_management.yaml | 34 ++++- 5 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 tripleo_common/actions/container_images.py create mode 100644 tripleo_common/tests/actions/test_container_images.py diff --git a/setup.cfg b/setup.cfg index 07a876d25..4b3ed4ddd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -77,6 +77,7 @@ mistral.actions = tripleo.baremetal.probe_node = tripleo_common.actions.baremetal:ProbeNode tripleo.config.download_config = tripleo_common.actions.config:DownloadConfigAction tripleo.config.get_overcloud_config = tripleo_common.actions.config:GetOvercloudConfig + tripleo.container_images.prepare = tripleo_common.actions.container_images:PrepareContainerImageEnv tripleo.deployment.config = tripleo_common.actions.deployment:OrchestrationDeployAction tripleo.deployment.deploy = tripleo_common.actions.deployment:DeployStackAction tripleo.deployment.overcloudrc = tripleo_common.actions.deployment:OvercloudRcAction diff --git a/tripleo_common/actions/container_images.py b/tripleo_common/actions/container_images.py new file mode 100644 index 000000000..f53e15d3e --- /dev/null +++ b/tripleo_common/actions/container_images.py @@ -0,0 +1,83 @@ +# Copyright 2017 Red Hat, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 logging +import os +import sys + +from mistral_lib import actions +from swiftclient import exceptions as swiftexceptions +import yaml + +from tripleo_common.actions import base +from tripleo_common.actions import heat_capabilities +from tripleo_common import constants +from tripleo_common.image import kolla_builder + + +LOG = logging.getLogger(__name__) + + +class PrepareContainerImageEnv(base.TripleOAction): + """Populates env parameters with results from container image prepare + + :param container: Name of the Swift container / plan name + """ + + def __init__(self, container): + super(PrepareContainerImageEnv, self).__init__() + self.container = container + + def run(self, context): + + def ffunc(entry): + return entry + + template_file = os.path.join(sys.prefix, 'share', 'tripleo-common', + 'container-images', + 'overcloud_containers.yaml.j2') + + builder = kolla_builder.KollaImageBuilder([template_file]) + result = builder.container_images_from_template(filter=ffunc) + + params = {} + for entry in result: + imagename = entry.get('imagename', '') + if 'params' in entry: + for p in entry.pop('params'): + params[p] = imagename + swift = self.get_object_client(context) + try: + swift.put_object( + self.container, + constants.CONTAINER_DEFAULTS_ENVIRONMENT, + yaml.safe_dump( + {'parameter_defaults': params}, + default_flow_style=False + ) + ) + except swiftexceptions.ClientException as err: + err_msg = ("Error updating %s for plan %s: %s" % ( + constants.CONTAINER_DEFAULTS_ENVIRONMENT, + self.container, err)) + LOG.exception(err_msg) + return actions.Result(error=err_msg) + + environments = {constants.CONTAINER_DEFAULTS_ENVIRONMENT: True} + + update_action = heat_capabilities.UpdateCapabilitiesAction( + environments, container=self.container) + return update_action.run(context) diff --git a/tripleo_common/constants.py b/tripleo_common/constants.py index f45440529..9c8fa87a7 100644 --- a/tripleo_common/constants.py +++ b/tripleo_common/constants.py @@ -127,6 +127,10 @@ DEFAULT_VOLUME_API_VERSION = '3' # import/export PLAN_ENVIRONMENT = 'plan-environment.yaml' +# The name of the file which holds container image default parameters +CONTAINER_DEFAULTS_ENVIRONMENT = ('environments/' + 'containers-default-parameters.yaml') + DEFAULT_DEPLOY_KERNEL_NAME = 'bm-deploy-kernel' DEFAULT_DEPLOY_RAMDISK_NAME = 'bm-deploy-ramdisk' diff --git a/tripleo_common/tests/actions/test_container_images.py b/tripleo_common/tests/actions/test_container_images.py new file mode 100644 index 000000000..b9a6cddaa --- /dev/null +++ b/tripleo_common/tests/actions/test_container_images.py @@ -0,0 +1,118 @@ +# Copyright 2017 Red Hat, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# 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 os +import sys + +import mock +from swiftclient import exceptions as swiftexceptions +import yaml + +from mistral_lib import actions +from tripleo_common.actions import container_images +from tripleo_common.tests import base + + +image_entries = [{ + 'imagename': 't/cb-nova-compute:liberty', + 'params': ['DockerNovaComputeImage', 'DockerNovaLibvirtConfigImage'] +}, { + 'imagename': 't/cb-nova-libvirt:liberty', + 'params': ['DockerNovaLibvirtImage'] +}] + + +class PrepareContainerImageEnvTest(base.TestCase): + + def setUp(self): + super(PrepareContainerImageEnvTest, self).setUp() + self.ctx = mock.MagicMock() + + @mock.patch("tripleo_common.actions.container_images." + "PrepareContainerImageEnv.get_object_client") + @mock.patch("tripleo_common.actions.heat_capabilities." + "UpdateCapabilitiesAction") + @mock.patch("tripleo_common.image.kolla_builder.KollaImageBuilder") + def test_run(self, kib, update_action, goc): + swift = goc.return_value + builder = kib.return_value + builder.container_images_from_template.return_value = image_entries + final_env = {'environments': [ + {'path': 'overcloud-resource-registry-puppet.yaml'}, + {'path': 'environments/containers-default-parameters.yaml'}, + {'path': 'user-environment.yaml'} + ]} + update_action.return_value.run.return_value = final_env + + action = container_images.PrepareContainerImageEnv( + container='overcloud') + self.assertEqual(final_env, action.run(self.ctx)) + + kib.assert_called_once_with( + [os.path.join(sys.prefix, 'share', 'tripleo-common', + 'container-images', 'overcloud_containers.yaml.j2')] + ) + params = { + 'DockerNovaComputeImage': 't/cb-nova-compute:liberty', + 'DockerNovaLibvirtConfigImage': 't/cb-nova-compute:liberty', + 'DockerNovaLibvirtImage': 't/cb-nova-libvirt:liberty', + } + expected_env = yaml.safe_dump( + {'parameter_defaults': params}, + default_flow_style=False + ) + swift.put_object.assert_called_once_with( + 'overcloud', + 'environments/containers-default-parameters.yaml', + expected_env + ) + update_action.assert_called_once_with( + {'environments/containers-default-parameters.yaml': True}, + container='overcloud' + ) + + @mock.patch("tripleo_common.actions.container_images." + "PrepareContainerImageEnv.get_object_client") + @mock.patch("tripleo_common.actions.heat_capabilities." + "UpdateCapabilitiesAction") + @mock.patch("tripleo_common.image.kolla_builder.KollaImageBuilder") + def test_run_failed(self, kib, update_action, goc): + swift = goc.return_value + builder = kib.return_value + builder.container_images_from_template.return_value = image_entries + final_env = {'environments': [ + {'path': 'overcloud-resource-registry-puppet.yaml'}, + {'path': 'environments/containers-default-parameters.yaml'}, + {'path': 'user-environment.yaml'} + ]} + update_action.return_value.run.return_value = final_env + + action = container_images.PrepareContainerImageEnv( + container='overcloud') + self.assertEqual(final_env, action.run(self.ctx)) + + update_action.return_value.run.return_value = actions.Result( + error='Error updating environment for plan overcloud: ouch') + self.assertEqual( + 'Error updating environment for plan overcloud: ouch', + action.run(self.ctx).error + ) + + swift.put_object.side_effect = swiftexceptions.ClientException('nope') + self.assertEqual( + 'Error updating environments/containers-default-parameters.yaml ' + 'for plan overcloud: nope', + action.run(self.ctx).error + ) diff --git a/workbooks/plan_management.yaml b/workbooks/plan_management.yaml index 9d98ba3dd..61a91372d 100644 --- a/workbooks/plan_management.yaml +++ b/workbooks/plan_management.yaml @@ -123,14 +123,21 @@ workflows: action: tripleo.plan.migrate plan=<% $.container %> on-success: - ensure_passwords_exist: <% $.generate_passwords = true %> - - process_templates: <% $.generate_passwords != true %> + - container_images_prepare: <% $.generate_passwords != true %> on-error: migrate_plan_set_status_failed ensure_passwords_exist: action: tripleo.parameters.generate_passwords container=<% $.container %> - on-success: process_templates + on-success: container_images_prepare on-error: ensure_passwords_exist_set_status_failed + container_images_prepare: + description: > + Populate all container image parameters with default values. + action: tripleo.container_images.prepare container=<% $.container %> + on-success: process_templates + on-error: container_images_prepare_set_status_failed + process_templates: action: tripleo.templates.process container=<% $.container %> on-success: set_status_success @@ -184,6 +191,12 @@ workflows: status: FAILED message: <% task(create_plan).result %> + container_images_prepare_set_status_failed: + on-success: notify_zaqar + publish: + status: FAILED + message: <% task(create_plan).result %> + notify_zaqar: action: zaqar.queue_post retry: count=5 delay=1 @@ -244,14 +257,21 @@ workflows: action: tripleo.plan.migrate plan=<% $.container %> on-success: - ensure_passwords_exist: <% $.generate_passwords = true %> - - process_templates: <% $.generate_passwords != true %> + - container_images_prepare: <% $.generate_passwords != true %> on-error: migrate_plan_set_status_failed ensure_passwords_exist: action: tripleo.parameters.generate_passwords container=<% $.container %> - on-success: process_templates + on-success: container_images_prepare on-error: ensure_passwords_exist_set_status_failed + container_images_prepare: + description: > + Populate all container image parameters with default values. + action: tripleo.container_images.prepare container=<% $.container %> + on-success: process_templates + on-error: container_images_prepare_set_status_failed + process_templates: action: tripleo.templates.process container=<% $.container %> on-success: set_status_success @@ -299,6 +319,12 @@ workflows: status: FAILED message: <% task(update_plan).result %> + container_images_prepare_set_status_failed: + on-success: notify_zaqar + publish: + status: FAILED + message: <% task(create_plan).result %> + notify_zaqar: action: zaqar.queue_post retry: count=5 delay=1