[api versions]Support to specify api versions in the spec of env

Spec example:
  Specify cinder client version

  {
      "openstack": {
          "auth_url": "http://example.net:5000/v2.0/",
          "region_name": "RegionOne",
          "endpoint_type": "public",
          "admin": {
              "username": "admin",
              "password": "myadminpass",
              "tenant_name": "demo"
          },
          "https_insecure": false,
          "https_cacert": ""
          "api_info": {
              "cinder": {
                  "version": "1",
                  "service_type": "volume"
              }
          }
      }
  }

Change-Id: I74578e3b1c4e7b662ba66955ef9fb62cd4eeb788
This commit is contained in:
chenhb 2018-08-06 13:46:47 +08:00
parent 467cc05559
commit 0a73f8ae80
11 changed files with 224 additions and 13 deletions

View File

@ -27,6 +27,7 @@ Added
``existing@openstack`` platform to represent client certificate bundle and
key files. Also the support for appropriate system environment variables (
``OS_CERT``, ``OS_KEY``) is added.
* Support client api option while deploying platform.
Changed
~~~~~~~

View File

@ -14,6 +14,7 @@
# under the License.
import collections
import copy
import uuid
from rally.common import broker
@ -106,8 +107,14 @@ class UserGenerator(context.Context):
creds = self.env["platforms"]["openstack"]
if creds.get("admin"):
admin_cred = copy.deepcopy(creds["admin"])
api_info = copy.deepcopy(creds.get("api_info", {}))
if "api_info" in admin_cred:
api_info.update(creds["admin"]["api_info"])
admin_cred["api_info"] = api_info
context["admin"] = {
"credential": credential.OpenStackCredential(**creds["admin"])}
"credential": credential.OpenStackCredential(**admin_cred)
}
if creds["users"] and not (set(self.config) - {"user_choice_method"}):
self.existing_users = creds["users"]
@ -218,7 +225,8 @@ class UserGenerator(context.Context):
https_cacert=self.credential["https_cacert"],
region_name=self.credential["region_name"],
profiler_hmac_key=self.credential["profiler_hmac_key"],
profiler_conn_str=self.credential["profiler_conn_str"])
profiler_conn_str=self.credential["profiler_conn_str"],
api_info=self.credential["api_info"])
users.append({"id": user.id,
"credential": user_credential,
"tenant_id": tenant_id})
@ -284,7 +292,13 @@ class UserGenerator(context.Context):
def use_existing_users(self):
LOG.debug("Using existing users for OpenStack platform.")
api_info = copy.deepcopy(self.env["platforms"]["openstack"].get(
"api_info", {}))
for user_credential in self.existing_users:
user_credential = copy.deepcopy(user_credential)
if "api_info" in user_credential:
api_info.update(user_credential["api_info"])
user_credential["api_info"] = api_info
user_credential = credential.OpenStackCredential(**user_credential)
user_clients = osclients.Clients(user_credential)
user_id = user_clients.keystone.auth_ref.user_id

View File

@ -28,7 +28,8 @@ class OpenStackCredential(dict):
domain_name=None, endpoint=None, user_domain_name=None,
project_domain_name=None,
https_insecure=False, https_cacert=None, https_cert=None,
profiler_hmac_key=None, profiler_conn_str=None, **kwargs):
profiler_hmac_key=None, profiler_conn_str=None,
api_info=None, **kwargs):
if kwargs:
raise TypeError("%s" % kwargs)
@ -50,7 +51,8 @@ class OpenStackCredential(dict):
("https_cacert", https_cacert),
("https_cert", https_cert),
("profiler_hmac_key", profiler_hmac_key),
("profiler_conn_str", profiler_conn_str)
("profiler_conn_str", profiler_conn_str),
("api_info", api_info or {})
])
self._clients_cache = {}

View File

