From 2fb4ba41d5c08c6f81a99e375fc95030a2e10357 Mon Sep 17 00:00:00 2001 From: Aswad Rangnekar Date: Fri, 4 Jul 2014 11:06:01 +0530 Subject: [PATCH] Split user and admin cleanup to 2 separate classes Partially implements: blueprint benchmark-context-cleanup-refactor Change-Id: I3ba0deeb5a2793b8c45f6160fe0eda22250a1ece --- .../context/cleanup/admin_cleanup.py | 78 ++++++++++++ .../cleanup/{cleanup.py => user_cleanup.py} | 56 +++------ rally/benchmark/context/cleanup/utils.py | 5 + rally/benchmark/scenarios/keystone/basic.py | 18 ++- rally/benchmark/scenarios/quotas/quotas.py | 9 +- .../context/cleanup/test_admin_cleanup.py | 59 +++++++++ .../benchmark/context/cleanup/test_cleanup.py | 117 ------------------ .../context/cleanup/test_user_cleanup.py | 92 ++++++++++++++ 8 files changed, 269 insertions(+), 165 deletions(-) create mode 100644 rally/benchmark/context/cleanup/admin_cleanup.py rename rally/benchmark/context/cleanup/{cleanup.py => user_cleanup.py} (58%) create mode 100644 tests/benchmark/context/cleanup/test_admin_cleanup.py delete mode 100644 tests/benchmark/context/cleanup/test_cleanup.py create mode 100644 tests/benchmark/context/cleanup/test_user_cleanup.py diff --git a/rally/benchmark/context/cleanup/admin_cleanup.py b/rally/benchmark/context/cleanup/admin_cleanup.py new file mode 100644 index 0000000000..44d75b854d --- /dev/null +++ b/rally/benchmark/context/cleanup/admin_cleanup.py @@ -0,0 +1,78 @@ +# Copyright 2014: Mirantis 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 sys + +import six + +from rally.benchmark.context import base +from rally.benchmark.context.cleanup import utils +from rally.openstack.common.gettextutils import _ +from rally.openstack.common import log as logging +from rally import osclients +from rally import utils as rutils + + +LOG = logging.getLogger(__name__) + + +class AdminCleanup(base.Context): + """Context class for admin resource cleanup.""" + + __ctx_name__ = "admin_cleanup" + __ctx_order__ = 200 + __ctx_hidden__ = True + + CONFIG_SCHEMA = { + "type": "array", + "$schema": rutils.JSON_SCHEMA, + "items": { + "type": "string", + "enum": ["keystone", "quotas"] + }, + "uniqueItems": True + } + + def __init__(self, context): + super(AdminCleanup, self).__init__(context) + self.endpoint = None + + def _cleanup_resources(self): + client = osclients.Clients(self.endpoint) + + cleanup_methods = { + "keystone": (utils.delete_keystone_resources, client.keystone()), + "quotas": (utils.delete_admin_quotas, client, + self.context.get("tenants", [])), + } + + for service_name in self.config: + cleanup_method = cleanup_methods[service_name] + method, client = cleanup_method[:2] + try: + method(client, *cleanup_method[2:]) + except Exception as e: + LOG.debug("Not all admin resources were cleaned.", + exc_info=sys.exc_info()) + LOG.warning(_('Unable to fully cleanup the cloud: %s') % + (six.text_type(e))) + + @rutils.log_task_wrapper(LOG.info, _("Enter context: `admin cleanup`")) + def setup(self): + self.endpoint = self.context["admin"]["endpoint"] + + @rutils.log_task_wrapper(LOG.info, _("Exit context: `admin cleanup`")) + def cleanup(self): + self._cleanup_resources() diff --git a/rally/benchmark/context/cleanup/cleanup.py b/rally/benchmark/context/cleanup/user_cleanup.py similarity index 58% rename from rally/benchmark/context/cleanup/cleanup.py rename to rally/benchmark/context/cleanup/user_cleanup.py index 06e683ed67..a2dae57915 100644 --- a/rally/benchmark/context/cleanup/cleanup.py +++ b/rally/benchmark/context/cleanup/user_cleanup.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import functools import sys import six @@ -29,11 +28,11 @@ LOG = logging.getLogger(__name__) -class ResourceCleaner(base.Context): - """Context class for resource cleanup (both admin and non-admin).""" +class UserCleanup(base.Context): + """Context class for user resource cleanup.""" __ctx_name__ = "cleanup" - __ctx_order__ = 200 + __ctx_order__ = 201 __ctx_hidden__ = True CONFIG_SCHEMA = { @@ -42,29 +41,24 @@ class ResourceCleaner(base.Context): "items": { "type": "string", "enum": ["nova", "glance", "cinder", - "quotas", "neutron", "ceilometer", "heat", "sahara"] + "neutron", "ceilometer", "heat", "sahara"] }, "uniqueItems": True } def __init__(self, context): - super(ResourceCleaner, self).__init__(context) - self.admin = [] - self.users = [] + super(UserCleanup, self).__init__(context) + self.users_endpoints = [] - @rutils.log_task_wrapper(LOG.info, _("Cleanup users resources.")) - def _cleanup_users_resources(self): - for user in self.users: + def _cleanup_resources(self): + for user in self.users_endpoints: clients = osclients.Clients(user) - admin_clients = functools.partial(osclients.Clients, self.admin) tenant_id = clients.keystone().tenant_id cleanup_methods = { "nova": (utils.delete_nova_resources, clients.nova), "glance": (utils.delete_glance_resources, clients.glance, tenant_id), "cinder": (utils.delete_cinder_resources, clients.cinder), - "quotas": (utils.delete_quotas, admin_clients, - tenant_id), "neutron": (utils.delete_neutron_resources, clients.neutron, tenant_id), "ceilometer": (utils.delete_ceilometer_resources, @@ -74,39 +68,23 @@ def _cleanup_users_resources(self): } for service_name in self.config: + cleanup_method = cleanup_methods[service_name] + method = cleanup_method[0] + client = cleanup_method[1]() try: - service = cleanup_methods[service_name] - method = service[0] - client = service[1]() - args = service[2:] - method(client, *args) + method(client, *cleanup_method[2:]) except Exception as e: - LOG.debug("Not all resources were cleaned.", + LOG.debug("Not all user resources were cleaned.", exc_info=sys.exc_info()) LOG.warning(_('Unable to fully cleanup the cloud: %s') % (six.text_type(e))) - @rutils.log_task_wrapper(LOG.info, _("Cleanup admin resources.")) - def _cleanup_admin_resources(self): - try: - admin = osclients.Clients(self.admin) - utils.delete_keystone_resources(admin.keystone()) - except Exception as e: - LOG.debug("Not all resources were cleaned.", - exc_info=sys.exc_info()) - LOG.warning(_('Unable to fully cleanup keystone service: %s') % - (six.text_type(e))) - @rutils.log_task_wrapper(LOG.info, _("Enter context: `cleanup`")) def setup(self): - if "admin" in self.context and self.context["admin"]: - self.admin = self.context["admin"]["endpoint"] - if "users" in self.context and self.context["users"]: - self.users = [u["endpoint"] for u in self.context["users"]] + self.users_endpoints = [u["endpoint"] + for u in self.context.get("users", [])] @rutils.log_task_wrapper(LOG.info, _("Exit context: `cleanup`")) def cleanup(self): - if self.users and self.config: - self._cleanup_users_resources() - if self.admin: - self._cleanup_admin_resources() + if self.users_endpoints and self.config: + self._cleanup_resources() diff --git a/rally/benchmark/context/cleanup/utils.py b/rally/benchmark/context/cleanup/utils.py index 9ebe39145c..04a9db7c9a 100644 --- a/rally/benchmark/context/cleanup/utils.py +++ b/rally/benchmark/context/cleanup/utils.py @@ -39,6 +39,11 @@ def delete_heat_resources(heat): delete_stacks(heat) +def delete_admin_quotas(client, tenants): + for tenant in tenants: + delete_quotas(client, tenant["id"]) + + def delete_keystone_resources(keystone): keystone = keystone_wrapper.wrap(keystone) for resource in ["user", "project", "service", "role"]: diff --git a/rally/benchmark/scenarios/keystone/basic.py b/rally/benchmark/scenarios/keystone/basic.py index 62c7fff580..9ac0ef9b52 100644 --- a/rally/benchmark/scenarios/keystone/basic.py +++ b/rally/benchmark/scenarios/keystone/basic.py @@ -20,20 +20,24 @@ class KeystoneBasic(kutils.KeystoneScenario): - @scenario_base.scenario(admin_only=True, context={"cleanup": []}) + @scenario_base.scenario(admin_only=True, + context={"admin_cleanup": ["keystone"]}) def create_user(self, name_length=10, **kwargs): self._user_create(name_length=name_length, **kwargs) - @scenario_base.scenario(admin_only=True, context={"cleanup": []}) + @scenario_base.scenario(admin_only=True, + context={"admin_cleanup": ["keystone"]}) def create_delete_user(self, name_length=10, **kwargs): user = self._user_create(name_length=name_length, **kwargs) self._resource_delete(user) - @scenario_base.scenario(admin_only=True, context={"cleanup": []}) + @scenario_base.scenario(admin_only=True, + context={"admin_cleanup": ["keystone"]}) def create_tenant(self, name_length=10, **kwargs): self._tenant_create(name_length=name_length, **kwargs) - @scenario_base.scenario(admin_only=True, context={"cleanup": []}) + @scenario_base.scenario(admin_only=True, + context={"admin_cleanup": ["keystone"]}) @validation.add(validation.required_parameters(['users_per_tenant'])) def create_tenant_with_users(self, users_per_tenant, name_length=10, **kwargs): @@ -41,12 +45,14 @@ def create_tenant_with_users(self, users_per_tenant, name_length=10, self._users_create(tenant, users_per_tenant=users_per_tenant, name_length=name_length) - @scenario_base.scenario(admin_only=True, context={"cleanup": []}) + @scenario_base.scenario(admin_only=True, + context={"admin_cleanup": ["keystone"]}) def create_and_list_users(self, name_length=10, **kwargs): self._user_create(name_length=name_length, **kwargs) self._list_users() - @scenario_base.scenario(admin_only=True, context={"cleanup": []}) + @scenario_base.scenario(admin_only=True, + context={"admin_cleanup": ["keystone"]}) def create_and_list_tenants(self, name_length=10, **kwargs): self._tenant_create(name_length=name_length, **kwargs) self._list_tenants() diff --git a/rally/benchmark/scenarios/quotas/quotas.py b/rally/benchmark/scenarios/quotas/quotas.py index 7e8095d260..b57cb0ec07 100644 --- a/rally/benchmark/scenarios/quotas/quotas.py +++ b/rally/benchmark/scenarios/quotas/quotas.py @@ -19,7 +19,8 @@ class Quotas(utils.QuotasScenario): - @scenario_base.scenario(admin_only=True, context={"cleanup": ["quotas"]}) + @scenario_base.scenario(admin_only=True, + context={"admin_cleanup": ["quotas"]}) def nova_update(self, max_quota=1024): """Tests updating quotas for nova. @@ -28,7 +29,8 @@ def nova_update(self, max_quota=1024): tenant_id = self.context()["user"]["tenant_id"] self._update_quotas('nova', tenant_id, max_quota) - @scenario_base.scenario(admin_only=True, context={"cleanup": ["quotas"]}) + @scenario_base.scenario(admin_only=True, + context={"admin_cleanup": ["quotas"]}) def nova_update_and_delete(self, max_quota=1024): """Tests updating and deleting quotas for nova. @@ -39,7 +41,8 @@ def nova_update_and_delete(self, max_quota=1024): self._update_quotas('nova', tenant_id, max_quota) self._delete_quotas('nova', tenant_id) - @scenario_base.scenario(admin_only=True, context={"cleanup": ["quotas"]}) + @scenario_base.scenario(admin_only=True, + context={"admin_cleanup": ["quotas"]}) def cinder_update(self, max_quota=1024): """Tests updating quotas for cinder. diff --git a/tests/benchmark/context/cleanup/test_admin_cleanup.py b/tests/benchmark/context/cleanup/test_admin_cleanup.py new file mode 100644 index 0000000000..2df45ab98c --- /dev/null +++ b/tests/benchmark/context/cleanup/test_admin_cleanup.py @@ -0,0 +1,59 @@ +# Copyright 2014: Mirantis 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 mock + +from rally.benchmark.context.cleanup import admin_cleanup +from tests import fakes +from tests import test + + +BASE = "rally.benchmark.context.cleanup.admin_cleanup" + + +class AdminCleanupTestCase(test.TestCase): + + def test_with_statement(self): + fake_admin_ctx = fakes.FakeUserContext({}).context + fake_admin_ctx["config"] = {"admin_cleanup": ["keystone"]} + admin_cleaner = admin_cleanup.AdminCleanup(fake_admin_ctx) + admin_cleaner.setup() + + admin_cleaner._cleanup_resources = mock.MagicMock() + + with admin_cleaner as cleaner: + self.assertEqual(admin_cleaner, cleaner) + + admin_cleaner._cleanup_resources.assert_called_once_with() + + @mock.patch("%s.osclients.Clients" % BASE) + @mock.patch("%s.utils.delete_keystone_resources" % BASE) + def test_cleaner_admin(self, mock_del_keystone, mock_clients): + context = { + "task": mock.MagicMock(), + "config": {"admin_cleanup": ["keystone"]}, + "admin": {"endpoint": mock.MagicMock()}, + } + res_cleaner = admin_cleanup.AdminCleanup(context) + + fake_keystone = mock.MagicMock() + mock_clients.return_value.keystone.return_value = fake_keystone + + with res_cleaner: + res_cleaner.setup() + + mock_clients.assert_called_once_with(context["admin"]["endpoint"]) + mock_clients.return_value.keystone.assert_called_with() + mock_del_keystone.assert_called_once_with(fake_keystone) diff --git a/tests/benchmark/context/cleanup/test_cleanup.py b/tests/benchmark/context/cleanup/test_cleanup.py deleted file mode 100644 index 4085656b6c..0000000000 --- a/tests/benchmark/context/cleanup/test_cleanup.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright 2014: Mirantis 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 mock - -from rally.benchmark.context.cleanup import cleanup as cleanup_ctx -from tests import fakes -from tests import test - - -BASE = "rally.benchmark.context.cleanup.cleanup" - - -class ResourceCleanerTestCase(test.TestCase): - - def test_with_statement_no_user_no_admin(self): - context = { - "task": mock.MagicMock(), - "admin": None, - "users": [], - "tenants": [], - } - resource_cleaner = cleanup_ctx.ResourceCleaner(context) - with resource_cleaner: - resource_cleaner.setup() - - def test_with_statement(self): - fake_user_ctx = fakes.FakeUserContext({}).context - fake_user_ctx["config"] = {"cleanup": ["nova"]} - res_cleaner = cleanup_ctx.ResourceCleaner(fake_user_ctx) - res_cleaner.setup() - - res_cleaner._cleanup_users_resources = mock.MagicMock() - res_cleaner._cleanup_admin_resources = mock.MagicMock() - - with res_cleaner as cleaner: - self.assertEqual(res_cleaner, cleaner) - - res_cleaner._cleanup_users_resources.assert_called_once_with() - res_cleaner._cleanup_admin_resources.assert_called_once_with() - - @mock.patch("%s.osclients.Clients" % BASE) - @mock.patch("%s.utils.delete_keystone_resources" % BASE) - def test_cleaner_admin(self, mock_del_keystone, mock_clients): - context = { - "task": mock.MagicMock(), - "config": {"cleanup": ["cinder", "nova"]}, - "admin": {"endpoint": mock.MagicMock()}, - } - res_cleaner = cleanup_ctx.ResourceCleaner(context) - - mock_clients.return_value.keystone.return_value = 'keystone' - - with res_cleaner: - res_cleaner.setup() - - mock_clients.assert_called_once_with(context["admin"]["endpoint"]) - mock_clients.return_value.keystone.assert_called_once_with() - mock_del_keystone.assert_called_once_with('keystone') - - @mock.patch("%s.osclients.Clients" % BASE) - @mock.patch("%s.utils.delete_nova_resources" % BASE) - @mock.patch("%s.utils.delete_glance_resources" % BASE) - @mock.patch("%s.utils.delete_cinder_resources" % BASE) - @mock.patch("%s.utils.delete_neutron_resources" % BASE) - def test_cleaner_users_resources(self, - mock_del_neutron, - mock_del_cinder, - mock_del_glance, - mock_del_nova, - mock_clients): - context = { - "task": mock.MagicMock(), - "users": [{"endpoint": mock.MagicMock()}, - {"endpoint": mock.MagicMock()}], - "config": {"cleanup": ["cinder", "nova", "glance", "neutron"]}, - "tenants": [mock.MagicMock()] - } - res_cleaner = cleanup_ctx.ResourceCleaner(context) - - with res_cleaner: - res_cleaner.setup() - - expected = [mock.call(context["users"][0]["endpoint"]), - mock.call(context["users"][1]["endpoint"])] - mock_clients.assert_has_calls(expected, any_order=True) - - self.assertEqual(mock_del_nova.call_count, 2) - self.assertEqual(mock_del_glance.call_count, 2) - self.assertEqual(mock_del_cinder.call_count, 2) - self.assertEqual(mock_del_neutron.call_count, 2) - - @mock.patch("%s.ResourceCleaner._cleanup_users_resources" % BASE) - def test_cleaner_users_default_behavior(self, mock_cleanup): - context = { - "task": mock.MagicMock(), - "users": [{"endpoint": mock.MagicMock()}, - {"endpoint": mock.MagicMock()}], - } - res_cleaner = cleanup_ctx.ResourceCleaner(context) - - with res_cleaner: - res_cleaner.setup() - - self.assertEqual(mock_cleanup.call_count, 0) diff --git a/tests/benchmark/context/cleanup/test_user_cleanup.py b/tests/benchmark/context/cleanup/test_user_cleanup.py new file mode 100644 index 0000000000..81440ced21 --- /dev/null +++ b/tests/benchmark/context/cleanup/test_user_cleanup.py @@ -0,0 +1,92 @@ +# Copyright 2014: Mirantis 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 mock + +from rally.benchmark.context.cleanup import user_cleanup +from tests import fakes +from tests import test + + +BASE = "rally.benchmark.context.cleanup.user_cleanup" + + +class UserCleanupTestCase(test.TestCase): + + def test_with_statement_no_user(self): + context = { + "task": mock.MagicMock(), + "admin": mock.MagicMock(), + "users": [], + "tenants": [], + } + user_cleaner = user_cleanup.UserCleanup(context) + with user_cleaner: + user_cleaner.setup() + + def test_with_statement(self): + fake_user_ctx = fakes.FakeUserContext({}).context + fake_user_ctx["config"] = {"cleanup": ["nova"]} + user_cleaner = user_cleanup.UserCleanup(fake_user_ctx) + user_cleaner.setup() + + user_cleaner._cleanup_resources = mock.MagicMock() + + with user_cleaner as cleaner: + self.assertEqual(user_cleaner, cleaner) + + user_cleaner._cleanup_resources.assert_called_once_with() + + @mock.patch("%s.osclients.Clients" % BASE) + @mock.patch("%s.utils.delete_nova_resources" % BASE) + @mock.patch("%s.utils.delete_glance_resources" % BASE) + @mock.patch("%s.utils.delete_cinder_resources" % BASE) + @mock.patch("%s.utils.delete_neutron_resources" % BASE) + def test_cleaner_resources(self, mock_del_neutron, mock_del_cinder, + mock_del_glance, mock_del_nova, mock_clients): + context = { + "task": mock.MagicMock(), + "users": [{"endpoint": mock.MagicMock()}, + {"endpoint": mock.MagicMock()}], + "config": {"cleanup": ["cinder", "nova", "glance", "neutron"]}, + "tenants": [mock.MagicMock()] + } + user_cleaner = user_cleanup.UserCleanup(context) + + with user_cleaner: + user_cleaner.setup() + + expected = [mock.call(context["users"][0]["endpoint"]), + mock.call(context["users"][1]["endpoint"])] + mock_clients.assert_has_calls(expected, any_order=True) + + self.assertEqual(mock_del_nova.call_count, 2) + self.assertEqual(mock_del_glance.call_count, 2) + self.assertEqual(mock_del_cinder.call_count, 2) + self.assertEqual(mock_del_neutron.call_count, 2) + + @mock.patch("%s.UserCleanup._cleanup_resources" % BASE) + def test_cleaner_default_behavior(self, mock_cleanup): + context = { + "task": mock.MagicMock(), + "users": [{"endpoint": mock.MagicMock()}, + {"endpoint": mock.MagicMock()}], + } + user_cleaner = user_cleanup.UserCleanup(context) + + with user_cleaner: + user_cleaner.setup() + + self.assertEqual(mock_cleanup.call_count, 0)