Prepare for real benchmark context support

Move already existing context objects from benchmark.runner to
separated modules in benchmark.context module.

partial bp benchmark-context
Change-Id: Ib00082c521288f358fd8a1cb9fe73c9d74ab145e
This commit is contained in:
Boris Pavlovic 2014-02-28 21:37:32 +04:00
parent 30b8b619b2
commit a3963a0920
10 changed files with 327 additions and 236 deletions

View File

View File

@ -0,0 +1,82 @@
# 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 functools
import itertools
import sys
from rally.benchmark import utils
from rally.openstack.common.gettextutils import _
from rally.openstack.common import log as logging
LOG = logging.getLogger(__name__)
class ResourceCleaner(object):
"""Context class for resource cleanup (both admin and non-admin)."""
def __init__(self, admin=None, users=None):
self.admin = admin
self.users = users
def _cleanup_users_resources(self):
if not self.users:
return
for user in itertools.imap(utils.create_openstack_clients, self.users):
methods = [
functools.partial(utils.delete_nova_resources, user["nova"]),
functools.partial(utils.delete_glance_resources,
user["glance"], user["keystone"]),
functools.partial(utils.delete_cinder_resources,
user["cinder"])
]
for method in methods:
try:
method()
except Exception as e:
LOG.debug(_("Not all resources were cleaned."),
exc_info=sys.exc_info())
LOG.warning(_('Unable to fully cleanup the cloud: \n%s') %
(e.message))
def _cleanup_admin_resources(self):
if not self.admin:
return
try:
admin = utils.create_openstack_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') %
(e.message))
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
self._cleanup_users_resources()
self._cleanup_admin_resources()
if exc_type:
LOG.debug(_("An error occurred while launching "
"the benchmark scenario."),
exc_info=(exc_type, exc_value, exc_traceback))
else:
LOG.debug(_("Completed resources cleanup."))

View File

@ -0,0 +1,88 @@
# 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 uuid
from rally.benchmark import utils
from rally import consts
from rally.objects import endpoint
from rally.openstack.common.gettextutils import _
from rally.openstack.common import log as logging
LOG = logging.getLogger(__name__)
class UserGenerator(object):
"""Context class for generating temporary users/tenants for benchmarks."""
def __init__(self, admin_endpoints):
self.users = []
self.tenants = []
self.keystone_client = \
utils.create_openstack_clients(admin_endpoints)["keystone"]
def _create_user(self, user_id, tenant_id):
pattern = "%(tenant_id)s_user_%(uid)d"
name = pattern % {"tenant_id": tenant_id, "uid": user_id}
email = "%s@email.me" % name
return self.keystone_client.users.create(name, "password",
email, tenant_id)
def _create_tenant(self, run_id, i):
pattern = "temp_%(run_id)s_tenant_%(iter)i"
return self.keystone_client.tenants.create(pattern % {"run_id": run_id,
"iter": i})
def create_users_and_tenants(self, tenants, users_per_tenant):
run_id = str(uuid.uuid4())
auth_url = self.keystone_client.auth_url
self.tenants = [self._create_tenant(run_id, i)
for i in range(tenants)]
self.users = []
endpoints = []
for tenant in self.tenants:
for user_id in range(users_per_tenant):
user = self._create_user(user_id, tenant.id)
self.users.append(user)
endpoints.append(endpoint.Endpoint(
auth_url, user.name, "password", tenant.name,
consts.EndpointPermission.USER))
return endpoints
def _delete_users_and_tenants(self):
for user in self.users:
try:
user.delete()
except Exception:
LOG.info("Failed to delete user: %s" % user.name)
for tenant in self.tenants:
try:
tenant.delete()
except Exception:
LOG.info("Failed to delete tenant: %s" % tenant.name)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
self._delete_users_and_tenants()
if exc_type:
LOG.debug(_("Failed to generate temporary users."),
exc_info=(exc_type, exc_value, exc_traceback))
else:
LOG.debug(_("Completed deleting temporary users and tenants."))

View File

@ -17,6 +17,7 @@ import json
import jsonschema
from rally.benchmark import base
from rally.benchmark.context import users as users_ctx
from rally.benchmark import runner
from rally.benchmark import utils
from rally import consts
@ -155,7 +156,7 @@ class BenchmarkEngine(object):
if self.admin_endpoint:
admin_client = utils.create_openstack_clients(self.admin_endpoint)
validate(admin_validators, admin_client)
with runner.UserGenerator(self.admin_endpoint) as generator:
with users_ctx.UserGenerator(self.admin_endpoint) as generator:
temp_user = generator.create_users_and_tenants(1, 1)[0]
user_client = utils.create_openstack_clients(temp_user)
validate(user_validators, user_client)