@ -111,7 +111,8 @@ class OSClient(plugin.Plugin):
self.credential = credential
if not isinstance(self.credential, oscred.OpenStackCredential):
self.credential = oscred.OpenStackCredential(**self.credential)
self.api_info = api_info
if api_info:
self.credential.api_info.update(api_info)
self.cache = cache_obj
def choose_version(self, version=None):
@ -138,8 +139,8 @@ class OSClient(plugin.Plugin):
# For those clients which doesn't accept string value(for example
# zaqarclient), this method should be overridden.
version = (version or
self.api_info.get(self.get_name(), {}).get("version") or
self._meta_get("default_version"))
self.credential.api_info.get(self.get_name(), {}).get(
"version") or self._meta_get("default_version"))
if version is not None:
version = str(version)
return version
@ -172,8 +173,8 @@ class OSClient(plugin.Plugin):
service type from api_info(configured from a context) and default.
"""
return (service_type or
self.api_info.get(self.get_name(), {}).get("service_type") or
self._meta_get("default_service_type"))
self.credential.api_info.get(self.get_name(), {}).get(
"service_type") or self._meta_get("default_service_type"))
@classmethod
def is_service_type_configurable(cls):
@ -184,7 +185,7 @@ class OSClient(plugin.Plugin):
@property
def keystone(self):
return OSClient.get("keystone")(self.credential, self.api_info,
return OSClient.get("keystone")(self.credential, None,
self.cache)
def _get_endpoint(self, service_type=None):

View File

@ -33,6 +33,13 @@ class OpenStack(platform.Platform):
It may be used to test any existing OpenStack API compatible cloud.
"""
VERSION_SCHEMA = {
"anyOf": [
{"type": "string", "description": "a string-like version."},
{"type": "number", "description": "a number-like version."}
]
}
CONFIG_SCHEMA = {
"type": "object",
"definitions": {
@ -63,6 +70,21 @@ class OpenStack(platform.Platform):
"additionalProperties": False
}
],
},
"api_info": {
"type": "object",
"patternProperties": {
"^[a-z]+$": {
"type": "object",
"properties": {
"version": VERSION_SCHEMA,
"service_type": {"type": "string"}
},
"minProperties": 1,
"additionalProperties": False
}
},
"additionalProperties": False
}
},
"properties": {
@ -81,7 +103,8 @@ class OpenStack(platform.Platform):
"type": "array",
"items": {"$ref": "#/definitions/user"},
"minItems": 1
}
},
"api_info": {"$ref": "#/definitions/api_info"}
},
"anyOf": [
{
@ -115,6 +138,7 @@ class OpenStack(platform.Platform):
del new_data["endpoint"]
admin = new_data.pop("admin", None)
users = new_data.pop("users", [])
api_info = new_data.pop("api_info", None)
if new_data.get("https_cert") and new_data.get("https_key"):
new_data["https_cert"] = (new_data["https_cert"],
@ -132,7 +156,10 @@ class OpenStack(platform.Platform):
user.update(new_data)
for k, v in defaults.items():
user.setdefault(k, v)
return {"admin": admin, "users": users}, {}
platform_data = {"admin": admin, "users": users}
if api_info:
platform_data["api_info"] = api_info
return platform_data, {}
def destroy(self):
# NOTE(boris-42): No action need to be performed.

View File

@ -28,3 +28,9 @@ existing-with-predefined-users.json
If you are using read-only backend in Keystone like LDAP, AD then
you need this sample. If you don't specify "users" rally will use already
existing users that you provide.
existing-api.json
--------------------------------
If you expect to specify version of some clients, you could register existing
Openstack cluster like this sample.

View File

@ -0,0 +1,20 @@
{
"openstack": {
"auth_url": "http://example.net:5000/v2.0/",
"region_name": "RegionOne",
"endpoint_type": "public",
"admin": {
"username": "admin",
"password": "myadminpass",
"tenant_name": "demo"
},
"https_insecure": false,
"https_cacert": ""
"api_info": {
"specified_client_name": {
"version": "version_number",
"service_type": "service_type"
}
}
}
}

View File

@ -0,0 +1,39 @@
# 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_openstack import osclients
from rally_openstack import scenario
@osclients.configure("fakedummy", default_version="1",
default_service_type="dummy",
supported_versions=["1", "2"])
class FakeDummy(osclients.OSClient):
def create_client(self, version=None, service_type=None):
version = self.choose_version(version)
service_type = self.choose_service_type(service_type)
return {"version": version, "service_type": service_type}
@scenario.configure(name="FakeDummy.openstack_api")
class FakeDummyOpenstackAPI(scenario.OpenStackScenario):
def run(self):
admin_client = self.admin_clients("fakedummy")
self.assertEqual("dummyv2", admin_client["service_type"])
self.assertEqual("2", admin_client["version"])
client = self.clients("fakedummy")
self.assertEqual("dummyv2", client["service_type"])
self.assertEqual("2", client["version"])

View File

@ -0,0 +1,81 @@
# 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 json
import unittest
from tests.functional import utils
class TaskTestCase(unittest.TestCase):
def test_specify_version_by_deployment(self):
rally = utils.Rally()
deployment = json.loads(rally("deployment config"))
deployment["openstack"]["api_info"] = {
"fakedummy": {
"version": "2",
"service_type": "dummyv2"
}
}
deployment = utils.JsonTempFile(deployment)
rally("deployment create --name t_create_with_api_info "
"--filename %s" % deployment.filename)
self.assertIn("t_create_with_api_info", rally("deployment list"))
config = {
"FakeDummy.openstack_api": [
{
"runner": {
"type": "constant",
"times": 1,
"concurrency": 1
}
}
]
}
config = utils.TaskConfig(config)
plugins = "tests/functional/extra/fake_dir/fake_plugin.py"
rally("--plugin-paths %s task start --task %s" % (
plugins, config.filename))
def test_specify_version_by_deployment_with_existing_users(self):
rally = utils.Rally()
deployment = json.loads(rally("deployment config"))
deployment["openstack"]["users"] = [deployment["openstack"]["admin"]]
deployment["openstack"]["api_info"] = {
"fakedummy": {
"version": "2",
"service_type": "dummyv2"
}
}
deployment = utils.JsonTempFile(deployment)
rally("deployment create --name t_create_with_api_info "
"--filename %s" % deployment.filename)
self.assertIn("t_create_with_api_info", rally("deployment list"))
config = {
"FakeDummy.openstack_api": [
{
"runner": {
"type": "constant",
"times": 1,
"concurrency": 1
}
}
]
}
config = utils.TaskConfig(config)
plugins = "tests/functional/extra/fake_dir/fake_plugin.py"
rally("--plugin-paths %s task start --task %s" % (
plugins, config.filename))

View File

@ -75,6 +75,25 @@ class ExistingPlatformTestCase(PlatformBaseTestCase):
spec, spec["existing@openstack"])
self.assertNotEqual([], result)
def test_validate_spec_schema_with_api_info(self):
spec = {
"existing@openstack": {
"auth_url": "url",
"admin": {
"username": "admin",
"password": "password123",
"tenant_name": "admin"
},
"api_info": {
"nova": {"version": 1},
"cinder": {"version": 2, "service_type": "volumev2"}
}
}
}
result = platform.Platform.validate("existing@openstack", {},
spec, spec["existing@openstack"])
self.assertEqual([], result)
def test_create_users_only(self):
spec = {

View File

@ -43,7 +43,8 @@ class OpenStackCredentialTestCase(test.TestCase):
"project_domain_name": None,
"user_domain_name": None,
"profiler_hmac_key": None,
"profiler_conn_str": None},
"profiler_conn_str": None,
"api_info": {}},
self.credential.to_dict())
@mock.patch("rally_openstack.osclients.Clients")