From e8d8099d4dce62195b1b9c5da5df9cdedbf94a3b Mon Sep 17 00:00:00 2001 From: Endre Karlson Date: Fri, 22 Aug 2014 23:52:45 +0200 Subject: [PATCH] Add designate support with domain scenarios *) Add designate client *) Add designate cleanup *) Add domain benchmarks Change-Id: I4d791411487788ba157f60c3f99aa48bf237b10b --- .../designate/create-and-delete-domain.json | 17 ++++ .../designate/create-and-delete-domain.yaml | 11 +++ .../designate/create-and-list-domain.json | 17 ++++ .../designate/create-and-list-domain.yaml | 11 +++ .../scenarios/designate/list-domains.json | 17 ++++ .../scenarios/designate/list-domains.yaml | 11 +++ .../benchmark/context/cleanup/user_cleanup.py | 6 +- rally/benchmark/context/cleanup/utils.py | 5 ++ .../benchmark/scenarios/designate/__init__.py | 0 rally/benchmark/scenarios/designate/basic.py | 66 +++++++++++++++ rally/benchmark/scenarios/designate/utils.py | 49 +++++++++++ rally/consts.py | 4 + rally/osclients.py | 14 ++++ requirements.txt | 1 + .../benchmark/scenarios/designate/__init__.py | 0 .../scenarios/designate/test_basic.py | 56 +++++++++++++ .../scenarios/designate/test_utils.py | 84 +++++++++++++++++++ 17 files changed, 367 insertions(+), 2 deletions(-) create mode 100644 doc/samples/tasks/scenarios/designate/create-and-delete-domain.json create mode 100644 doc/samples/tasks/scenarios/designate/create-and-delete-domain.yaml create mode 100644 doc/samples/tasks/scenarios/designate/create-and-list-domain.json create mode 100644 doc/samples/tasks/scenarios/designate/create-and-list-domain.yaml create mode 100644 doc/samples/tasks/scenarios/designate/list-domains.json create mode 100644 doc/samples/tasks/scenarios/designate/list-domains.yaml create mode 100644 rally/benchmark/scenarios/designate/__init__.py create mode 100644 rally/benchmark/scenarios/designate/basic.py create mode 100644 rally/benchmark/scenarios/designate/utils.py create mode 100644 tests/benchmark/scenarios/designate/__init__.py create mode 100644 tests/benchmark/scenarios/designate/test_basic.py create mode 100644 tests/benchmark/scenarios/designate/test_utils.py diff --git a/doc/samples/tasks/scenarios/designate/create-and-delete-domain.json b/doc/samples/tasks/scenarios/designate/create-and-delete-domain.json new file mode 100644 index 0000000000..0c3b1fcff1 --- /dev/null +++ b/doc/samples/tasks/scenarios/designate/create-and-delete-domain.json @@ -0,0 +1,17 @@ +{ + "DesignateBasic.create_and_delete_domain": [ + { + "runner": { + "type": "constant", + "times": 3, + "concurrency": 2 + }, + "context": { + "users": { + "tenants": 2, + "users_per_tenant": 2 + } + } + } + ] +} diff --git a/doc/samples/tasks/scenarios/designate/create-and-delete-domain.yaml b/doc/samples/tasks/scenarios/designate/create-and-delete-domain.yaml new file mode 100644 index 0000000000..5132c28c6a --- /dev/null +++ b/doc/samples/tasks/scenarios/designate/create-and-delete-domain.yaml @@ -0,0 +1,11 @@ +--- + DesignateBasic.create_and_delete_domain: + - + runner: + type: "constant" + times: 3 + concurrency: 2 + context: + users: + tenants: 2 + users_per_tenant: 2 diff --git a/doc/samples/tasks/scenarios/designate/create-and-list-domain.json b/doc/samples/tasks/scenarios/designate/create-and-list-domain.json new file mode 100644 index 0000000000..c22b34a862 --- /dev/null +++ b/doc/samples/tasks/scenarios/designate/create-and-list-domain.json @@ -0,0 +1,17 @@ +{ + "DesignateBasic.create_and_list_domains": [ + { + "runner": { + "type": "constant", + "times": 3, + "concurrency": 2 + }, + "context": { + "users": { + "tenants": 2, + "users_per_tenant": 2 + } + } + } + ] +} diff --git a/doc/samples/tasks/scenarios/designate/create-and-list-domain.yaml b/doc/samples/tasks/scenarios/designate/create-and-list-domain.yaml new file mode 100644 index 0000000000..3b113eac25 --- /dev/null +++ b/doc/samples/tasks/scenarios/designate/create-and-list-domain.yaml @@ -0,0 +1,11 @@ +--- + DesignateBasic.create_and_list_domains: + - + runner: + type: "constant" + times: 3 + concurrency: 2 + context: + users: + tenants: 2 + users_per_tenant: 2 diff --git a/doc/samples/tasks/scenarios/designate/list-domains.json b/doc/samples/tasks/scenarios/designate/list-domains.json new file mode 100644 index 0000000000..d440415ea8 --- /dev/null +++ b/doc/samples/tasks/scenarios/designate/list-domains.json @@ -0,0 +1,17 @@ +{ + "DesignateBasic.list_domains": [ + { + "runner": { + "type": "constant", + "times": 3, + "concurrency": 2 + }, + "context": { + "users": { + "tenants": 2, + "users_per_tenant": 2 + } + } + } + ] +} diff --git a/doc/samples/tasks/scenarios/designate/list-domains.yaml b/doc/samples/tasks/scenarios/designate/list-domains.yaml new file mode 100644 index 0000000000..cf2f28244b --- /dev/null +++ b/doc/samples/tasks/scenarios/designate/list-domains.yaml @@ -0,0 +1,11 @@ +--- + DesignateBasic.list_domains: + - + runner: + type: "constant" + times: 3 + concurrency: 2 + context: + users: + tenants: 2 + users_per_tenant: 2 diff --git a/rally/benchmark/context/cleanup/user_cleanup.py b/rally/benchmark/context/cleanup/user_cleanup.py index a2dae57915..8301d41767 100644 --- a/rally/benchmark/context/cleanup/user_cleanup.py +++ b/rally/benchmark/context/cleanup/user_cleanup.py @@ -41,7 +41,7 @@ class UserCleanup(base.Context): "items": { "type": "string", "enum": ["nova", "glance", "cinder", - "neutron", "ceilometer", "heat", "sahara"] + "neutron", "ceilometer", "heat", "sahara", "designate"] }, "uniqueItems": True } @@ -64,7 +64,9 @@ class UserCleanup(base.Context): "ceilometer": (utils.delete_ceilometer_resources, clients.ceilometer, tenant_id), "heat": (utils.delete_heat_resources, clients.heat), - "sahara": (utils.delete_sahara_resources, clients.sahara) + "sahara": (utils.delete_sahara_resources, clients.sahara), + "designate": (utils.delete_designate_resources, + clients.designate), } for service_name in self.config: diff --git a/rally/benchmark/context/cleanup/utils.py b/rally/benchmark/context/cleanup/utils.py index 04a9db7c9a..9b3ad6d22a 100644 --- a/rally/benchmark/context/cleanup/utils.py +++ b/rally/benchmark/context/cleanup/utils.py @@ -154,6 +154,11 @@ def delete_neutron_resources(neutron, project_uuid): neutron.delete_network(network["id"]) +def delete_designate_resources(designate): + for domain in designate.domains.list(): + designate.domains.delete(domain.id) + + def delete_ceilometer_resources(ceilometer, project_uuid): delete_alarms(ceilometer, project_uuid) diff --git a/rally/benchmark/scenarios/designate/__init__.py b/rally/benchmark/scenarios/designate/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rally/benchmark/scenarios/designate/basic.py b/rally/benchmark/scenarios/designate/basic.py new file mode 100644 index 0000000000..ae24d7ebc1 --- /dev/null +++ b/rally/benchmark/scenarios/designate/basic.py @@ -0,0 +1,66 @@ +# Copyright 2014 Hewlett-Packard Development Company, L.P. +# +# Author: Endre Karlson +# +# 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 rally.benchmark.scenarios import base +from rally.benchmark.scenarios.designate import utils +from rally.benchmark import validation +from rally import consts + + +class DesignateBasic(utils.DesignateScenario): + + @base.scenario(context={"cleanup": ["designate"]}) + @validation.required_services(consts.Service.DESIGNATE) + def create_and_list_domains(self): + """Tests creating a domain and listing domains. + + This scenario is a very useful tool to measure + the "designate domain-list" command performance. + + If you have only 1 user in your context, you will + add 1 domain on every iteration. So you will have more + and more domain and will be able to measure the + performance of the "designate domain-list" command depending on + the number of domains owned by users. + """ + self._create_domain() + self._list_domains() + + @base.scenario(context={"cleanup": ["designate"]}) + @validation.required_services(consts.Service.DESIGNATE) + def list_domains(self): + """Test the designate domain-list command. + + This simple scenario tests the designate domain-list command by listing + all the domains. + + Suppose if we have 2 users in context and each has 2 domains + uploaded for them we will be able to test the performance of + designate domain-list command in this case. + """ + + self._list_domains() + + @base.scenario(context={"cleanup": ["designate"]}) + @validation.required_services(consts.Service.DESIGNATE) + def create_and_delete_domain(self): + """Test adds and then deletes domain. + + This is very useful to measure perfromance of creating and deleting + domains with different level of load. + """ + domain = self._create_domain() + self._delete_domain(domain['id']) diff --git a/rally/benchmark/scenarios/designate/utils.py b/rally/benchmark/scenarios/designate/utils.py new file mode 100644 index 0000000000..52f24c5079 --- /dev/null +++ b/rally/benchmark/scenarios/designate/utils.py @@ -0,0 +1,49 @@ +# Copyright 2014 Hewlett-Packard Development Company, L.P. +# +# Author: Endre Karlson +# +# 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 rally.benchmark.scenarios import base + + +class DesignateScenario(base.Scenario): + """This class should contain base operations for benchmarking designate.""" + + RESOURCE_NAME_PREFIX = "rally_" + + @base.atomic_action_timer('designate.create_domain') + def _create_domain(self, domain=None): + """Create domain. + + :param domain: dict, POST /v1/domains request options + :returns: designate domain dict + """ + domain = domain or {} + + domain.setdefault('email', 'root@random.name') + domain.setdefault('name', '%s.name.' % self._generate_random_name()) + return self.clients("designate").domains.create(domain) + + @base.atomic_action_timer('designate.list_domains') + def _list_domains(self): + """Return user domain list.""" + return self.clients("designate").domains.list() + + @base.atomic_action_timer('designate.delete_domain') + def _delete_domain(self, domain_id): + """Delete designate zone. + + :param domain: Domain object + """ + self.clients("designate").domains.delete(domain_id) diff --git a/rally/consts.py b/rally/consts.py index 25c8b340a7..71a0caf43b 100644 --- a/rally/consts.py +++ b/rally/consts.py @@ -28,6 +28,7 @@ TEMPEST_TEST_SETS = ("full", "smoke", "baremetal", "compute", + "dns", "data_processing", "identity", "image", @@ -87,6 +88,7 @@ class _Service(utils.ImmutableMixin, utils.EnumMixin): HEAT = "heat" KEYSTONE = "keystone" NEUTRON = "neutron" + DESIGNATE = "designate" CEILOMETER = "ceilometer" S3 = "s3" TROVE = "trove" @@ -106,6 +108,7 @@ class _ServiceType(utils.ImmutableMixin, utils.EnumMixin): COMPUTE = "compute" COMPUTEV3 = "computev3" NETWORK = "network" + DNS = "dns" METERING = "metering" S3 = "s3" DATABASE = "database" @@ -123,6 +126,7 @@ class _ServiceType(utils.ImmutableMixin, utils.EnumMixin): self.ORCHESTRATION: _Service.HEAT, self.IDENTITY: _Service.KEYSTONE, self.NETWORK: _Service.NEUTRON, + self.DNS: _Service.DESIGNATE, self.METERING: _Service.CEILOMETER, self.S3: _Service.S3, self.DATABASE: _Service.TROVE, diff --git a/rally/osclients.py b/rally/osclients.py index 1615c7e6b9..746d955065 100644 --- a/rally/osclients.py +++ b/rally/osclients.py @@ -17,6 +17,7 @@ import urlparse from ceilometerclient import client as ceilometer from cinderclient import client as cinder +from designateclient import v1 as designate import glanceclient as glance from heatclient import client as heat from ironicclient import client as ironic @@ -250,6 +251,19 @@ class Clients(object): return client + @cached + def designate(self): + """Return designate client.""" + kc = self.keystone() + dns_api_url = kc.service_catalog.url_for( + service_type='dns', endpoint_type='public', + region_name=self.endpoint.region_name) + client = designate.Client( + endpoint=dns_api_url, + token=kc.auth_token, + insecure=CONF.https_insecure) + return client + @cached def services(self): """Return available services names and types. diff --git a/requirements.txt b/requirements.txt index 92cc851581..3649e94583 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,7 @@ pbr>=0.6,!=0.7,<1.0 pecan>=0.5.0 PrettyTable>=0.7,<0.8 PyYAML>=3.1.0 +python-designateclient>=1.0.0 python-glanceclient>=0.13.1 python-keystoneclient>=0.10.0 python-novaclient>=2.17.0 diff --git a/tests/benchmark/scenarios/designate/__init__.py b/tests/benchmark/scenarios/designate/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/benchmark/scenarios/designate/test_basic.py b/tests/benchmark/scenarios/designate/test_basic.py new file mode 100644 index 0000000000..3ab75e36db --- /dev/null +++ b/tests/benchmark/scenarios/designate/test_basic.py @@ -0,0 +1,56 @@ +# Copyright 2014 Hewlett-Packard Development Company, L.P. +# +# Author: Endre Karlson +# +# 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.scenarios.designate import basic +from tests import test + +DESIGNATE_BASIC = "rally.benchmark.scenarios.designate.basic.DesignateBasic" + + +class DesignateBasicTestCase(test.TestCase): + + @mock.patch(DESIGNATE_BASIC + "._list_domains") + @mock.patch(DESIGNATE_BASIC + "._create_domain") + def test_create_and_list_networks(self, mock_create, mock_list): + scenario = basic.DesignateBasic() + + # Default options + scenario.create_and_list_domains() + mock_create.assert_called_once_with() + mock_list.assert_called_once_with() + + @mock.patch(DESIGNATE_BASIC + "._delete_domain") + @mock.patch(DESIGNATE_BASIC + "._create_domain") + def test_create_and_delete_domain(self, mock_create, mock_delete): + scenario = basic.DesignateBasic() + + mock_create.return_value = {"id": "123"} + + # Default options + scenario.create_and_delete_domain() + + mock_create.assert_called_once_with() + mock_delete.assert_called_once_with("123") + + @mock.patch(DESIGNATE_BASIC + "._list_domains") + def test_list_domains(self, mock_list): + scenario = basic.DesignateBasic() + + # Default options + scenario.list_domains() + mock_list.assert_called_once_with() diff --git a/tests/benchmark/scenarios/designate/test_utils.py b/tests/benchmark/scenarios/designate/test_utils.py new file mode 100644 index 0000000000..a322a27a74 --- /dev/null +++ b/tests/benchmark/scenarios/designate/test_utils.py @@ -0,0 +1,84 @@ +# Copyright 2014 Hewlett-Packard Development Company, L.P. +# +# Author: Endre Karlson +# +# 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.scenarios.designate import utils +from tests.benchmark.scenarios import test_base +from tests import test + + +DESIGNATE_UTILS = "rally.benchmark.scenarios.designate.utils." + + +class DesignateScenarioTestCase(test.TestCase): + + def setUp(self): + super(DesignateScenarioTestCase, self).setUp() + self.domain = mock.Mock() + + def _test_atomic_action_timer(self, atomic_actions_time, name): + action_duration = test_base.get_atomic_action_timer_value_by_name( + atomic_actions_time, name) + self.assertIsNotNone(action_duration) + self.assertIsInstance(action_duration, float) + + @mock.patch(DESIGNATE_UTILS + 'DesignateScenario._generate_random_name') + @mock.patch(DESIGNATE_UTILS + 'DesignateScenario.clients') + def test_create_domain(self, mock_clients, mock_random_name): + scenario = utils.DesignateScenario() + + random_name = "foo" + explicit_name = "bar.io." + email = "root@zone.name" + + mock_random_name.return_value = random_name + mock_clients("designate").domains.create.return_value = self.domain + + # Check that the defaults / randoms are used if nothing is specified + domain = scenario._create_domain() + mock_clients("designate").domains.create.assert_called_once_with( + {"email": "root@random.name", "name": '%s.name.' % random_name}) + self.assertEqual(self.domain, domain) + self._test_atomic_action_timer(scenario.atomic_actions(), + 'designate.create_domain') + + mock_clients("designate").domains.create.reset_mock() + + # Check that when specifying zone defaults are not used... + data = {"email": email, "name": explicit_name} + domain = scenario._create_domain(data) + mock_clients("designate").domains.create.assert_called_once_with(data) + self.assertEqual(self.domain, domain) + + @mock.patch(DESIGNATE_UTILS + 'DesignateScenario.clients') + def test_list_domains(self, mock_clients): + scenario = utils.DesignateScenario() + domains_list = [] + mock_clients("designate").domains.list.return_value = domains_list + return_domains_list = scenario._list_domains() + self.assertEqual(domains_list, return_domains_list) + self._test_atomic_action_timer(scenario.atomic_actions(), + 'designate.list_domains') + + @mock.patch(DESIGNATE_UTILS + 'DesignateScenario.clients') + def test_delete_domain(self, mock_clients): + scenario = utils.DesignateScenario() + + domain = scenario._create_domain() + scenario._delete_domain(domain['id']) + self._test_atomic_action_timer(scenario.atomic_actions(), + 'designate.delete_domain')