View File

@ -14,18 +14,14 @@
# under the License.
import abc
import functools
import itertools
import sys
import uuid
from oslo.config import cfg
from rally.benchmark import base
from rally.benchmark.context import cleaner as cleaner_ctx
from rally.benchmark.context import users as users_ctx
from rally.benchmark import utils
from rally import consts
from rally.objects import endpoint
from rally.openstack.common.gettextutils import _
from rally.openstack.common import log as logging
from rally import utils as rutils
@ -63,126 +59,6 @@ def _run_scenario_once(args):
"atomic_actions_time": scenario.atomic_actions_time()}
class UserGenerator(object):
"""Context class for generating temporary users/tenants for benchmarks."""
def __init__(self, admin_endpoints):
self.users = []
self.tenants = []
self.keystone_client = \
utils.create_openstack_clients(admin_endpoints)["keystone"]
def _create_user(self, user_id, tenant_id):
pattern = "%(tenant_id)s_user_%(uid)d"
name = pattern % {"tenant_id": tenant_id, "uid": user_id}
email = "%s@email.me" % name
return self.keystone_client.users.create(name, "password",
email, tenant_id)
def _create_tenant(self, run_id, i):
pattern = "temp_%(run_id)s_tenant_%(iter)i"
return self.keystone_client.tenants.create(pattern % {"run_id": run_id,
"iter": i})
def create_users_and_tenants(self, tenants, users_per_tenant):
run_id = str(uuid.uuid4())
auth_url = self.keystone_client.auth_url
self.tenants = [self._create_tenant(run_id, i)
for i in range(tenants)]
self.users = []
endpoints = []
for tenant in self.tenants:
for user_id in range(users_per_tenant):
user = self._create_user(user_id, tenant.id)
self.users.append(user)
endpoints.append(endpoint.Endpoint(
auth_url, user.name, "password", tenant.name,
consts.EndpointPermission.USER))
return endpoints
def _delete_users_and_tenants(self):
for user in self.users:
try:
user.delete()
except Exception:
LOG.info("Failed to delete user: %s" % user.name)
for tenant in self.tenants:
try:
tenant.delete()
except Exception:
LOG.info("Failed to delete tenant: %s" % tenant.name)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
self._delete_users_and_tenants()
if exc_type:
LOG.debug(_("Failed to generate temporary users."),
exc_info=(exc_type, exc_value, exc_traceback))
else:
LOG.debug(_("Completed deleting temporary users and tenants."))
class ResourceCleaner(object):
"""Context class for resource cleanup (both admin and non-admin)."""
def __init__(self, admin=None, users=None):
self.admin = admin
self.users = users
def _cleanup_users_resources(self):
if not self.users:
return
for user in itertools.imap(utils.create_openstack_clients, self.users):
methods = [
functools.partial(utils.delete_nova_resources, user["nova"]),
functools.partial(utils.delete_glance_resources,
user["glance"], user["keystone"]),
functools.partial(utils.delete_cinder_resources,
user["cinder"])
]
for method in methods:
try:
method()
except Exception as e:
LOG.debug(_("Not all resources were cleaned."),
exc_info=sys.exc_info())
LOG.warning(_('Unable to fully cleanup the cloud: \n%s') %
(e.message))
def _cleanup_admin_resources(self):
if not self.admin:
return
try:
admin = utils.create_openstack_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') %
(e.message))
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
self._cleanup_users_resources()
self._cleanup_admin_resources()
if exc_type:
LOG.debug(_("An error occurred while launching "
"the benchmark scenario."),
exc_info=(exc_type, exc_value, exc_traceback))
else:
LOG.debug(_("Completed resources cleanup."))
class ScenarioRunner(object):
"""Base class for all scenario runners.
@ -246,19 +122,19 @@ class ScenarioRunner(object):
def _run_as_admin(self, name, kwargs):
config = kwargs.get('config', {})
with UserGenerator(self.admin_user) as generator:
with users_ctx.UserGenerator(self.admin_user) as generator:
tenants = config.get("tenants", 1)
users_per_tenant = config.get("users_per_tenant", 1)
self.users = generator.create_users_and_tenants(tenants,
users_per_tenant)
with ResourceCleaner(admin=self.admin_user,
users=self.users):
with cleaner_ctx.ResourceCleaner(admin=self.admin_user,
users=self.users):
return self._prepare_and_run_scenario(name, kwargs)
def _run_as_non_admin(self, name, kwargs):
# TODO(boris-42): Somehow setup clients from deployment/config
with ResourceCleaner(users=self.users):
with cleaner_ctx.ResourceCleaner(users=self.users):
return self._prepare_and_run_scenario(name, kwargs)
def run(self, name, kwargs):

View File

View File

@ -0,0 +1,94 @@
# 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 import cleaner as cleaner_ctx
from tests import test
BASE = "rally.benchmark.context.cleaner"
class ResourceCleanerTestCase(test.TestCase):
def test_with_statement_no_user_no_admin(self):
resource_cleaner = cleaner_ctx.ResourceCleaner()
with resource_cleaner:
pass
def test_with_statement(self):
res_cleaner = cleaner_ctx.ResourceCleaner()
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.utils.create_openstack_clients" % BASE)
@mock.patch("%s.utils.delete_keystone_resources" % BASE)
def test_cleaner_admin(self, mock_del_keystone, mock_create_os_clients):
admin_endpoint = "admin"
res_cleaner = cleaner_ctx.ResourceCleaner(admin=admin_endpoint)
admin_client = mock.MagicMock()
admin_client.__getitem__ = mock.MagicMock(return_value="keystone_cl")
mock_create_os_clients.return_value = admin_client
with res_cleaner:
pass
mock_create_os_clients.assert_called_once_with(admin_endpoint)
admin_client.__getitem__.assert_called_once_with("keystone")
mock_del_keystone.assert_called_once_with("keystone_cl")
@mock.patch("%s.utils.create_openstack_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)
def test_cleaner_users(self, mock_del_cinder, mock_del_glance,
mock_del_nova, mock_create_os_clients):
users = ["user1", "user2"]
res_cleaner = cleaner_ctx.ResourceCleaner(users=users)
client = mock.MagicMock()
client.__getitem__ = mock.MagicMock(side_effect=lambda cl: cl + "_cl")
mock_create_os_clients.return_value = client
with res_cleaner:
pass
os_clients = ["nova", "glance", "keystone", "cinder"]
expected = [mock.call("user1"), mock.call("user2")]
mock_calls = filter(lambda call: '__getitem__' not in call[0],
mock_create_os_clients.mock_calls)
self.assertEqual(mock_calls, expected)
expected = [mock.call(c) for c in os_clients] * len(users)
self.assertEqual(client.__getitem__.mock_calls, expected)
expected = [mock.call("nova_cl")] * len(users)
self.assertEqual(mock_del_nova.mock_calls, expected)
expected = [mock.call("glance_cl", "keystone_cl")] * len(users)
self.assertEqual(mock_del_glance.mock_calls, expected)
expected = [mock.call("cinder_cl")] * len(users)
self.assertEqual(mock_del_cinder.mock_calls, expected)

View File

@ -0,0 +1,52 @@
# 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 import users
from tests import fakes
from tests import test
class UserGeneratorTestCase(test.TestCase):
@mock.patch("rally.benchmark.context.users.utils.create_openstack_clients")
def test_with_statement(self, mock_create_os_clients):
admin_endpoint = "admin"
with users.UserGenerator(admin_endpoint):
pass
@mock.patch("rally.benchmark.context.users.utils.create_openstack_clients")
def test_create_and_delete_users_and_tenants(self, mock_create_os_clients):
fc = fakes.FakeClients()
# TODO(msdubov): This indicates that osclients.Clients should be
# perhaps refactored to support dictionary-like access.
mock_create_os_clients.return_value = {
"keystone": fc.get_keystone_client()
}
admin_user = {"username": "admin", "password": "pwd",
"tenant_name": "admin", "auth_url": "url"}
created_users = []
created_tenants = []
with users.UserGenerator(admin_user) as generator:
tenants = 10
users_per_tenant = 5
endpoints = generator.create_users_and_tenants(tenants,
users_per_tenant)
self.assertEqual(len(endpoints), tenants * users_per_tenant)
created_users = generator.users
created_tenants = generator.tenants
self.assertTrue(all(u.status == "DELETED" for u in created_users))
self.assertTrue(all(t.status == "DELETED" for t in created_tenants))

View File

@ -115,7 +115,7 @@ class BenchmarkEngineTestCase(test.TestCase):
mock.MagicMock())
@mock.patch("rally.benchmark.base.Scenario.get_by_name")
@mock.patch("rally.benchmark.runner.UserGenerator."
@mock.patch("rally.benchmark.context.users.UserGenerator."
"create_users_and_tenants")
@mock.patch("rally.benchmark.utils.create_openstack_clients")
@mock.patch("rally.benchmark.engine.BenchmarkEngine._validate_config")
@ -148,7 +148,7 @@ class BenchmarkEngineTestCase(test.TestCase):
mock_create_os_clients.assert_has_calls(expected, any_order=True)
@mock.patch("rally.benchmark.base.Scenario.get_by_name")
@mock.patch("rally.benchmark.runner.UserGenerator."
@mock.patch("rally.benchmark.context.users.UserGenerator."
"create_users_and_tenants")
@mock.patch("rally.benchmark.utils.create_openstack_clients")
@mock.patch("rally.benchmark.engine.BenchmarkEngine._validate_config")

View File

@ -198,105 +198,3 @@ class ScenarioRunnerTestCase(test.TestCase):
"tenants": 5, "users_per_tenant": 2})
]
self.assertEqual(srunner._run_scenario.mock_calls, expected)
class UserGeneratorTestCase(test.TestCase):
@mock.patch("rally.benchmark.runner.utils.create_openstack_clients")
def test_with_statement(self, mock_create_os_clients):
admin_endpoint = "admin"
with runner.UserGenerator(admin_endpoint):
pass
@mock.patch("rally.benchmark.runner.utils.create_openstack_clients")
def test_create_and_delete_users_and_tenants(self, mock_create_os_clients):
fc = fakes.FakeClients()
# TODO(msdubov): This indicates that osclients.Clients should be
# perhaps refactored to support dictionary-like access.
mock_create_os_clients.return_value = {
"keystone": fc.get_keystone_client()
}
admin_user = {"username": "admin", "password": "pwd",
"tenant_name": "admin", "auth_url": "url"}
created_users = []
created_tenants = []
with runner.UserGenerator(admin_user) as generator:
tenants = 10
users_per_tenant = 5
endpoints = generator.create_users_and_tenants(tenants,
users_per_tenant)
self.assertEqual(len(endpoints), tenants * users_per_tenant)
created_users = generator.users
created_tenants = generator.tenants
self.assertTrue(all(u.status == "DELETED" for u in created_users))
self.assertTrue(all(t.status == "DELETED" for t in created_tenants))
class ResourceCleanerTestCase(test.TestCase):
def test_with_statement_no_user_no_admin(self):
resource_cleaner = runner.ResourceCleaner()
with resource_cleaner:
pass
def test_with_statement(self):
res_cleaner = runner.ResourceCleaner()
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("rally.benchmark.runner.utils.create_openstack_clients")
@mock.patch("rally.benchmark.runner.utils.delete_keystone_resources")
def test_cleaner_admin(self, mock_del_keystone, mock_create_os_clients):
admin_endpoint = "admin"
res_cleaner = runner.ResourceCleaner(admin=admin_endpoint)
admin_client = mock.MagicMock()
admin_client.__getitem__ = mock.MagicMock(return_value="keystone_cl")
mock_create_os_clients.return_value = admin_client
with res_cleaner:
pass
mock_create_os_clients.assert_called_once_with(admin_endpoint)
admin_client.__getitem__.assert_called_once_with("keystone")
mock_del_keystone.assert_called_once_with("keystone_cl")
@mock.patch("rally.benchmark.runner.utils.create_openstack_clients")
@mock.patch("rally.benchmark.runner.utils.delete_nova_resources")
@mock.patch("rally.benchmark.runner.utils.delete_glance_resources")
@mock.patch("rally.benchmark.runner.utils.delete_cinder_resources")
def test_cleaner_users(self, mock_del_cinder, mock_del_glance,
mock_del_nova, mock_create_os_clients):
users = ["user1", "user2"]
res_cleaner = runner.ResourceCleaner(users=users)
client = mock.MagicMock()
client.__getitem__ = mock.MagicMock(side_effect=lambda cl: cl + "_cl")
mock_create_os_clients.return_value = client
with res_cleaner:
pass
os_clients = ["nova", "glance", "keystone", "cinder"]
expected = [mock.call("user1"), mock.call("user2")]
mock_calls = filter(lambda call: '__getitem__' not in call[0],
mock_create_os_clients.mock_calls)
self.assertEqual(mock_calls, expected)
expected = [mock.call(c) for c in os_clients] * len(users)
self.assertEqual(client.__getitem__.mock_calls, expected)
expected = [mock.call("nova_cl")] * len(users)
self.assertEqual(mock_del_nova.mock_calls, expected)
expected = [mock.call("glance_cl", "keystone_cl")] * len(users)
self.assertEqual(mock_del_glance.mock_calls, expected)
expected = [mock.call("cinder_cl")] * len(users)
self.assertEqual(mock_del_cinder.mock_calls, expected)