Merge "[validation] Use jsonschema validator in Context plugins"
This commit is contained in:
commit
f29c5538a2
@ -12,13 +12,37 @@
|
|||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from rally.common.i18n import _, _LE
|
from rally.common.i18n import _
|
||||||
|
from rally.common import validation
|
||||||
from rally import consts
|
from rally import consts
|
||||||
from rally import exceptions
|
from rally import exceptions
|
||||||
from rally import osclients
|
from rally import osclients
|
||||||
from rally.task import context
|
from rally.task import context
|
||||||
|
|
||||||
|
|
||||||
|
@validation.configure("check_api_versions")
|
||||||
|
class CheckOpenStackAPIVersionsValidator(validation.Validator):
|
||||||
|
"""Additional validation for api_versions context"""
|
||||||
|
|
||||||
|
def validate(self, credentials, config, plugin_cls, plugin_cfg):
|
||||||
|
for client in plugin_cfg:
|
||||||
|
client_cls = osclients.OSClient.get(client)
|
||||||
|
try:
|
||||||
|
if ("service_type" in plugin_cfg[client] or
|
||||||
|
"service_name" in plugin_cfg[client]):
|
||||||
|
client_cls.is_service_type_configurable()
|
||||||
|
|
||||||
|
if "version" in plugin_cfg[client]:
|
||||||
|
client_cls.validate_version(plugin_cfg[client]["version"])
|
||||||
|
|
||||||
|
except exceptions.RallyException as e:
|
||||||
|
return self.fail(
|
||||||
|
"Invalid settings for '%(client)s': %(error)s" % {
|
||||||
|
"client": client,
|
||||||
|
"error": e.format_message()})
|
||||||
|
|
||||||
|
|
||||||
|
@validation.add("check_api_versions")
|
||||||
@context.configure(name="api_versions", order=150)
|
@context.configure(name="api_versions", order=150)
|
||||||
class OpenStackAPIVersions(context.Context):
|
class OpenStackAPIVersions(context.Context):
|
||||||
"""Context for specifying OpenStack clients versions and service types.
|
"""Context for specifying OpenStack clients versions and service types.
|
||||||
@ -157,30 +181,49 @@ class OpenStackAPIVersions(context.Context):
|
|||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
VERSION_SCHEMA = {
|
||||||
|
"anyOf": [
|
||||||
|
{"type": "string", "description": "a string-like version."},
|
||||||
|
{"type": "number", "description": "a number-like version."}
|
||||||
|
]
|
||||||
|
}
|
||||||
CONFIG_SCHEMA = {
|
CONFIG_SCHEMA = {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"$schema": consts.JSON_SCHEMA,
|
"$schema": consts.JSON_SCHEMA,
|
||||||
"patternProperties": {
|
"patternProperties": {
|
||||||
"^[a-z]+$": {
|
"^[a-z]+$": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"oneOf": [
|
||||||
"version": {
|
{
|
||||||
"anyOf": [{"type": "string",
|
"description": "version only",
|
||||||
"description": "a string-like version."},
|
"properties": {
|
||||||
{"type": "number",
|
"version": VERSION_SCHEMA,
|
||||||
"description": "a number-like version."}]
|
},
|
||||||
|
"required": ["version"],
|
||||||
|
"additionalProperties": False
|
||||||
},
|
},
|
||||||
"service_name": {
|
{
|
||||||
"type": "string"
|
"description": "version and service_name",
|
||||||
|
"properties": {
|
||||||
|
"version": VERSION_SCHEMA,
|
||||||
|
"service_name": {"type": "string"}
|
||||||
|
},
|
||||||
|
"required": ["service_name"],
|
||||||
|
"additionalProperties": False
|
||||||
},
|
},
|
||||||
"service_type": {
|
{
|
||||||
"type": "string"
|
"description": "version and service_type",
|
||||||
|
"properties": {
|
||||||
|
"version": VERSION_SCHEMA,
|
||||||
|
"service_type": {"type": "string"}
|
||||||
|
},
|
||||||
|
"required": ["service_type"],
|
||||||
|
"additionalProperties": False
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
"additionalProperties": False
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"minProperties": 1,
|
||||||
"additionalProperties": False
|
"additionalProperties": False
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,27 +262,3 @@ class OpenStackAPIVersions(context.Context):
|
|||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
# nothing to do here
|
# nothing to do here
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def validate(cls, config):
|
|
||||||
super(OpenStackAPIVersions, cls).validate(config)
|
|
||||||
for client in config:
|
|
||||||
client_cls = osclients.OSClient.get(client)
|
|
||||||
if ("service_type" in config[client] and
|
|
||||||
"service_name" in config[client]):
|
|
||||||
raise exceptions.ValidationError(_LE(
|
|
||||||
"Setting both 'service_type' and 'service_name' properties"
|
|
||||||
" is restricted."))
|
|
||||||
try:
|
|
||||||
if ("service_type" in config[client] or
|
|
||||||
"service_name" in config[client]):
|
|
||||||
client_cls.is_service_type_configurable()
|
|
||||||
|
|
||||||
if "version" in config[client]:
|
|
||||||
client_cls.validate_version(config[client]["version"])
|
|
||||||
|
|
||||||
except exceptions.RallyException as e:
|
|
||||||
raise exceptions.ValidationError(
|
|
||||||
_LE("Invalid settings for '%(client)s': %(error)s") % {
|
|
||||||
"client": client,
|
|
||||||
"error": e.format_message()})
|
|
||||||
|
@ -17,6 +17,7 @@ import sys
|
|||||||
|
|
||||||
from rally.common.i18n import _
|
from rally.common.i18n import _
|
||||||
from rally.common import logging
|
from rally.common import logging
|
||||||
|
from rally.common import validation
|
||||||
from rally.plugins.openstack.cleanup import manager
|
from rally.plugins.openstack.cleanup import manager
|
||||||
from rally.plugins.openstack.context.cleanup import base
|
from rally.plugins.openstack.context.cleanup import base
|
||||||
from rally.plugins.openstack import scenario
|
from rally.plugins.openstack import scenario
|
||||||
@ -26,23 +27,12 @@ from rally.task import context
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@validation.add(name="check_cleanup_resources", admin_required=True)
|
||||||
# NOTE(amaretskiy): Set order to run this just before UserCleanup
|
# NOTE(amaretskiy): Set order to run this just before UserCleanup
|
||||||
@context.configure(name="admin_cleanup", order=(sys.maxsize - 1), hidden=True)
|
@context.configure(name="admin_cleanup", order=(sys.maxsize - 1), hidden=True)
|
||||||
class AdminCleanup(base.CleanupMixin, context.Context):
|
class AdminCleanup(base.CleanupMixin, context.Context):
|
||||||
"""Context class for admin resources cleanup."""
|
"""Context class for admin resources cleanup."""
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def validate(cls, config):
|
|
||||||
super(AdminCleanup, cls).validate(config)
|
|
||||||
|
|
||||||
missing = set(config)
|
|
||||||
missing -= manager.list_resource_names(admin_required=True)
|
|
||||||
missing = ", ".join(missing)
|
|
||||||
if missing:
|
|
||||||
LOG.info(_("Couldn't find cleanup resource managers: %s")
|
|
||||||
% missing)
|
|
||||||
raise base.NoSuchCleanupResources(missing)
|
|
||||||
|
|
||||||
@logging.log_task_wrapper(LOG.info, _("admin resources cleanup"))
|
@logging.log_task_wrapper(LOG.info, _("admin resources cleanup"))
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
manager.cleanup(
|
manager.cleanup(
|
||||||
|
@ -13,13 +13,27 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from rally.common.i18n import _
|
from rally.common import validation
|
||||||
from rally import consts
|
from rally import consts
|
||||||
from rally import exceptions
|
from rally.plugins.openstack.cleanup import manager
|
||||||
|
|
||||||
|
|
||||||
class NoSuchCleanupResources(exceptions.RallyException):
|
@validation.configure("check_cleanup_resources")
|
||||||
msg_fmt = _("Missing cleanup resource managers: %(message)s")
|
class CheckCleanupResourcesValidator(validation.Validator):
|
||||||
|
"""Validates that openstack resource managers exist"""
|
||||||
|
|
||||||
|
def __init__(self, admin_required):
|
||||||
|
super(CheckCleanupResourcesValidator, self).__init__()
|
||||||
|
self.admin_required = admin_required
|
||||||
|
|
||||||
|
def validate(self, credentials, config, plugin_cls, plugin_cfg):
|
||||||
|
missing = set(plugin_cfg)
|
||||||
|
missing -= manager.list_resource_names(
|
||||||
|
admin_required=self.admin_required)
|
||||||
|
missing = ", ".join(missing)
|
||||||
|
if missing:
|
||||||
|
return self.fail(
|
||||||
|
"Couldn't find cleanup resource managers: %s" % missing)
|
||||||
|
|
||||||
|
|
||||||
class CleanupMixin(object):
|
class CleanupMixin(object):
|
||||||
|
@ -17,6 +17,7 @@ import sys
|
|||||||
|
|
||||||
from rally.common.i18n import _
|
from rally.common.i18n import _
|
||||||
from rally.common import logging
|
from rally.common import logging
|
||||||
|
from rally.common import validation
|
||||||
from rally.plugins.openstack.cleanup import manager
|
from rally.plugins.openstack.cleanup import manager
|
||||||
from rally.plugins.openstack.context.cleanup import base
|
from rally.plugins.openstack.context.cleanup import base
|
||||||
from rally.plugins.openstack import scenario
|
from rally.plugins.openstack import scenario
|
||||||
@ -26,23 +27,12 @@ from rally.task import context
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@validation.add(name="check_cleanup_resources", admin_required=False)
|
||||||
# NOTE(amaretskiy): Set maximum order to run this last
|
# NOTE(amaretskiy): Set maximum order to run this last
|
||||||
@context.configure(name="cleanup", order=sys.maxsize, hidden=True)
|
@context.configure(name="cleanup", order=sys.maxsize, hidden=True)
|
||||||
class UserCleanup(base.CleanupMixin, context.Context):
|
class UserCleanup(base.CleanupMixin, context.Context):
|
||||||
"""Context class for user resources cleanup."""
|
"""Context class for user resources cleanup."""
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def validate(cls, config):
|
|
||||||
super(UserCleanup, cls).validate(config)
|
|
||||||
|
|
||||||
missing = set(config)
|
|
||||||
missing -= manager.list_resource_names(admin_required=False)
|
|
||||||
missing = ", ".join(missing)
|
|
||||||
if missing:
|
|
||||||
LOG.info(_("Couldn't find cleanup resource managers: %s")
|
|
||||||
% missing)
|
|
||||||
raise base.NoSuchCleanupResources(missing)
|
|
||||||
|
|
||||||
@logging.log_task_wrapper(LOG.info, _("user resources cleanup"))
|
@logging.log_task_wrapper(LOG.info, _("user resources cleanup"))
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
manager.cleanup(
|
manager.cleanup(
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
import copy
|
import copy
|
||||||
|
|
||||||
import ddt
|
import ddt
|
||||||
import jsonschema
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from rally.plugins.openstack.context.cinder import volumes
|
from rally.plugins.openstack.context.cinder import volumes
|
||||||
|
from rally.task import context
|
||||||
from tests.unit import test
|
from tests.unit import test
|
||||||
|
|
||||||
CTX = "rally.plugins.openstack.context"
|
CTX = "rally.plugins.openstack.context"
|
||||||
@ -50,16 +50,15 @@ class VolumeGeneratorTestCase(test.ScenarioTestCase):
|
|||||||
@ddt.data({"config": {"size": 1, "volumes_per_tenant": 5}},
|
@ddt.data({"config": {"size": 1, "volumes_per_tenant": 5}},
|
||||||
{"config": {"size": 1, "type": None, "volumes_per_tenant": 5}},
|
{"config": {"size": 1, "type": None, "volumes_per_tenant": 5}},
|
||||||
{"config": {"size": 1, "type": -1, "volumes_per_tenant": 5},
|
{"config": {"size": 1, "type": -1, "volumes_per_tenant": 5},
|
||||||
"validation_raises": jsonschema.exceptions.ValidationError})
|
"valid": False})
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
@mock.patch("%s.block.BlockStorage" % SERVICE)
|
@mock.patch("%s.block.BlockStorage" % SERVICE)
|
||||||
def test_setup(self, mock_block_storage, config,
|
def test_setup(self, mock_block_storage, config, valid=True):
|
||||||
validation_raises=None):
|
results = context.Context.validate("volumes", None, None, config)
|
||||||
try:
|
if valid:
|
||||||
volumes.VolumeGenerator.validate(config)
|
self.assertEqual([], results)
|
||||||
except Exception as e:
|
else:
|
||||||
if not isinstance(e, validation_raises):
|
self.assertEqual(1, len(results))
|
||||||
raise
|
|
||||||
|
|
||||||
from rally.plugins.openstack.services.storage import block
|
from rally.plugins.openstack.services.storage import block
|
||||||
created_volume = block.Volume(id="uuid", size=config["size"],
|
created_volume = block.Volume(id="uuid", size=config["size"],
|
||||||
|
@ -13,44 +13,41 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import jsonschema
|
import ddt
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from rally.common import utils
|
from rally.common import utils
|
||||||
from rally.plugins.openstack.context.cleanup import admin
|
from rally.plugins.openstack.context.cleanup import admin
|
||||||
from rally.plugins.openstack.context.cleanup import base
|
|
||||||
from rally.plugins.openstack import scenario
|
from rally.plugins.openstack import scenario
|
||||||
|
from rally.task import context
|
||||||
from tests.unit import test
|
from tests.unit import test
|
||||||
|
|
||||||
|
|
||||||
BASE = "rally.plugins.openstack.context.cleanup.admin"
|
ADMIN = "rally.plugins.openstack.context.cleanup.admin"
|
||||||
|
BASE = "rally.plugins.openstack.context.cleanup.base"
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.ddt
|
||||||
class AdminCleanupTestCase(test.TestCase):
|
class AdminCleanupTestCase(test.TestCase):
|
||||||
|
|
||||||
@mock.patch("%s.manager" % BASE)
|
@mock.patch("%s.manager" % BASE)
|
||||||
def test_validate(self, mock_manager):
|
@ddt.data((["a", "b"], True),
|
||||||
mock_manager.list_resource_names.return_value = set(["a", "b", "c"])
|
(["a", "e"], False),
|
||||||
admin.AdminCleanup.validate(["a"])
|
(3, False))
|
||||||
mock_manager.list_resource_names.assert_called_once_with(
|
@ddt.unpack
|
||||||
admin_required=True)
|
def test_validate(self, config, valid, mock_manager):
|
||||||
|
mock_manager.list_resource_names.return_value = {"a", "b", "c"}
|
||||||
@mock.patch("%s.manager" % BASE)
|
results = context.Context.validate(
|
||||||
def test_validate_no_such_cleanup(self, mock_manager):
|
"admin_cleanup", None, None, config, allow_hidden=True)
|
||||||
mock_manager.list_resource_names.return_value = set(["a", "b", "c"])
|
if valid:
|
||||||
self.assertRaises(base.NoSuchCleanupResources,
|
self.assertEqual([], results)
|
||||||
admin.AdminCleanup.validate, ["a", "d"])
|
else:
|
||||||
mock_manager.list_resource_names.assert_called_once_with(
|
self.assertGreater(len(results), 0)
|
||||||
admin_required=True)
|
|
||||||
|
|
||||||
def test_validate_invalid_config(self):
|
|
||||||
self.assertRaises(jsonschema.ValidationError,
|
|
||||||
admin.AdminCleanup.validate, {})
|
|
||||||
|
|
||||||
@mock.patch("rally.common.plugin.discover.itersubclasses")
|
@mock.patch("rally.common.plugin.discover.itersubclasses")
|
||||||
@mock.patch("%s.manager.find_resource_managers" % BASE,
|
@mock.patch("%s.manager.find_resource_managers" % ADMIN,
|
||||||
return_value=[mock.MagicMock(), mock.MagicMock()])
|
return_value=[mock.MagicMock(), mock.MagicMock()])
|
||||||
@mock.patch("%s.manager.SeekAndDestroy" % BASE)
|
@mock.patch("%s.manager.SeekAndDestroy" % ADMIN)
|
||||||
def test_cleanup(self, mock_seek_and_destroy, mock_find_resource_managers,
|
def test_cleanup(self, mock_seek_and_destroy, mock_find_resource_managers,
|
||||||
mock_itersubclasses):
|
mock_itersubclasses):
|
||||||
class ResourceClass(utils.RandomNameGeneratorMixin):
|
class ResourceClass(utils.RandomNameGeneratorMixin):
|
||||||
@ -89,9 +86,9 @@ class AdminCleanupTestCase(test.TestCase):
|
|||||||
])
|
])
|
||||||
|
|
||||||
@mock.patch("rally.common.plugin.discover.itersubclasses")
|
@mock.patch("rally.common.plugin.discover.itersubclasses")
|
||||||
@mock.patch("%s.manager.find_resource_managers" % BASE,
|
@mock.patch("%s.manager.find_resource_managers" % ADMIN,
|
||||||
return_value=[mock.MagicMock(), mock.MagicMock()])
|
return_value=[mock.MagicMock(), mock.MagicMock()])
|
||||||
@mock.patch("%s.manager.SeekAndDestroy" % BASE)
|
@mock.patch("%s.manager.SeekAndDestroy" % ADMIN)
|
||||||
def test_cleanup_admin_with_api_versions(self,
|
def test_cleanup_admin_with_api_versions(self,
|
||||||
mock_seek_and_destroy,
|
mock_seek_and_destroy,
|
||||||
mock_find_resource_managers,
|
mock_find_resource_managers,
|
||||||
|
@ -13,44 +13,41 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import jsonschema
|
import ddt
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from rally.common import utils
|
from rally.common import utils
|
||||||
from rally.plugins.openstack.context.cleanup import base
|
|
||||||
from rally.plugins.openstack.context.cleanup import user
|
from rally.plugins.openstack.context.cleanup import user
|
||||||
from rally.plugins.openstack import scenario
|
from rally.plugins.openstack import scenario
|
||||||
|
from rally.task import context
|
||||||
from tests.unit import test
|
from tests.unit import test
|
||||||
|
|
||||||
|
|
||||||
BASE = "rally.plugins.openstack.context.cleanup.user"
|
ADMIN = "rally.plugins.openstack.context.cleanup.admin"
|
||||||
|
BASE = "rally.plugins.openstack.context.cleanup.base"
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.ddt
|
||||||
class UserCleanupTestCase(test.TestCase):
|
class UserCleanupTestCase(test.TestCase):
|
||||||
|
|
||||||
@mock.patch("%s.manager" % BASE)
|
@mock.patch("%s.manager" % BASE)
|
||||||
def test_validate(self, mock_manager):
|
@ddt.data((["a", "b"], True),
|
||||||
mock_manager.list_resource_names.return_value = set(["a", "b", "c"])
|
(["a", "e"], False),
|
||||||
user.UserCleanup.validate(["a"])
|
(3, False))
|
||||||
mock_manager.list_resource_names.assert_called_once_with(
|
@ddt.unpack
|
||||||
admin_required=False)
|
def test_validate(self, config, valid, mock_manager):
|
||||||
|
mock_manager.list_resource_names.return_value = {"a", "b", "c"}
|
||||||
@mock.patch("%s.manager" % BASE)
|
results = context.Context.validate(
|
||||||
def test_validate_no_such_cleanup(self, mock_manager):
|
"cleanup", None, None, config, allow_hidden=True)
|
||||||
mock_manager.list_resource_names.return_value = set(["a", "b", "c"])
|
if valid:
|
||||||
self.assertRaises(base.NoSuchCleanupResources,
|
self.assertEqual([], results)
|
||||||
user.UserCleanup.validate, ["a", "b", "d"])
|
else:
|
||||||
mock_manager.list_resource_names.assert_called_once_with(
|
self.assertGreater(len(results), 0)
|
||||||
admin_required=False)
|
|
||||||
|
|
||||||
def test_validate_invalid_config(self):
|
|
||||||
self.assertRaises(jsonschema.ValidationError,
|
|
||||||
user.UserCleanup.validate, {})
|
|
||||||
|
|
||||||
@mock.patch("rally.common.plugin.discover.itersubclasses")
|
@mock.patch("rally.common.plugin.discover.itersubclasses")
|
||||||
@mock.patch("%s.manager.find_resource_managers" % BASE,
|
@mock.patch("%s.manager.find_resource_managers" % ADMIN,
|
||||||
return_value=[mock.MagicMock(), mock.MagicMock()])
|
return_value=[mock.MagicMock(), mock.MagicMock()])
|
||||||
@mock.patch("%s.manager.SeekAndDestroy" % BASE)
|
@mock.patch("%s.manager.SeekAndDestroy" % ADMIN)
|
||||||
def test_cleanup(self, mock_seek_and_destroy, mock_find_resource_managers,
|
def test_cleanup(self, mock_seek_and_destroy, mock_find_resource_managers,
|
||||||
mock_itersubclasses):
|
mock_itersubclasses):
|
||||||
|
|
||||||
@ -83,9 +80,9 @@ class UserCleanupTestCase(test.TestCase):
|
|||||||
])
|
])
|
||||||
|
|
||||||
@mock.patch("rally.common.plugin.discover.itersubclasses")
|
@mock.patch("rally.common.plugin.discover.itersubclasses")
|
||||||
@mock.patch("%s.manager.find_resource_managers" % BASE,
|
@mock.patch("%s.manager.find_resource_managers" % ADMIN,
|
||||||
return_value=[mock.MagicMock(), mock.MagicMock()])
|
return_value=[mock.MagicMock(), mock.MagicMock()])
|
||||||
@mock.patch("%s.manager.SeekAndDestroy" % BASE)
|
@mock.patch("%s.manager.SeekAndDestroy" % ADMIN)
|
||||||
def test_cleanup_user_with_api_versions(
|
def test_cleanup_user_with_api_versions(
|
||||||
self,
|
self,
|
||||||
mock_seek_and_destroy,
|
mock_seek_and_destroy,
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
import copy
|
import copy
|
||||||
|
|
||||||
import ddt
|
import ddt
|
||||||
import jsonschema
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from rally.plugins.openstack.context.glance import images
|
from rally.plugins.openstack.context.glance import images
|
||||||
@ -59,16 +58,6 @@ class ImageGeneratorTestCase(test.ScenarioTestCase):
|
|||||||
tenants[str(id_)] = {"name": str(id_)}
|
tenants[str(id_)] = {"name": str(id_)}
|
||||||
return tenants
|
return tenants
|
||||||
|
|
||||||
def test_init_validation(self):
|
|
||||||
self.context["config"] = {
|
|
||||||
"images": {
|
|
||||||
"image_url": "mock_url"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.assertRaises(jsonschema.ValidationError,
|
|
||||||
images.ImageGenerator.validate, self.context)
|
|
||||||
|
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
{},
|
{},
|
||||||
{"min_disk": 1, "min_ram": 2},
|
{"min_disk": 1, "min_ram": 2},
|
||||||
|
@ -14,14 +14,13 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import random
|
|
||||||
|
|
||||||
import ddt
|
import ddt
|
||||||
import jsonschema
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from rally.common import logging
|
from rally.common import logging
|
||||||
from rally.plugins.openstack.context.quotas import quotas
|
from rally.plugins.openstack.context.quotas import quotas
|
||||||
|
from rally.task import context
|
||||||
from tests.unit import test
|
from tests.unit import test
|
||||||
|
|
||||||
QUOTAS_PATH = "rally.plugins.openstack.context.quotas"
|
QUOTAS_PATH = "rally.plugins.openstack.context.quotas"
|
||||||
@ -43,98 +42,56 @@ class QuotasTestCase(test.TestCase):
|
|||||||
"task": mock.MagicMock()
|
"task": mock.MagicMock()
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_quotas_schemas(self):
|
@ddt.data(("cinder", "backup_gigabytes"),
|
||||||
ctx = copy.deepcopy(self.context)
|
("cinder", "backups"),
|
||||||
ctx["config"]["quotas"] = {
|
("cinder", "gigabytes"),
|
||||||
"cinder": {
|
("cinder", "snapshots"),
|
||||||
"volumes": self.unlimited,
|
("cinder", "volumes"),
|
||||||
"snapshots": self.unlimited,
|
("manila", "gigabytes"),
|
||||||
"gigabytes": self.unlimited
|
("manila", "share_networks"),
|
||||||
},
|
("manila", "shares"),
|
||||||
"nova": {
|
("manila", "snapshot_gigabytes"),
|
||||||
"instances": self.unlimited,
|
("manila", "snapshots"),
|
||||||
"cores": self.unlimited,
|
("neutron", "floatingip"),
|
||||||
"ram": self.unlimited,
|
("neutron", "health_monitor"),
|
||||||
"floating_ips": self.unlimited,
|
("neutron", "network"),
|
||||||
"fixed_ips": self.unlimited,
|
("neutron", "pool"),
|
||||||
"metadata_items": self.unlimited,
|
("neutron", "port"),
|
||||||
"injected_files": self.unlimited,
|
("neutron", "router"),
|
||||||
"injected_file_content_bytes": self.unlimited,
|
("neutron", "security_group"),
|
||||||
"injected_file_path_bytes": self.unlimited,
|
("neutron", "security_group_rule"),
|
||||||
"key_pairs": self.unlimited,
|
("neutron", "subnet"),
|
||||||
"security_groups": self.unlimited,
|
("neutron", "vip"),
|
||||||
"security_group_rules": self.unlimited
|
("nova", "cores"),
|
||||||
},
|
("nova", "fixed_ips"),
|
||||||
"neutron": {
|
("nova", "floating_ips"),
|
||||||
"network": self.unlimited,
|
("nova", "injected_file_content_bytes"),
|
||||||
"subnet": self.unlimited,
|
("nova", "injected_file_path_bytes"),
|
||||||
"port": self.unlimited,
|
("nova", "injected_files"),
|
||||||
"router": self.unlimited,
|
("nova", "instances"),
|
||||||
"floatingip": self.unlimited,
|
("nova", "key_pairs"),
|
||||||
"security_group": self.unlimited,
|
("nova", "metadata_items"),
|
||||||
"security_group_rule": self.unlimited
|
("nova", "ram"),
|
||||||
}
|
("nova", "security_group_rules"),
|
||||||
}
|
("nova", "security_groups"),
|
||||||
for service in ctx["config"]["quotas"]:
|
("nova", "server_group_members"),
|
||||||
for key in ctx["config"]["quotas"][service]:
|
("nova", "server_groups"))
|
||||||
# Test invalid values
|
@ddt.unpack
|
||||||
ctx["config"]["quotas"][service][key] = self.unlimited - 1
|
def test_validate(self, group, parameter):
|
||||||
try:
|
configs = [
|
||||||
quotas.Quotas.validate(ctx["config"]["quotas"])
|
({group: {parameter: self.unlimited}}, True),
|
||||||
except jsonschema.ValidationError:
|
({group: {parameter: 0}}, True),
|
||||||
pass
|
({group: {parameter: 10000}}, True),
|
||||||
else:
|
({group: {parameter: 2.5}}, False),
|
||||||
self.fail("Invalid value %s must raise a validation error"
|
({group: {parameter: "-1"}}, False),
|
||||||
% ctx["config"]["quotas"][service][key])
|
({group: {parameter: -2}}, False),
|
||||||
|
]
|
||||||
ctx["config"]["quotas"][service][key] = 2.5
|
for config, valid in configs:
|
||||||
try:
|
results = context.Context.validate("quotas", None, None, config)
|
||||||
quotas.Quotas.validate(ctx["config"]["quotas"])
|
if valid:
|
||||||
except jsonschema.ValidationError:
|
self.assertEqual([], results)
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.fail("Invalid value %s must raise a validation error"
|
|
||||||
% ctx["config"]["quotas"][service][key])
|
|
||||||
|
|
||||||
ctx["config"]["quotas"][service][key] = "-1"
|
|
||||||
try:
|
|
||||||
quotas.Quotas.validate(ctx["config"]["quotas"])
|
|
||||||
except jsonschema.ValidationError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.fail("Invalid value %s must raise a validation error"
|
|
||||||
% ctx["config"]["quotas"][service][key])
|
|
||||||
|
|
||||||
# Test valid values
|
|
||||||
ctx["config"]["quotas"][service][key] = random.randint(0,
|
|
||||||
1000000)
|
|
||||||
try:
|
|
||||||
quotas.Quotas.validate(ctx["config"]["quotas"])
|
|
||||||
except jsonschema.ValidationError:
|
|
||||||
self.fail("Positive integers are valid quota values")
|
|
||||||
|
|
||||||
ctx["config"]["quotas"][service][key] = self.unlimited
|
|
||||||
try:
|
|
||||||
quotas.Quotas.validate(ctx["config"]["quotas"])
|
|
||||||
except jsonschema.ValidationError:
|
|
||||||
self.fail("%d is a valid quota value" % self.unlimited)
|
|
||||||
|
|
||||||
# Test additional keys are refused
|
|
||||||
ctx["config"]["quotas"][service]["additional"] = self.unlimited
|
|
||||||
try:
|
|
||||||
quotas.Quotas.validate(ctx["config"]["quotas"])
|
|
||||||
except jsonschema.ValidationError:
|
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
self.fail("Additional keys must raise a validation error")
|
self.assertGreater(len(results), 0)
|
||||||
del ctx["config"]["quotas"][service]["additional"]
|
|
||||||
|
|
||||||
# Test valid keys are optional
|
|
||||||
ctx["config"]["quotas"][service] = {}
|
|
||||||
try:
|
|
||||||
quotas.Quotas.validate(ctx["config"]["quotas"])
|
|
||||||
except jsonschema.ValidationError:
|
|
||||||
self.fail("Valid quota keys are optional")
|
|
||||||
|
|
||||||
@mock.patch("%s.quotas.osclients.Clients" % QUOTAS_PATH)
|
@mock.patch("%s.quotas.osclients.Clients" % QUOTAS_PATH)
|
||||||
@mock.patch("%s.cinder_quotas.CinderQuotas" % QUOTAS_PATH)
|
@mock.patch("%s.cinder_quotas.CinderQuotas" % QUOTAS_PATH)
|
||||||
|
@ -10,15 +10,17 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import jsonschema
|
import ddt
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from rally.common import utils
|
from rally.common import utils
|
||||||
from rally import exceptions
|
from rally import exceptions
|
||||||
from rally.plugins.openstack.context import api_versions
|
from rally.plugins.openstack.context import api_versions
|
||||||
|
from rally.task import context
|
||||||
from tests.unit import test
|
from tests.unit import test
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.ddt
|
||||||
class OpenStackServicesTestCase(test.TestCase):
|
class OpenStackServicesTestCase(test.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -30,82 +32,51 @@ class OpenStackServicesTestCase(test.TestCase):
|
|||||||
self.service_catalog.get_endpoints.return_value = []
|
self.service_catalog.get_endpoints.return_value = []
|
||||||
self.mock_kc.services.list.return_value = []
|
self.mock_kc.services.list.return_value = []
|
||||||
|
|
||||||
def test_validate_correct_config(self):
|
@ddt.data(({"nova": {"service_type": "compute", "version": 2},
|
||||||
api_versions.OpenStackAPIVersions.validate({
|
"cinder": {"service_name": "cinderv2", "version": 2},
|
||||||
"nova": {"service_type": "compute", "version": 2},
|
"neutron": {"service_type": "network"},
|
||||||
"cinder": {"service_name": "cinderv2", "version": 2},
|
"glance": {"service_name": "glance"},
|
||||||
"neutron": {"service_type": "network"},
|
"heat": {"version": 1}}, True),
|
||||||
"glance": {"service_name": "glance"},
|
({"nova": {"service_type": "compute",
|
||||||
"heat": {"version": 1}
|
"service_name": "nova"}}, False),
|
||||||
})
|
({"keystone": {"service_type": "foo"}}, False),
|
||||||
|
({"nova": {"version": "foo"}}, False),
|
||||||
def test_validate_wrong_configs(self):
|
({}, False))
|
||||||
# Non-existing clients should be caught
|
@ddt.unpack
|
||||||
self.assertRaises(
|
def test_validate(self, config, valid):
|
||||||
exceptions.PluginNotFound,
|
results = context.Context.validate("api_versions", None, None, config)
|
||||||
api_versions.OpenStackAPIVersions.validate,
|
if valid:
|
||||||
{"invalid": {"service_type": "some_type"}})
|
self.assertEqual([], results)
|
||||||
|
else:
|
||||||
# Additional properties should be restricted
|
self.assertGreater(len(results), 0)
|
||||||
self.assertRaises(
|
|
||||||
jsonschema.ValidationError,
|
|
||||||
api_versions.OpenStackAPIVersions.validate,
|
|
||||||
{"nova": {"some_key": "some_value"}})
|
|
||||||
|
|
||||||
# Setting service_type is allowed only
|
|
||||||
# for those clients, which support it
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.ValidationError,
|
|
||||||
api_versions.OpenStackAPIVersions.validate,
|
|
||||||
{"keystone": {"service_type": "identity"}})
|
|
||||||
|
|
||||||
# Setting service_name is allowed only
|
|
||||||
# for those clients, which support it
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.ValidationError,
|
|
||||||
api_versions.OpenStackAPIVersions.validate,
|
|
||||||
{"keystone": {"service_name": "keystone"}})
|
|
||||||
|
|
||||||
# Setting version is allowed only
|
|
||||||
# for those clients, which support it
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.ValidationError,
|
|
||||||
api_versions.OpenStackAPIVersions.validate,
|
|
||||||
{"keystone": {"version": 1}})
|
|
||||||
|
|
||||||
# Unsupported version should be caught
|
|
||||||
self.assertRaises(
|
|
||||||
exceptions.ValidationError,
|
|
||||||
api_versions.OpenStackAPIVersions.validate,
|
|
||||||
{"nova": {"version": 666}})
|
|
||||||
|
|
||||||
def test_setup_with_wrong_service_name(self):
|
def test_setup_with_wrong_service_name(self):
|
||||||
context = {
|
context_obj = {
|
||||||
"config": {api_versions.OpenStackAPIVersions.get_name(): {
|
"config": {api_versions.OpenStackAPIVersions.get_name(): {
|
||||||
"nova": {"service_name": "service_name"}}},
|
"nova": {"service_name": "service_name"}}},
|
||||||
"admin": {"credential": mock.MagicMock()},
|
"admin": {"credential": mock.MagicMock()},
|
||||||
"users": [{"credential": mock.MagicMock()}]}
|
"users": [{"credential": mock.MagicMock()}]}
|
||||||
ctx = api_versions.OpenStackAPIVersions(context)
|
ctx = api_versions.OpenStackAPIVersions(context_obj)
|
||||||
self.assertRaises(exceptions.ValidationError, ctx.setup)
|
self.assertRaises(exceptions.ValidationError, ctx.setup)
|
||||||
self.service_catalog.get_endpoints.assert_called_once_with()
|
self.service_catalog.get_endpoints.assert_called_once_with()
|
||||||
self.mock_kc.services.list.assert_called_once_with()
|
self.mock_kc.services.list.assert_called_once_with()
|
||||||
|
|
||||||
def test_setup_with_wrong_service_name_and_without_admin(self):
|
def test_setup_with_wrong_service_name_and_without_admin(self):
|
||||||
context = {
|
context_obj = {
|
||||||
"config": {api_versions.OpenStackAPIVersions.get_name(): {
|
"config": {api_versions.OpenStackAPIVersions.get_name(): {
|
||||||
"nova": {"service_name": "service_name"}}},
|
"nova": {"service_name": "service_name"}}},
|
||||||
"users": [{"credential": mock.MagicMock()}]}
|
"users": [{"credential": mock.MagicMock()}]}
|
||||||
ctx = api_versions.OpenStackAPIVersions(context)
|
ctx = api_versions.OpenStackAPIVersions(context_obj)
|
||||||
self.assertRaises(exceptions.BenchmarkSetupFailure, ctx.setup)
|
self.assertRaises(exceptions.BenchmarkSetupFailure, ctx.setup)
|
||||||
self.service_catalog.get_endpoints.assert_called_once_with()
|
self.service_catalog.get_endpoints.assert_called_once_with()
|
||||||
self.assertFalse(self.mock_kc.services.list.called)
|
self.assertFalse(self.mock_kc.services.list.called)
|
||||||
|
|
||||||
def test_setup_with_wrong_service_type(self):
|
def test_setup_with_wrong_service_type(self):
|
||||||
context = {
|
context_obj = {
|
||||||
"config": {api_versions.OpenStackAPIVersions.get_name(): {
|
"config": {api_versions.OpenStackAPIVersions.get_name(): {
|
||||||
"nova": {"service_type": "service_type"}}},
|
"nova": {"service_type": "service_type"}}},
|
||||||
"users": [{"credential": mock.MagicMock()}]}
|
"users": [{"credential": mock.MagicMock()}]}
|
||||||
ctx = api_versions.OpenStackAPIVersions(context)
|
ctx = api_versions.OpenStackAPIVersions(context_obj)
|
||||||
self.assertRaises(exceptions.ValidationError, ctx.setup)
|
self.assertRaises(exceptions.ValidationError, ctx.setup)
|
||||||
self.service_catalog.get_endpoints.assert_called_once_with()
|
self.service_catalog.get_endpoints.assert_called_once_with()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user