From cd87bc60f25e6e3e7f83bde055829885a16346cb Mon Sep 17 00:00:00 2001 From: Ana Krivokapic Date: Fri, 9 Dec 2016 20:52:06 +0100 Subject: [PATCH] Add utility functions for deleting/emptying swift containers Change-Id: I5ae14eba2cc6707fa8e5273bdd93fcc10ac9d15a --- .../notes/5.8.0-d1ca2298ba598431.yaml | 1 + tripleo_common/actions/plan.py | 38 +++------ tripleo_common/tests/utils/test_swift.py | 77 +++++++++++++++++++ tripleo_common/utils/swift.py | 45 +++++++++++ 4 files changed, 134 insertions(+), 27 deletions(-) create mode 100644 tripleo_common/tests/utils/test_swift.py create mode 100644 tripleo_common/utils/swift.py diff --git a/releasenotes/notes/5.8.0-d1ca2298ba598431.yaml b/releasenotes/notes/5.8.0-d1ca2298ba598431.yaml index 65e4d17d2..36359986a 100644 --- a/releasenotes/notes/5.8.0-d1ca2298ba598431.yaml +++ b/releasenotes/notes/5.8.0-d1ca2298ba598431.yaml @@ -10,6 +10,7 @@ features: - CephMdsKey is now a generated Heat parameter. - Add an new Action which generates environment parameters for configuring fencing. + - Add utility functions for deleting/emptying swift containers. fixes: - Fixes `bug 1644756 `__ so that flavour matching works as expected with the object-storage role. diff --git a/tripleo_common/actions/plan.py b/tripleo_common/actions/plan.py index 472697a99..73df400ee 100644 --- a/tripleo_common/actions/plan.py +++ b/tripleo_common/actions/plan.py @@ -19,11 +19,13 @@ import yaml from heatclient import exc as heatexceptions from mistral.workflow import utils as mistral_workflow_utils from mistralclient.api import base as mistralclient_base +import six from swiftclient import exceptions as swiftexceptions from tripleo_common.actions import base from tripleo_common import constants from tripleo_common import exception +from tripleo_common.utils import swift as swiftutils from tripleo_common.utils.validations import pattern_validator @@ -235,38 +237,20 @@ class DeletePlanAction(base.TripleOAction): try: swift = self.get_object_client() - if self.container in [container["name"] for container in - swift.get_account()[1]]: - box = swift.get_container(self.container) - # ensure container is a plan - if box[0].get(constants.TRIPLEO_META_USAGE_KEY) != 'plan': - error_text = ("The {name} container does not contain a " - "TripleO deployment plan and was not " - "deleted.".format(name=self.container)) - else: - # FIXME(rbrady): remove delete_object loop when - # LP#1615830 is fixed. See LP#1615825 for more info. - # delete files from plan - for data in box[1]: - swift.delete_object(self.container, data['name']) - # delete plan container - swift.delete_container(self.container) - # if mistral environment exists, delete it too - mistral = self.get_workflow_client() - if self.container in [env.name for env in - mistral.environments.list()]: - # deletes environment - mistral.environments.delete(self.container) - else: - # container does not exist - error_text = "The {name} container does not exist.".format( - name=self.container) + swiftutils.delete_container(swift, self.container) + + # if mistral environment exists, delete it too + mistral = self.get_workflow_client() + if self.container in [env.name for env in + mistral.environments.list()]: + # deletes environment + mistral.environments.delete(self.container) except swiftexceptions.ClientException as ce: LOG.exception("Swift error deleting plan.") error_text = ce.msg except Exception as err: LOG.exception("Error deleting plan.") - error_text = err + error_text = six.text_type(err) if error_text: return mistral_workflow_utils.Result(error=error_text) diff --git a/tripleo_common/tests/utils/test_swift.py b/tripleo_common/tests/utils/test_swift.py new file mode 100644 index 000000000..f618e0211 --- /dev/null +++ b/tripleo_common/tests/utils/test_swift.py @@ -0,0 +1,77 @@ +# Copyright (c) 2017 Red Hat, Inc. +# +# 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 mock + +from tripleo_common.tests import base +from tripleo_common.utils import swift as swift_utils + + +class SwiftTest(base.TestCase): + def setUp(self): + super(SwiftTest, self).setUp() + self.container_name = 'overcloud' + self.swiftclient = mock.MagicMock() + self.swiftclient.get_account.return_value = ({}, [ + {'name': self.container_name}, + {'name': 'test'}, + ]) + self.swiftclient.get_container.return_value = ( + {'x-container-meta-usage-tripleo': 'plan'}, [ + {'name': 'some-name.yaml'}, + {'name': 'some-other-name.yaml'}, + {'name': 'yet-some-other-name.yaml'}, + {'name': 'finally-another-name.yaml'} + ] + ) + + def test_delete_container_success(self): + swift_utils.empty_container(self.swiftclient, self.container_name) + + mock_calls = [ + mock.call('overcloud', 'some-name.yaml'), + mock.call('overcloud', 'some-other-name.yaml'), + mock.call('overcloud', 'yet-some-other-name.yaml'), + mock.call('overcloud', 'finally-another-name.yaml') + ] + self.swiftclient.delete_object.assert_has_calls( + mock_calls, any_order=True) + + self.swiftclient.get_account.assert_called() + self.swiftclient.get_container.assert_called_with(self.container_name) + + def test_delete_container_not_found(self): + self.assertRaises(ValueError, + swift_utils.empty_container, + self.swiftclient, 'idontexist') + self.swiftclient.get_account.assert_called() + self.swiftclient.get_container.assert_not_called() + self.swiftclient.delete_object.assert_not_called() + + def test_delete_container_not_a_plan(self): + self.swiftclient.get_container.return_value = ( + {'x-container-meta-usage-tripleo': 'not-a-plan'}, [ + {'name': 'some-name.yaml'}, + {'name': 'some-other-name.yaml'}, + {'name': 'yet-some-other-name.yaml'}, + {'name': 'finally-another-name.yaml'} + ] + ) + self.assertRaises(ValueError, + swift_utils.empty_container, + self.swiftclient, self.container_name) + self.swiftclient.get_account.assert_called() + self.swiftclient.get_container.assert_called() + self.swiftclient.delete_object.assert_not_called() diff --git a/tripleo_common/utils/swift.py b/tripleo_common/utils/swift.py new file mode 100644 index 000000000..336595f85 --- /dev/null +++ b/tripleo_common/utils/swift.py @@ -0,0 +1,45 @@ +# Copyright 2016 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. + +from tripleo_common import constants + + +def empty_container(swiftclient, name): + container_names = [container["name"] for container + in swiftclient.get_account()[1]] + + if name in container_names: + headers, objects = swiftclient.get_container(name) + # ensure container is a plan + if headers.get(constants.TRIPLEO_META_USAGE_KEY) != 'plan': + error_text = ("The {name} container does not contain a " + "TripleO deployment plan and was not " + "deleted.".format(name=name)) + raise ValueError(error_text) + else: + # FIXME(rbrady): remove delete_object loop when + # LP#1615830 is fixed. See LP#1615825 for more info. + # delete files from plan + for o in objects: + swiftclient.delete_object(name, o['name']) + else: + error_text = "The {name} container does not exist.".format(name=name) + raise ValueError(error_text) + + +def delete_container(swiftclient, name): + empty_container(swiftclient, name) + swiftclient.delete_container(name)