[Core] Create new plugin based class for credentials

* Moved credential class from rally.objects to rally.deployment.
* Added OpenStackCredential plugin.
* Simplified getting of osclients.
* Enabled client caching on OpenStackCredential plugin.

spec: deployment_type.rst

Change-Id: I9ac8c4989c681885534683bd4ee4bc34fcfabaac
This commit is contained in:
Anton Studenov 2017-03-21 12:49:11 +03:00
parent 7cb9b6e5ce
commit f89c32d698
19 changed files with 479 additions and 220 deletions

View File

@ -50,12 +50,16 @@ then
cat >$1 <<EOF
{
"type": "ExistingCloud",
"auth_url": "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/v$IDENTITY_API_VERSION",
"region_name": "$REGION_NAME",
"admin": {
"username": "admin",
"password": "$ADMIN_PASSWORD",
"tenant_name": "admin",
"creds": {
"openstack": {
"auth_url": "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/v$IDENTITY_API_VERSION",
"region_name": "$REGION_NAME",
"admin": {
"username": "admin",
"password": "$ADMIN_PASSWORD",
"tenant_name": "admin",
}
}
}
}
EOF
@ -65,14 +69,18 @@ then
cat >$1 <<EOF
{
"type": "ExistingCloud",
"auth_url": "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/v$IDENTITY_API_VERSION",
"region_name": "$REGION_NAME",
"admin": {
"username": "admin",
"password": "$ADMIN_PASSWORD",
"project_name": "admin",
"user_domain_name": "Default",
"project_domain_name": "Default"
"creds": {
"openstack": {
"auth_url": "$KEYSTONE_SERVICE_PROTOCOL://$KEYSTONE_SERVICE_HOST:$KEYSTONE_SERVICE_PORT/v$IDENTITY_API_VERSION",
"region_name": "$REGION_NAME",
"admin": {
"username": "admin",
"password": "$ADMIN_PASSWORD",
"project_name": "admin",
"user_domain_name": "Default",
"project_domain_name": "Default"
}
}
}
}
EOF

View File

@ -21,7 +21,6 @@ from rally.common import logging
from rally.common.plugin import discover
from rally.common.plugin import plugin
from rally.common import utils as rutils
from rally import osclients
from rally.plugins.openstack.cleanup import base
@ -29,7 +28,6 @@ LOG = logging.getLogger(__name__)
class SeekAndDestroy(object):
cache = {}
def __init__(self, manager_cls, admin, users, api_versions=None,
resource_classes=None, task_id=None):
@ -58,15 +56,8 @@ class SeekAndDestroy(object):
"""Simplifies initialization and caching OpenStack clients."""
if not user:
return None
if self.api_versions:
key = str((user["credential"], sorted(self.api_versions.items())))
else:
key = user["credential"]
if key not in self.cache:
self.cache[key] = osclients.Clients(
user["credential"], api_info=self.api_versions)
return self.cache[key]
# NOTE(astudenov): Credential now supports caching by default
return user["credential"].clients(api_info=self.api_versions)
def _delete_single_resource(self, resource):
"""Safe resource deletion with retries and timeouts.
@ -170,7 +161,6 @@ class SeekAndDestroy(object):
admin=admin_client,
user=self._get_cached_client(user),
tenant_uuid=user["tenant_id"])
_publish(self.admin, user, manager)
def _consumer(self, cache, args):
@ -268,14 +258,14 @@ def cleanup(names=None, admin_required=None, admin=None, users=None,
:param admin_required: If None -> return all plugins
If True -> return only admin plugins
If False -> return only non admin plugins
:param admin: rally.common.objects.Credential that corresponds to OpenStack
admin.
:param admin: rally.deployment.credential.Credential that corresponds to
OpenStack admin.
:param users: List of OpenStack users that was used during benchmarking.
Every user has next structure:
{
"id": <uuid1>,
"tenant_id": <uuid2>,
"credential": <rally.common.objects.Credential>
"credential": <rally.deployment.credential.Credential>
}
:param superclass: The plugin superclass to perform cleanup
for. E.g., this could be

View File

@ -21,11 +21,11 @@ from oslo_config import cfg
from rally.common import broker
from rally.common.i18n import _
from rally.common import logging
from rally.common import objects
from rally.common import utils as rutils
from rally import consts
from rally import exceptions
from rally import osclients
from rally.plugins.openstack import credential
from rally.plugins.openstack.services.identity import identity
from rally.plugins.openstack.wrappers import network
from rally.task import context
@ -207,10 +207,12 @@ class UserGenerator(context.Context):
project_id=tenant_id,
domain_name=user_dom,
default_role=default_role)
user_credential = objects.Credential(
self.credential.auth_url, user.name, password,
self.context["tenants"][tenant_id]["name"],
consts.EndpointPermission.USER,
user_credential = credential.OpenStackCredential(
auth_url=self.credential.auth_url,
username=user.name,
password=password,
tenant_name=self.context["tenants"][tenant_id]["name"],
permission=consts.EndpointPermission.USER,
project_domain_name=project_dom,
user_domain_name=user_dom,
endpoint_type=self.credential.endpoint_type,

View File

@ -0,0 +1,173 @@
# Copyright 2017: 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.
from rally import consts
from rally.deployment import credential
from rally import osclients
@credential.configure("openstack")
class OpenStackCredential(credential.Credential):
"""Credential for OpenStack."""
def __init__(self, auth_url, username, password, tenant_name=None,
project_name=None,
permission=consts.EndpointPermission.USER,
region_name=None, endpoint_type=None,
domain_name=None, endpoint=None, user_domain_name=None,
project_domain_name=None,
https_insecure=False, https_cacert=None):
self.auth_url = auth_url
self.username = username
self.password = password
self.tenant_name = tenant_name or project_name
self.permission = permission
self.region_name = region_name
self.endpoint_type = endpoint_type
self.domain_name = domain_name
self.user_domain_name = user_domain_name
self.project_domain_name = project_domain_name
self.endpoint = endpoint
self.https_insecure = https_insecure
self.https_cacert = https_cacert
self._clients_cache = {}
# backward compatibility
@property
def insecure(self):
return self.https_insecure
# backward compatibility
@property
def cacert(self):
return self.https_cacert
def to_dict(self):
return {"auth_url": self.auth_url,
"username": self.username,
"password": self.password,
"tenant_name": self.tenant_name,
"region_name": self.region_name,
"endpoint_type": self.endpoint_type,
"domain_name": self.domain_name,
"endpoint": self.endpoint,
"https_insecure": self.https_insecure,
"https_cacert": self.https_cacert,
"user_domain_name": self.user_domain_name,
"project_domain_name": self.project_domain_name,
"permission": self.permission}
def verify_connection(self):
if self.permission == consts.EndpointPermission.ADMIN:
self.clients().verified_keystone()
else:
self.clients().keystone()
def list_services(self):
return self.clients().services()
def clients(self, api_info=None):
return osclients.Clients(self, api_info=api_info,
cache=self._clients_cache)
@credential.configure_builder("openstack")
class OpenStackCredentialBuilder(credential.CredentialBuilder):
"""Builds credentials provided by ExistingCloud config."""
USER_SCHEMA = {
"type": "object",
"oneOf": [
{
"description": "Keystone V2.0",
"properties": {
"username": {"type": "string"},
"password": {"type": "string"},
"tenant_name": {"type": "string"},
},
"required": ["username", "password", "tenant_name"],
"additionalProperties": False
},
{
"description": "Keystone V3.0",
"properties": {
"username": {"type": "string"},
"password": {"type": "string"},
"domain_name": {"type": "string"},
"user_domain_name": {"type": "string"},
"project_name": {"type": "string"},
"project_domain_name": {"type": "string"},
},
"required": ["username", "password", "project_name"],
"additionalProperties": False
}
],
}
CONFIG_SCHEMA = {
"type": "object",
"properties": {
"admin": USER_SCHEMA,
"users": {"type": "array", "items": USER_SCHEMA},
"auth_url": {"type": "string"},
"region_name": {"type": "string"},
# NOTE(andreykurilin): it looks like we do not use endpoint
# var at all
"endpoint": {"type": ["string", "null"]},
"endpoint_type": {
"enum": [consts.EndpointType.ADMIN,
consts.EndpointType.INTERNAL,
consts.EndpointType.PUBLIC,
None]},
"https_insecure": {"type": "boolean"},
"https_cacert": {"type": "string"},
},
"required": ["auth_url", "admin"],
"additionalProperties": False
}
def _create_credential(self, common, user, permission):
cred = OpenStackCredential(
auth_url=common["auth_url"],
username=user["username"],
password=user["password"],
tenant_name=user.get("project_name", user.get("tenant_name")),
permission=permission,
region_name=common.get("region_name"),
endpoint_type=common.get("endpoint_type"),
endpoint=common.get("endpoint"),
domain_name=user.get("domain_name"),
user_domain_name=user.get("user_domain_name", None),
project_domain_name=user.get("project_domain_name", None),
https_insecure=common.get("https_insecure", False),
https_cacert=common.get("https_cacert"))
return cred.to_dict()
def build_credentials(self):
permissions = consts.EndpointPermission
users = [self._create_credential(self.config, user, permissions.USER)
for user in self.config.get("users", [])]
admin = self._create_credential(self.config,
self.config.get("admin"),
permissions.ADMIN)
return {"admin": admin, "users": users}
# NOTE(astudenov): Let's consider moving rally.osclients here

View File

@ -108,9 +108,9 @@ class OpenStackScenario(scenario.Scenario):
@classmethod
def validate(cls, name, config, admin=None, users=None, deployment=None):
if admin:
admin = osclients.Clients(admin)
admin = admin.clients()
if users:
users = [osclients.Clients(user["credential"]) for user in users]
users = [user["credential"].clients() for user in users]
super(OpenStackScenario, cls).validate(
name=name, config=config, admin=admin, users=users,
deployment=deployment)

View File

@ -21,8 +21,6 @@ import six
from six.moves import configparser
from six.moves.urllib import parse
from rally.common import objects
from rally import osclients
from rally.verification import utils
@ -88,7 +86,7 @@ class TempestConfigfileManager(object):
def __init__(self, deployment):
self.credential = deployment.get_credentials_for("openstack")["admin"]
self.clients = osclients.Clients(objects.Credential(**self.credential))
self.clients = self.credential.clients()
self.available_services = self.clients.services().values()
self.conf = configparser.ConfigParser()
@ -100,14 +98,14 @@ class TempestConfigfileManager(object):
def _configure_auth(self, section_name="auth"):
self.conf.set(section_name, "admin_username",
self.credential["username"])
self.credential.username)
self.conf.set(section_name, "admin_password",
self.credential["password"])
self.credential.password)
self.conf.set(section_name, "admin_project_name",
self.credential["tenant_name"])
self.credential.tenant_name)
# Keystone v3 related parameter
self.conf.set(section_name, "admin_domain_name",
self.credential["user_domain_name"] or "Default")
self.credential.user_domain_name or "Default")
# Sahara has two service types: 'data_processing' and 'data-processing'.
# 'data_processing' is deprecated, but it can be used in previous OpenStack
@ -120,9 +118,9 @@ class TempestConfigfileManager(object):
def _configure_identity(self, section_name="identity"):
self.conf.set(section_name, "region",
self.credential["region_name"])
self.credential.region_name)
auth_url = self.credential["auth_url"]
auth_url = self.credential.auth_url
if "/v2" not in auth_url and "/v3" not in auth_url:
auth_version = "v2"
auth_url_v2 = parse.urljoin(auth_url, "/v2.0")
@ -136,9 +134,9 @@ class TempestConfigfileManager(object):
auth_url_v2.replace("/v2.0", "/v3"))
self.conf.set(section_name, "disable_ssl_certificate_validation",
str(self.credential["https_insecure"]))
str(self.credential.https_insecure))
self.conf.set(section_name, "ca_certificates_file",
self.credential["https_cacert"])
self.credential.https_cacert)
# The compute section is configured in context class for Tempest resources.
# Options which are configured there: 'image_ref', 'image_ref_alt',

View File

@ -21,9 +21,7 @@ from six.moves import configparser
from rally.common.i18n import _
from rally.common import logging
from rally.common import objects
from rally import exceptions
from rally import osclients
from rally.plugins.openstack.verification.tempest import config as conf
from rally.plugins.openstack.wrappers import glance
from rally.plugins.openstack.wrappers import network
@ -45,7 +43,7 @@ class TempestContext(context.VerifierContext):
super(TempestContext, self).__init__(ctx)
creds = self.verifier.deployment.get_credentials_for("openstack")
self.clients = osclients.Clients(objects.Credential(**creds["admin"]))
self.clients = creds["admin"].clients()
self.available_services = self.clients.services().values()
self.conf = configparser.ConfigParser()

View File

@ -24,10 +24,9 @@ import sys
import six
from rally.cli import cliutils
from rally.common import objects
from rally.common.plugin import discover
from rally import consts
from rally import osclients
from rally.plugins.openstack import credential
def skip_if_service(service):
@ -340,7 +339,7 @@ class CloudResources(object):
"""
def __init__(self, **kwargs):
self.clients = osclients.Clients(objects.Credential(**kwargs))
self.clients = credential.OpenStackCredential(**kwargs).clients()
def _deduplicate(self, lst):
"""Change list duplicates to make all items unique.
@ -426,8 +425,8 @@ def main():
out = subprocess.check_output(["rally", "deployment",
"config"])
config = json.loads(out if six.PY2 else out.decode("utf-8"))
config = config["creds"]["openstack"]
config.update(config.pop("admin"))
del config["type"]
if "users" in config:
del config["users"]

View File

@ -64,7 +64,7 @@ function setUp () {
DEPLOYMENT_CONFIG_FILE=~/.rally/with-existing-users-config
rally deployment config > $DEPLOYMENT_CONFIG_FILE
sed -i '1a "users": [\
sed -i '3a "users": [\
{\
"username": "rally-test-user-1",\
"password": "rally-test-password-1",\

View File

@ -23,8 +23,7 @@ import sys
import uuid
from rally.cli import envutils
from rally.common import objects
from rally import osclients
from rally.plugins.openstack import credential
from rally.ui import utils
LOG = logging.getLogger(__name__)
@ -172,9 +171,9 @@ def main():
config = json.loads(
subprocess.check_output(["rally", "deployment", "config"]))
config = config["creds"]["openstack"]
config.update(config.pop("admin"))
del config["type"]
clients = osclients.Clients(objects.Credential(**config))
clients = credential.OpenStackCredential(**config).clients()
if args.ctx_create_resources:
# If the 'ctx-create-resources' arg is provided, delete images and

View File

@ -115,11 +115,12 @@ class ConfigSchemasTestCase(test.TestCase):
self.fail(p, schema, ("Found unexpected key(s) for integer/number "
"type: %s." % ", ".join(unexpected_keys)))
def _check_simpliest_types(self, p, schema):
def _check_simpliest_types(self, p, schema, type_name):
unexpected_keys = set(schema.keys()) - {"type", "description"}
if unexpected_keys:
self.fail(p, schema, ("Found unexpected key(s) for boolean type: "
"%s." % ", ".join(unexpected_keys)))
self.fail(p, schema, ("Found unexpected key(s) for %s type: "
"%s." % (type_name,
", ".join(unexpected_keys))))
def _check_item(self, p, schema, definitions):
if "type" in schema or "anyOf" in schema or "oneOf" in schema:
@ -135,7 +136,9 @@ class ConfigSchemasTestCase(test.TestCase):
elif schema["type"] in ("number", "integer"):
self._check_number_type(p, schema)
elif schema["type"] in ("boolean", "null"):
self._check_simpliest_types(p, schema)
self._check_simpliest_types(p, schema, schema["type"])
elif isinstance(schema["type"], list):
self._check_simpliest_types(p, schema, "mixed")
else:
self.fail(p, schema,
"Wrong type is used: %s" % schema["type"])

View File

@ -28,7 +28,6 @@ import six
from swiftclient import exceptions as swift_exceptions
from rally import api
from rally.common import objects
from rally.common import utils as rally_utils
from rally import consts
from rally.task import context
@ -83,6 +82,14 @@ def setup_dict(data, required=None, defaults=None):
return defaults
def fake_credential(**config):
m = mock.Mock()
m.to_dict.return_value = config
for key, value in config.items():
setattr(m, key, value)
return m
class FakeResource(object):
def __init__(self, manager=None, name=None, status="ACTIVE", items=None,
@ -1594,11 +1601,11 @@ class FakeClients(object):
self._ec2 = None
self._senlin = None
self._watcher = None
self._credential = credential_ or objects.Credential(
"http://fake.example.org:5000/v2.0/",
"fake_username",
"fake_password",
"fake_tenant_name")
self._credential = credential_ or fake_credential(
auth_url="http://fake.example.org:5000/v2.0/",
username="fake_username",
password="fake_password",
tenant_name="fake_tenant_name")
def keystone(self, version=None):
if not self._keystone:
@ -1806,11 +1813,19 @@ class FakeUserContext(FakeContext):
admin = {
"id": "adminuuid",
"credential": objects.Credential("aurl", "aname", "apwd", "atenant")
"credential": fake_credential(
auth_url="aurl",
username="aname",
password="apwd",
tenant_name="atenant")
}
user = {
"id": "uuid",
"credential": objects.Credential("url", "name", "pwd", "tenant"),
"credential": fake_credential(
auth_url="url",
username="name",
password="pwd",
tenant_name="tenant"),
"tenant_id": "uuid"
}
tenants = {"uuid": {"name": "tenant"}}

View File

@ -31,86 +31,19 @@ class SeekAndDestroyTestCase(test.TestCase):
# clear out the client cache
manager.SeekAndDestroy.cache = {}
@mock.patch("%s.osclients.Clients" % BASE,
side_effect=[mock.MagicMock(), mock.MagicMock()])
def test__get_cached_client(self, mock_clients):
destroyer = manager.SeekAndDestroy(None, None, None)
self.assertIsNone(destroyer._get_cached_client(None))
users = [{"credential": "a"}, {"credential": "b"}]
self.assertEqual(destroyer._get_cached_client(users[0]),
destroyer._get_cached_client(users[0]))
# ensure that cache is used
self.assertItemsEqual(mock_clients.call_args_list,
[mock.call("a", api_info=None)])
mock_clients.reset_mock()
self.assertEqual(destroyer._get_cached_client(users[1]),
destroyer._get_cached_client(users[1]))
self.assertItemsEqual(mock_clients.call_args_list,
[mock.call("b", api_info=None)])
mock_clients.reset_mock()
self.assertNotEqual(destroyer._get_cached_client(users[0]),
destroyer._get_cached_client(users[1]))
self.assertFalse(mock_clients.called)
@mock.patch("%s.osclients.Clients" % BASE,
side_effect=[mock.MagicMock(), mock.MagicMock()])
def test__get_cached_client_shared_cache(self, mock_clients):
# ensure that cache is shared between SeekAndDestroy objects
destroyer1 = manager.SeekAndDestroy(None, None, None)
destroyer2 = manager.SeekAndDestroy(None, None, None)
user = {"credential": "a"}
self.assertEqual(destroyer1._get_cached_client(user),
destroyer2._get_cached_client(user))
self.assertItemsEqual(mock_clients.call_args_list,
[mock.call("a", api_info=None)])
@mock.patch("%s.osclients.Clients" % BASE,
side_effect=[mock.MagicMock(), mock.MagicMock()])
def test__get_cached_client_shared_cache_api_versions(self, mock_clients):
# ensure that cache is shared between SeekAndDestroy objects
# with matching api_versions dicts
def test__get_cached_client(self):
api_versions = {"cinder": {"version": "1", "service_type": "volume"}}
destroyer1 = manager.SeekAndDestroy(None, None, None,
api_versions=api_versions)
destroyer2 = manager.SeekAndDestroy(None, None, None,
api_versions=api_versions)
destroyer = manager.SeekAndDestroy(None, None, None,
api_versions=api_versions)
cred = mock.Mock()
user = {"credential": cred}
user = {"credential": "a"}
clients = destroyer._get_cached_client(user)
self.assertIs(cred.clients.return_value, clients)
cred.clients.assert_called_once_with(api_info=api_versions)
self.assertEqual(destroyer1._get_cached_client(user),
destroyer2._get_cached_client(user))
self.assertItemsEqual(mock_clients.call_args_list,
[mock.call("a", api_info=api_versions)])
@mock.patch("%s.osclients.Clients" % BASE,
side_effect=[mock.MagicMock(), mock.MagicMock()])
def test__get_cached_client_no_cache_api_versions(self, mock_clients):
# ensure that cache is not shared between SeekAndDestroy
# objects with different api_versions dicts
api_versions = [
{"cinder": {"version": "1", "service_type": "volume"}},
{"cinder": {"version": "2", "service_type": "volumev2"}}
]
destroyer1 = manager.SeekAndDestroy(None, None, None,
api_versions=api_versions[0])
destroyer2 = manager.SeekAndDestroy(None, None, None,
api_versions=api_versions[1])
user = {"credential": "a"}
self.assertNotEqual(destroyer1._get_cached_client(user),
destroyer2._get_cached_client(user))
self.assertItemsEqual(mock_clients.call_args_list,
[mock.call("a", api_info=api_versions[0]),
mock.call("a", api_info=api_versions[1])])
self.assertIsNone(destroyer._get_cached_client(None))
@mock.patch("%s.LOG" % BASE)
def test__delete_single_resource(self, mock_log):

View File

@ -15,10 +15,10 @@
import mock
from rally.common import objects
from rally import consts
from rally import exceptions
from rally.plugins.openstack.context.keystone import users
from rally.plugins.openstack import credential as oscredential
from tests.unit import test
CTX = "rally.plugins.openstack.context.keystone.users"
@ -274,16 +274,17 @@ class UserGeneratorTestCase(test.ScenarioTestCase):
def test_users_and_tenants_in_context(self, mock_identity):
identity_service = mock_identity.Identity.return_value
credential = objects.Credential("foo_url", "foo", "foo_pass",
https_insecure=True,
https_cacert="cacert")
credential = oscredential.OpenStackCredential(
"foo_url", "foo", "foo_pass",
https_insecure=True,
https_cacert="cacert")
tmp_context = dict(self.context)
tmp_context["config"]["users"] = {"tenants": 1,
"users_per_tenant": 2,
"resource_management_workers": 1}
tmp_context["admin"]["credential"] = credential
credential_dict = credential.to_dict(False)
credential_dict = credential.to_dict()
user_list = [mock.MagicMock(id="id_%d" % i)
for i in range(self.users_num)]
identity_service.create_user.side_effect = user_list
@ -302,7 +303,7 @@ class UserGeneratorTestCase(test.ScenarioTestCase):
self.assertEqual(set(["id", "credential", "tenant_id"]),
set(user.keys()))
user_credential_dict = user["credential"].to_dict(False)
user_credential_dict = user["credential"].to_dict()
excluded_keys = ["auth_url", "username", "password",
"tenant_name", "region_name",
@ -323,7 +324,7 @@ class UserGeneratorTestCase(test.ScenarioTestCase):
@mock.patch("%s.identity" % CTX)
def test_users_contains_correct_endpoint_type(self, mock_identity):
credential = objects.Credential(
credential = oscredential.OpenStackCredential(
"foo_url", "foo", "foo_pass",
endpoint_type=consts.EndpointType.INTERNAL)
config = {
@ -346,7 +347,8 @@ class UserGeneratorTestCase(test.ScenarioTestCase):
@mock.patch("%s.identity" % CTX)
def test_users_contains_default_endpoint_type(self, mock_identity):
credential = objects.Credential("foo_url", "foo", "foo_pass")
credential = oscredential.OpenStackCredential(
"foo_url", "foo", "foo_pass")
config = {
"config": {
"users": {

View File

@ -14,8 +14,8 @@
import mock
from rally.common import objects
from rally.plugins.openstack.context.sahara import sahara_output_data_sources
from rally.plugins.openstack import credential as oscredential
from rally.plugins.openstack.scenarios.sahara import utils as sahara_utils
from tests.unit import test
@ -26,8 +26,8 @@ class SaharaOutputDataSourcesTestCase(test.ScenarioTestCase):
def setUp(self):
super(SaharaOutputDataSourcesTestCase, self).setUp()
fake_dict = objects.Credential("http://fake.example.org:5000/v2.0/",
"user", "passwd")
fake_dict = oscredential.OpenStackCredential(
"http://fake.example.org:5000/v2.0/", "user", "passwd")
self.tenants_num = 2
self.users_per_tenant = 2
self.users = self.tenants_num * self.users_per_tenant

View File

@ -0,0 +1,149 @@
# Copyright 2017: 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 jsonschema
import mock
from rally import consts
from rally.deployment import credential
from tests.unit import test
class OpenStackCredentialTestCase(test.TestCase):
def setUp(self):
super(OpenStackCredentialTestCase, self).setUp()
cred_cls = credential.get("openstack")
self.credential = cred_cls(
"foo_url", "foo_user", "foo_password",
tenant_name="foo_tenant",
permission=consts.EndpointPermission.ADMIN)
def test_to_dict(self):
self.assertEqual({"auth_url": "foo_url",
"username": "foo_user",
"password": "foo_password",
"tenant_name": "foo_tenant",
"region_name": None,
"domain_name": None,
"endpoint": None,
"permission": consts.EndpointPermission.ADMIN,
"endpoint_type": None,
"https_insecure": False,
"https_cacert": None,
"project_domain_name": None,
"user_domain_name": None}, self.credential.to_dict())
@mock.patch("rally.osclients.Clients")
def test_verify_connection_admin(self, mock_clients):
self.credential.verify_connection()
mock_clients.assert_called_once_with(
self.credential, api_info=None, cache={})
mock_clients.return_value.verified_keystone.assert_called_once_with()
@mock.patch("rally.osclients.Clients")
def test_verify_connection_user(self, mock_clients):
self.credential.permission = consts.EndpointPermission.USER
self.credential.verify_connection()
mock_clients.assert_called_once_with(
self.credential, api_info=None, cache={})
mock_clients.return_value.keystone.assert_called_once_with()
@mock.patch("rally.osclients.Clients")
def test_list_services(self, mock_clients):
result = self.credential.list_services()
mock_clients.assert_called_once_with(
self.credential, api_info=None, cache={})
mock_clients.return_value.services.assert_called_once_with()
self.assertIs(mock_clients.return_value.services.return_value, result)
@mock.patch("rally.osclients.Clients")
def test_clients(self, mock_clients):
clients = self.credential.clients(api_info="fake_info")
mock_clients.assert_called_once_with(
self.credential, api_info="fake_info", cache={})
self.assertIs(mock_clients.return_value, clients)
class OpenStackCredentialBuilderTestCase(test.TestCase):
def setUp(self):
super(OpenStackCredentialBuilderTestCase, self).setUp()
self.config = {
"auth_url": "http://example.net:5000/v2.0/",
"region_name": "RegionOne",
"endpoint_type": consts.EndpointType.INTERNAL,
"https_insecure": False,
"https_cacert": "cacert",
"admin": {
"username": "admin",
"password": "myadminpass",
"tenant_name": "demo"
},
"users": [
{
"username": "user1",
"password": "userpass",
"tenant_name": "demo"
}
]
}
self.cred_builder_cls = credential.get_builder("openstack")
def test_validate(self):
self.cred_builder_cls.validate(self.config)
def test_validate_error(self):
self.assertRaises(jsonschema.ValidationError,
self.cred_builder_cls.validate,
{"foo": "bar"})
def test_build_credentials(self):
creds_builder = self.cred_builder_cls(self.config)
creds = creds_builder.build_credentials()
self.assertEqual({
"admin": {
"auth_url": "http://example.net:5000/v2.0/",
"username": "admin",
"password": "myadminpass",
"permission": consts.EndpointPermission.ADMIN,
"domain_name": None,
"endpoint": None,
"endpoint_type": consts.EndpointType.INTERNAL,
"https_cacert": "cacert",
"https_insecure": False,
"project_domain_name": None,
"region_name": "RegionOne",
"tenant_name": "demo",
"user_domain_name": None,
},
"users": [
{
"auth_url": "http://example.net:5000/v2.0/",
"username": "user1",
"password": "userpass",
"permission": consts.EndpointPermission.USER,
"domain_name": None,
"endpoint": None,
"endpoint_type": consts.EndpointType.INTERNAL,
"https_cacert": "cacert",
"https_insecure": False,
"project_domain_name": None,
"region_name": "RegionOne",
"tenant_name": "demo",
"user_domain_name": None,
}
]
}, creds)

View File

@ -18,6 +18,7 @@ import mock
from oslotest import mockpatch
from rally.plugins.openstack import scenario as base_scenario
from tests.unit import fakes
from tests.unit import test
@ -130,27 +131,21 @@ class OpenStackScenarioTestCase(test.TestCase):
@mock.patch("rally.task.scenario.Scenario.validate")
def test_validate(self, mock_scenario_validate):
cred1 = mock.Mock()
cred2 = mock.Mock()
cred3 = mock.Mock()
self.osclients.mock.side_effect = [cred1, cred2, cred3]
cred1 = fakes.fake_credential(foo="bar1")
cred2 = fakes.fake_credential(foo="bar2")
cred3 = fakes.fake_credential(foo="bar3")
base_scenario.OpenStackScenario.validate(
name="foo_name",
config="foo_config",
admin="foo_admin",
users=[{"credential": "foo_user1"},
{"credential": "foo_user2"}],
admin=cred1,
users=[{"credential": cred2},
{"credential": cred3}],
deployment=None)
mock_scenario_validate.assert_called_once_with(
name="foo_name",
config="foo_config",
admin=cred1,
users=[cred2, cred3],
admin=cred1.clients.return_value,
users=[cred2.clients.return_value, cred3.clients.return_value],
deployment=None)
self.osclients.mock.assert_has_calls([
mock.call("foo_admin"),
mock.call("foo_user1"),
mock.call("foo_user2"),
])

View File

@ -25,20 +25,17 @@ from tests.unit import test
CONF = cfg.CONF
CREDS = {
"admin": {
"username": "admin",
"tenant_name": "admin",
"password": "admin-12345",
"auth_url": "http://test:5000/v2.0/",
"permission": "admin",
"region_name": "test",
"https_insecure": False,
"https_cacert": "/path/to/cacert/file",
"user_domain_name": "admin",
"project_domain_name": "admin"
},
"uuid": "fake_deployment"
CRED = {
"username": "admin",
"tenant_name": "admin",
"password": "admin-12345",
"auth_url": "http://test:5000/v2.0/",
"permission": "admin",
"region_name": "test",
"https_insecure": False,
"https_cacert": "/path/to/cacert/file",
"user_domain_name": "admin",
"project_domain_name": "admin"
}
PATH = "rally.plugins.openstack.verification.tempest.config"
@ -49,10 +46,8 @@ class TempestConfigfileManagerTestCase(test.TestCase):
def setUp(self):
super(TempestConfigfileManagerTestCase, self).setUp()
mock.patch("rally.osclients.Clients").start()
deployment = fakes.FakeDeployment(**CREDS)
deployment = fakes.FakeDeployment(uuid="fake_deployment",
admin=fakes.fake_credential(**CRED))
self.tempest = config.TempestConfigfileManager(deployment)
def test__configure_auth(self):
@ -60,10 +55,10 @@ class TempestConfigfileManagerTestCase(test.TestCase):
self.tempest._configure_auth()
expected = (
("admin_username", CREDS["admin"]["username"]),
("admin_password", CREDS["admin"]["password"]),
("admin_project_name", CREDS["admin"]["tenant_name"]),
("admin_domain_name", CREDS["admin"]["user_domain_name"]))
("admin_username", CRED["username"]),
("admin_password", CRED["password"]),
("admin_project_name", CRED["tenant_name"]),
("admin_domain_name", CRED["user_domain_name"]))
result = self.tempest.conf.items("auth")
for item in expected:
self.assertIn(item, result)
@ -85,13 +80,13 @@ class TempestConfigfileManagerTestCase(test.TestCase):
self.tempest._configure_identity()
expected = (
("region", CREDS["admin"]["region_name"]),
("region", CRED["region_name"]),
("auth_version", "v2"),
("uri", CREDS["admin"]["auth_url"][:-1]),
("uri_v3", CREDS["admin"]["auth_url"].replace("/v2.0/", "/v3")),
("uri", CRED["auth_url"][:-1]),
("uri_v3", CRED["auth_url"].replace("/v2.0/", "/v3")),
("disable_ssl_certificate_validation",
str(CREDS["admin"]["https_insecure"])),
("ca_certificates_file", CREDS["admin"]["https_cacert"]))
str(CRED["https_insecure"])),
("ca_certificates_file", CRED["https_cacert"]))
result = self.tempest.conf.items("identity")
for item in expected:
self.assertIn(item, result)

View File

@ -30,20 +30,17 @@ from tests.unit import test
CONF = cfg.CONF
CREDS = {
"admin": {
"username": "admin",
"tenant_name": "admin",
"password": "admin-12345",
"auth_url": "http://test:5000/v2.0/",
"permission": "admin",
"region_name": "test",
"https_insecure": False,
"https_cacert": "/path/to/cacert/file",
"user_domain_name": "admin",
"project_domain_name": "admin"
},
"uuid": "fake_deployment"
CRED = {
"username": "admin",
"tenant_name": "admin",
"password": "admin-12345",
"auth_url": "http://test:5000/v2.0/",
"permission": "admin",
"region_name": "test",
"https_insecure": False,
"https_cacert": "/path/to/cacert/file",
"user_domain_name": "admin",
"project_domain_name": "admin"
}
PATH = "rally.plugins.openstack.verification.tempest.context"
@ -55,11 +52,12 @@ class TempestContextTestCase(test.TestCase):
def setUp(self):
super(TempestContextTestCase, self).setUp()
mock.patch("rally.osclients.Clients").start()
self.mock_isfile = mock.patch("os.path.isfile",
return_value=True).start()
self.deployment = fakes.FakeDeployment(**CREDS)
self.cred = fakes.fake_credential(**CRED)
self.deployment = fakes.FakeDeployment(
uuid="fake_deployment", admin=self.cred)
cfg = {"verifier": mock.Mock(deployment=self.deployment),
"verification": {"uuid": "uuid"}}
cfg["verifier"].manager.home_dir = "/p/a/t/h"
@ -244,6 +242,7 @@ class TempestContextTestCase(test.TestCase):
def test__discover_or_create_flavor(self):
client = self.context.clients.nova()
client.flavors.list.return_value = []
client.flavors.create.side_effect = [fakes.FakeFlavor(id="id1")]
flavor = self.context._discover_or_create_flavor(64)
@ -260,6 +259,7 @@ class TempestContextTestCase(test.TestCase):
client.create_network.side_effect = [{"network": fake_network}]
client.create_router.side_effect = [{"router": {"id": "rid1"}}]
client.create_subnet.side_effect = [{"subnet": {"id": "subid1"}}]
client.list_networks.return_value = {"networks": []}
network = self.context._create_network_resources()
self.assertEqual("nid1", network["id"])
@ -333,16 +333,14 @@ class TempestContextTestCase(test.TestCase):
@mock.patch("%s.TempestContext._configure_option" % PATH)
@mock.patch("%s.TempestContext._create_tempest_roles" % PATH)
@mock.patch("rally.verification.utils.create_dir")
@mock.patch("%s.osclients.Clients" % PATH)
def test_setup(self, mock_clients, mock_create_dir,
def test_setup(self, mock_create_dir,
mock__create_tempest_roles, mock__configure_option,
mock_open):
self.deployment = fakes.FakeDeployment(**CREDS)
verifier = mock.Mock(deployment=self.deployment)
verifier.manager.home_dir = "/p/a/t/h"
# case #1: no neutron and heat
mock_clients.return_value.services.return_value = {}
self.cred.clients.return_value.services.return_value = {}
ctx = context.TempestContext({"verifier": verifier})
ctx.conf = mock.Mock()
@ -377,10 +375,12 @@ class TempestContextTestCase(test.TestCase):
mock__configure_option.reset_mock()
# case #2: neutron and heat are presented
mock_clients.return_value.services.return_value = {
self.cred.clients.return_value.services.return_value = {
"network": "neutron", "orchestration": "heat"}
ctx = context.TempestContext({"verifier": verifier})
neutron = ctx.clients.neutron()
neutron.list_networks.return_value = {"networks": ["fake_net"]}
ctx.conf = mock.Mock()
ctx.setup()