pass id or name of resources in scenario config

* added types.set(...) decorator to apply transform to scenario args
* abstract ResourceType implements generic transform logic
* support use of flavor name through FlavorResourceType
* support use of image name through ImageResourceType
* transform is applied during validation and during preprocessing
  which follows validation
* modified all sample scenarios to use flavor-by-name and
  image-by-name
* passing resource as simple key-value pair will no longer work,
  and instead resource must be object containig id, name, or regex
* unit tests for image and flavor transformations

Change-Id: If35959ece8f247b5376f5ff91ae3d3e008a84447
This commit is contained in:
Marco Morais 2014-04-08 17:36:40 +00:00
parent de36c14184
commit 4fe39730dc
30 changed files with 511 additions and 158 deletions

View File

@ -5,7 +5,9 @@
"image_location": "http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-disk.img",
"container_format": "bare",
"disk_format": "qcow2",
"flavor_id": 42,
"flavor": {
"name": "m1.nano"
},
"number_instances": 2
},
"runner": {

View File

@ -5,7 +5,8 @@
image_location: "http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-disk.img"
container_format: "bare"
disk_format: "qcow2"
flavor_id: 42
flavor:
name: "m1.nano"
number_instances: 2
runner:
type: "constant"

View File

@ -2,8 +2,12 @@
"NovaServers.boot_and_delete_server": [
{
"args": {
"flavor_id": 1,
"image_id": "73257560-c59b-4275-a1ec-ab140e5b9979"
"flavor": {
"name": "m1.nano"
},
"image": {
"name": "cirros-0.3.1-x86_64-uec"
}
},
"runner": {
"type": "constant",

View File

@ -2,8 +2,10 @@
NovaServers.boot_and_delete_server:
-
args:
flavor_id: 1
image_id: "73257560-c59b-4275-a1ec-ab140e5b9979"
flavor:
name: "m1.nano"
image:
name: "cirros-0.3.1-x86_64-uec"
runner:
type: "constant"
times: 10

View File

@ -2,8 +2,12 @@
"NovaServers.boot_and_list_server": [
{
"args": {
"flavor_id": 1,
"image_id": "ccc8fe37-9289-437b-a88c-fc4678656c75",
"flavor": {
"name": "m1.nano"
},
"image": {
"name": "cirros-0.3.1-x86_64-uec"
},
"detailed" : True
},
"runner": {

View File

@ -2,8 +2,10 @@
NovaServers.boot_and_list_server:
-
args:
flavor_id: 1
image_id: "ccc8fe37-9289-437b-a88c-fc4678656c75"
flavor:
name: "m1.nano"
image:
name: "cirros-0.3.1-x86_64-uec"
detailed: True
runner:
type: "constant"

View File

@ -1,27 +1,31 @@
{
"NovaServers.boot_and_bounce_server": [
{
"args": {
"flavor_id": 1,
"image_id": "3fa4482f-677a-4488-adaf-c48befac5e5a",
"actions": [
{"hard_reboot": 1},
{"soft_reboot": 1},
{"stop_start": 1},
{"rescue_unrescue": 1}
]
},
"runner": {
"type": "constant",
"times": 10,
"concurrency": 2
},
"context": {
"users": {
"tenants": 3,
"users_per_tenant": 2
}
}
}
]
}
{
"NovaServers.boot_and_bounce_server": [
{
"args": {
"flavor": {
"name": "m1.nano"
},
"image": {
"name": "cirros-0.3.1-x86_64-uec"
},
"actions": [
{"hard_reboot": 1},
{"soft_reboot": 1},
{"stop_start": 1},
{"rescue_unrescue": 1}
]
},
"runner": {
"type": "constant",
"times": 10,
"concurrency": 2
},
"context": {
"users": {
"tenants": 3,
"users_per_tenant": 2
}
}
}
]
}

View File

@ -1,23 +1,25 @@
---
NovaServers.boot_and_bounce_server:
-
args:
flavor_id: 1
image_id: "3fa4482f-677a-4488-adaf-c48befac5e5a"
actions:
-
hard_reboot: 1
-
soft_reboot: 1
-
stop_start: 1
-
rescue_unrescue: 1
runner:
type: "constant"
times: 10
concurrency: 2
context:
users:
tenants: 3
users_per_tenant: 2
---
NovaServers.boot_and_bounce_server:
-
args:
flavor:
name: "m1.nano"
image:
name: "cirros-0.3.1-x86_64-uec"
actions:
-
hard_reboot: 1
-
soft_reboot: 1
-
stop_start: 1
-
rescue_unrescue: 1
runner:
type: "constant"
times: 10
concurrency: 2
context:
users:
tenants: 3
users_per_tenant: 2

View File

@ -2,8 +2,12 @@
"NovaServers.boot_server_from_volume_and_delete": [
{
"args": {
"flavor_id": 1,
"image_id": "73257560-c59b-4275-a1ec-ab140e5b9979",
"flavor": {
"name": "m1.nano"
},
"image": {
"name": "cirros-0.3.1-x86_64-uec"
},
"volume_size": 10
},
"runner": {

View File

@ -2,8 +2,10 @@
NovaServers.boot_server_from_volume_and_delete:
-
args:
flavor_id: 1
image_id: "73257560-c59b-4275-a1ec-ab140e5b9979"
flavor:
name: "m1.nano"
image:
name: "cirros-0.3.1-x86_64-uec"
volume_size: 10
runner:
type: "constant"

View File

@ -2,8 +2,12 @@
"NovaServers.boot_server_from_volume": [
{
"args": {
"flavor_id": 1,
"image_id": "73257560-c59b-4275-a1ec-ab140e5b9979",
"flavor": {
"name": "m1.nano"
},
"image": {
"name": "cirros-0.3.1-x86_64-uec"
},
"volume_size": 10
},
"runner": {

View File

@ -2,8 +2,10 @@
NovaServers.boot_server_from_volume:
-
args:
flavor_id: 1
image_id: "73257560-c59b-4275-a1ec-ab140e5b9979"
flavor:
name: "m1.nano"
image:
name: "cirros-0.3.1-x86_64-uec"
volume_size: 10
runner:
type: "constant"

View File

@ -2,8 +2,12 @@
"NovaServers.snapshot_server": [
{
"args": {
"flavor_id": 1,
"image_id": "73257560-c59b-4275-a1ec-ab140e5b9979"
"flavor": {
"name": "m1.nano"
},
"image": {
"name": "cirros-0.3.1-x86_64-uec"
}
},
"runner": {
"type": "constant",

View File

@ -2,8 +2,10 @@
NovaServers.snapshot_server:
-
args:
flavor_id: 1
image_id: "73257560-c59b-4275-a1ec-ab140e5b9979"
flavor:
name: "m1.nano"
image:
name: "cirros-0.3.1-x86_64-uec"
runner:
type: "constant"
times: 10

View File

@ -2,8 +2,12 @@
"NovaServers.boot_server": [
{
"args": {
"flavor_id": 1,
"image_id": "73257560-c59b-4275-a1ec-ab140e5b9979"
"flavor": {
"name": "m1.nano"
},
"image": {
"name": "cirros-0.3.1-x86_64-uec"
}
},
"runner": {
"type": "constant",

View File

@ -2,8 +2,10 @@
NovaServers.boot_server:
-
args:
flavor_id: 1
image_id: "73257560-c59b-4275-a1ec-ab140e5b9979"
flavor:
name: "m1.nano"
image:
name: "cirros-0.3.1-x86_64-uec"
runner:
type: "constant"
times: 10

View File

@ -2,8 +2,12 @@
"VMTasks.boot_runcommand_delete": [
{
"args": {
"flavor_id": "100",
"image_id": "8ec0b1bc-bb9c-4176-9d06-e23016091a51",
"flavor": {
"name": "m1.nano"
},
"image": {
"name": "cirros-0.3.1-x86_64-uec"
},
"script": "doc/samples/support/instance_dd_test.sh",
"interpreter": "/bin/sh",
"username": "cirros"

View File

@ -2,8 +2,10 @@
VMTasks.boot_runcommand_delete:
-
args:
flavor_id: "1"
image_id: "231bdb41-f959-4496-a272-3ef9f6d9f790"
flavor:
name: "m1.nano"
image:
name: "cirros-0.3.1-x86_64-uec"
script: "doc/samples/support/instance_dd_test.sh"
interpreter: "/bin/sh"
username: "ubuntu"

View File

@ -113,8 +113,8 @@ class BenchmarkEngine(object):
reason=six.text_type(e)
)
def _validate_config_sematic_helper(self, admin, user, name, pos,
task, kwargs):
def _validate_config_semantic_helper(self, admin, user, name, pos,
task, kwargs):
args = {} if not kwargs else kwargs.get("args", {})
try:
base_scenario.Scenario.validate(name, args, admin=admin,
@ -139,9 +139,9 @@ class BenchmarkEngine(object):
for name, values in config.iteritems():
for pos, kwargs in enumerate(values):
self._validate_config_sematic_helper(admin, user, name,
pos, self.task,
kwargs)
self._validate_config_semantic_helper(admin, user, name,
pos, self.task,
kwargs)
@rutils.log_task_wrapper(LOG.info, _("Task validation."))
def validate(self):

View File

@ -207,6 +207,7 @@ class ScenarioRunner(object):
"config": scenario_context
}
args = cls.preprocess(method_name, context_obj, args)
results = base_ctx.ContextManager.run(context_obj, self._run_scenario,
cls, method_name, context_obj,
args)

View File

@ -20,6 +20,7 @@ import time
from rally import consts
from rally import exceptions
from rally import osclients
from rally import utils
@ -138,6 +139,22 @@ class Scenario(object):
method = getattr(cls, method_name)
return getattr(method, attr_name, default)
@classmethod
def preprocess(cls, method_name, context, args):
"""Run preprocessor on scenario arguments."""
preprocessors = Scenario.meta(cls, method_name=method_name,
attr_name="preprocessors", default={})
clients = osclients.Clients(context["admin"]["endpoint"])
for src, preprocessor in preprocessors.items():
resource_config = args.get(src)
if resource_config:
args[src] = preprocessor.transform(
clients=clients,
resource_config=resource_config)
return args
def context(self):
"""Returns the context of the current benchmark scenario."""
return self._context

View File

@ -16,6 +16,7 @@
from rally.benchmark.scenarios import base
from rally.benchmark.scenarios.glance import utils
from rally.benchmark.scenarios.nova import utils as nova_utils
from rally.benchmark import types as types
from rally.benchmark import validation
@ -57,11 +58,12 @@ class GlanceImages(utils.GlanceScenario, nova_utils.NovaScenario):
**kwargs)
self._delete_image(image)
@validation.add_validator(validation.flavor_exists("flavor_id"))
@types.set(flavor=types.FlavorResourceType)
@validation.add_validator(validation.flavor_exists("flavor"))
@base.scenario(context={"cleanup": ["glance", "nova"]})
def create_image_and_boot_instances(self, container_format,
image_location, disk_format,
flavor_id, number_instances,
flavor, number_instances,
**kwargs):
"""Test adds image, boots instance from it and then deletes them."""
image_name = self._generate_random_name()
@ -73,4 +75,4 @@ class GlanceImages(utils.GlanceScenario, nova_utils.NovaScenario):
image_id = image.id
server_name = self._generate_random_name(prefix="rally_novaserver_")
self._boot_servers(server_name, image_id,
flavor_id, number_instances, **kwargs)
flavor, number_instances, **kwargs)

View File

@ -20,6 +20,7 @@ from rally.benchmark.scenarios import base
from rally.benchmark.scenarios.cinder import utils as cinder_utils
from rally.benchmark.scenarios.nova import utils
from rally.benchmark.scenarios import utils as scenario_utils
from rally.benchmark import types as types
from rally.benchmark import validation as valid
from rally import exceptions as rally_exceptions
from rally.openstack.common.gettextutils import _ # noqa
@ -38,9 +39,11 @@ class NovaServers(utils.NovaScenario,
def __init__(self, *args, **kwargs):
super(NovaServers, self).__init__(*args, **kwargs)
@valid.add_validator(valid.image_valid_on_flavor("flavor_id", "image_id"))
@types.set(image=types.ImageResourceType,
flavor=types.FlavorResourceType)
@valid.add_validator(valid.image_valid_on_flavor("flavor", "image"))
@base.scenario(context={"cleanup": ["nova"]})
def boot_and_list_server(self, image_id, flavor_id,
def boot_and_list_server(self, image, flavor,
detailed=True, **kwargs):
"""Tests booting an image and then listing servers.
@ -54,37 +57,43 @@ class NovaServers(utils.NovaScenario,
the number of servers owned by users.
"""
self._boot_server(
self._generate_random_name(), image_id, flavor_id, **kwargs)
self._generate_random_name(), image, flavor, **kwargs)
self._list_servers(detailed)
@valid.add_validator(valid.image_valid_on_flavor("flavor_id", "image_id"))
@types.set(image=types.ImageResourceType,
flavor=types.FlavorResourceType)
@valid.add_validator(valid.image_valid_on_flavor("flavor", "image"))
@base.scenario(context={"cleanup": ["nova"]})
def boot_and_delete_server(self, image_id, flavor_id,
def boot_and_delete_server(self, image, flavor,
min_sleep=0, max_sleep=0, **kwargs):
"""Tests booting and then deleting an image."""
server = self._boot_server(
self._generate_random_name(), image_id, flavor_id, **kwargs)
self._generate_random_name(), image, flavor, **kwargs)
self.sleep_between(min_sleep, max_sleep)
self._delete_server(server)
@valid.add_validator(valid.image_valid_on_flavor("flavor_id", "image_id"))
@types.set(image=types.ImageResourceType,
flavor=types.FlavorResourceType)
@valid.add_validator(valid.image_valid_on_flavor("flavor", "image"))
@base.scenario(context={"cleanup": ["nova", "cinder"]})
def boot_server_from_volume_and_delete(self, image_id, flavor_id,
def boot_server_from_volume_and_delete(self, image, flavor,
volume_size,
min_sleep=0, max_sleep=0, **kwargs):
"""Tests booting from volume and then deleting an image and volume."""
volume = self._create_volume(volume_size, imageRef=image_id)
volume = self._create_volume(volume_size, imageRef=image)
block_device_mapping = {'vda': '%s:::1' % volume.id}
server = self._boot_server(self._generate_random_name(),
image_id, flavor_id,
image, flavor,
block_device_mapping=block_device_mapping,
**kwargs)
self.sleep_between(min_sleep, max_sleep)
self._delete_server(server)
@valid.add_validator(valid.image_valid_on_flavor("flavor_id", "image_id"))
@types.set(image=types.ImageResourceType,
flavor=types.FlavorResourceType)
@valid.add_validator(valid.image_valid_on_flavor("flavor", "image"))
@base.scenario(context={"cleanup": ["nova"]})
def boot_and_bounce_server(self, image_id, flavor_id, **kwargs):
def boot_and_bounce_server(self, image, flavor, **kwargs):
"""Tests booting a server then performing stop/start or hard/soft
reboot a number of times.
"""
@ -97,28 +106,32 @@ class NovaServers(utils.NovaScenario,
"Invalid server actions configuration \'%(actions)s\' due to: "
"%(error)s" % {'actions': str(actions), 'error': str(error)})
server = self._boot_server(self._generate_random_name(),
image_id, flavor_id, **kwargs)
image, flavor, **kwargs)
for action in action_builder.build_actions(actions, server):
action()
self._delete_server(server)
@valid.add_validator(valid.image_valid_on_flavor("flavor_id", "image_id"))
@types.set(image=types.ImageResourceType,
flavor=types.FlavorResourceType)
@valid.add_validator(valid.image_valid_on_flavor("flavor", "image"))
@base.scenario(context={"cleanup": ["nova", "glance"]})
def snapshot_server(self, image_id, flavor_id, **kwargs):
def snapshot_server(self, image, flavor, **kwargs):
"""Tests Nova instance snapshotting."""
server_name = self._generate_random_name()
server = self._boot_server(server_name, image_id, flavor_id, **kwargs)
server = self._boot_server(server_name, image, flavor, **kwargs)
image = self._create_image(server)
self._delete_server(server)
server = self._boot_server(server_name, image.id, flavor_id, **kwargs)
server = self._boot_server(server_name, image.id, flavor, **kwargs)
self._delete_server(server)
self._delete_image(image)
@valid.add_validator(valid.image_valid_on_flavor("flavor_id", "image_id"))
@types.set(image=types.ImageResourceType,
flavor=types.FlavorResourceType)
@valid.add_validator(valid.image_valid_on_flavor("flavor", "image"))
@base.scenario(context={"cleanup": ["nova"]})
def boot_server(self, image_id, flavor_id, **kwargs):
def boot_server(self, image, flavor, **kwargs):
"""Test VM boot - assumed clean-up is done elsewhere."""
if 'nics' not in kwargs:
nets = self.clients("nova").networks.list()
@ -126,11 +139,13 @@ class NovaServers(utils.NovaScenario,
random_nic = random.choice(nets)
kwargs['nics'] = [{'net-id': random_nic.id}]
self._boot_server(
self._generate_random_name(), image_id, flavor_id, **kwargs)
self._generate_random_name(), image, flavor, **kwargs)
@valid.add_validator(valid.image_valid_on_flavor("flavor_id", "image_id"))
@types.set(image=types.ImageResourceType,
flavor=types.FlavorResourceType)
@valid.add_validator(valid.image_valid_on_flavor("flavor", "image"))
@base.scenario(context={"cleanup": ["nova", "cinder"]})
def boot_server_from_volume(self, image_id, flavor_id,
def boot_server_from_volume(self, image, flavor,
volume_size, **kwargs):
"""Test VM boot from volume - assumed clean-up is done elsewhere."""
if 'nics' not in kwargs:
@ -138,10 +153,10 @@ class NovaServers(utils.NovaScenario,
if nets:
random_nic = random.choice(nets)
kwargs['nics'] = [{'net-id': random_nic.id}]
volume = self._create_volume(volume_size, imageRef=image_id)
volume = self._create_volume(volume_size, imageRef=image)
block_device_mapping = {'vda': '%s:::1' % volume.id}
self._boot_server(self._generate_random_name(),
image_id, flavor_id,
image, flavor,
block_device_mapping=block_device_mapping,
**kwargs)

View File

@ -18,6 +18,7 @@ import json
from rally.benchmark.scenarios import base
from rally.benchmark.scenarios.nova import utils as nova_utils
from rally.benchmark.scenarios.vm import utils as vm_utils
from rally.benchmark import types as types
from rally.benchmark import validation as valid
from rally.openstack.common.gettextutils import _ # noqa
from rally.openstack.common import log as logging
@ -31,13 +32,15 @@ class VMTasks(nova_utils.NovaScenario, vm_utils.VMScenario):
def __init__(self, *args, **kwargs):
super(VMTasks, self).__init__(*args, **kwargs)
@valid.add_validator(valid.image_valid_on_flavor("flavor_id", "image_id"))
@types.set(image=types.ImageResourceType,
flavor=types.FlavorResourceType)
@valid.add_validator(valid.image_valid_on_flavor("flavor", "image"))
@valid.add_validator(valid.file_exists("script"))
@valid.add_validator(valid.number("port", minval=1, maxval=65535,
nullable=True, integer_only=True))
@base.scenario(context={"cleanup": ["nova"],
"keypair": {}, "allow_ssh": {}})
def boot_runcommand_delete(self, image_id, flavor_id,
def boot_runcommand_delete(self, image, flavor,
script, interpreter, network='private',
username='ubuntu', ip_version=4,
port=22, **kwargs):
@ -59,7 +62,7 @@ class VMTasks(nova_utils.NovaScenario, vm_utils.VMScenario):
"""
server = self._boot_server(
self._generate_random_name("rally_novaserver_"),
image_id, flavor_id, key_name='rally_ssh_key', **kwargs)
image, flavor, key_name='rally_ssh_key', **kwargs)
code, out, err = self.run_command(server, username, network, port,
ip_version, interpreter, script)

130
rally/benchmark/types.py Normal file
View File

@ -0,0 +1,130 @@
# Copyright (C) 2014 Yahoo! Inc. All Rights Reserved.
# 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 abc
import operator
import re
from rally import exceptions
def set(**kwargs):
"""Decorator to define resource transformation(s) on scenario parameters.
The `kwargs` passed as arguments to the decorator are used to
map a key in the scenario config to the subclass of ResourceType
used to perform a transformation on the value of that key.
"""
def wrapper(func):
func.preprocessors = getattr(func, 'preprocessors', {})
func.preprocessors.update(kwargs)
return func
return wrapper
class ResourceType(object):
@classmethod
@abc.abstractmethod
def transform(cls, clients, resource_config):
"""Transform the resource.
:param clients: openstack admin client handles
:param resource_config: scenario config of resource
:returns: transformed value of resource
"""
pass
def _id_from_name(resource_config, resources, typename):
"""Return the id of the resource whose name matches the pattern.
When resource_config contains `name`, an exact match is used.
When resource_config contains `regex`, a pattern match is used.
An `InvalidScenarioArgument` is thrown if the pattern does
not match unambiguously.
:param resource_config: resource to be transformed
:param resources: iterable containing all resources
:param typename: name which describes the type of resource
:returns: resource id uniquely mapped to `name` or `regex`
"""
if resource_config.get('name'):
patternstr = "^{0}$".format(resource_config.get('name'))
elif resource_config.get('regex'):
patternstr = resource_config.get('regex')
else:
raise exceptions.InvalidScenarioArgument(
"{typename} 'id', 'name', or 'regex' not found "
"in '{resource_config}' ".format(typename=typename.title(),
resource_config=resource_config))
pattern = re.compile(patternstr)
matching = filter(lambda resource: re.search(pattern, resource.name),
resources)
if not matching:
raise exceptions.InvalidScenarioArgument(
"{typename} with pattern '{pattern}' not found".format(
typename=typename.title(), pattern=pattern.pattern))
elif len(matching) > 1:
raise exceptions.InvalidScenarioArgument(
"{typename} with name '{pattern}' is ambiguous, "
"possible matches by id: {ids}".format(
typename=typename.title(), pattern=pattern.pattern,
ids=", ".join(map(operator.attrgetter("id"), matching))))
return matching[0].id
class FlavorResourceType(ResourceType):
@classmethod
def transform(cls, clients, resource_config):
"""Transform the resource config to id.
:param clients: openstack admin client handles
:param resource_config: scenario config with `id`, `name` or `regex`
:returns: id matching resource
"""
resource_id = resource_config.get('id')
if not resource_id:
novaclient = clients.nova()
resource_id = _id_from_name(resource_config=resource_config,
resources=novaclient.
flavors.list(), typename='flavor')
return resource_id
class ImageResourceType(ResourceType):
@classmethod
def transform(cls, clients, resource_config):
"""Transform the resource config to id.
:param clients: openstack admin client handles
:param resource_config: scenario config with `id`, `name` or `regex`
:returns: id matching resource
"""
resource_id = resource_config.get('id')
if not resource_id:
glanceclient = clients.glance()
resource_id = _id_from_name(resource_config=resource_config,
resources=glanceclient.
images.list(), typename='image')
return resource_id

View File

@ -19,6 +19,7 @@ import os
from glanceclient import exc as glance_exc
from novaclient import exceptions as nova_exc
from rally.benchmark import types as types
from rally import consts
from rally.openstack.common.gettextutils import _
from rally.verification.verifiers.tempest import tempest
@ -144,10 +145,12 @@ def image_exists(param_name):
to get image id value.
"""
def image_exists_validator(**kwargs):
image_id = kwargs.get(param_name)
glanceclient = kwargs["clients"].glance()
clients = kwargs.get('clients')
image_id = types.ImageResourceType.transform(clients=clients,
resource_config=
kwargs.get(param_name))
try:
glanceclient.images.get(image=image_id)
clients.glance().images.get(image=image_id)
return ValidationResult()
except glance_exc.HTTPNotFound:
message = _("Image with id '%s' not found") % image_id
@ -162,10 +165,12 @@ def flavor_exists(param_name):
to get flavor id value.
"""
def flavor_exists_validator(**kwargs):
flavor_id = kwargs.get(param_name)
novaclient = kwargs["clients"].nova()
clients = kwargs.get('clients')
flavor_id = types.FlavorResourceType.transform(clients=clients,
resource_config=
kwargs.get(param_name))
try:
novaclient.flavors.get(flavor=flavor_id)
clients.nova().flavors.get(flavor=flavor_id)
return ValidationResult()
except nova_exc.NotFound:
message = _("Flavor with id '%s' not found") % flavor_id
@ -183,20 +188,22 @@ def image_valid_on_flavor(flavor_name, image_name):
"""
def image_valid_on_flavor_validator(**kwargs):
flavor_id = kwargs.get(flavor_name)
novaclient = kwargs["clients"].nova()
clients = kwargs.get('clients')
flavor_id = types.FlavorResourceType.transform(clients=clients,
resource_config=
kwargs.get(flavor_name))
try:
flavor = novaclient.flavors.get(flavor=flavor_id)
flavor = clients.nova().flavors.get(flavor=flavor_id)
except nova_exc.NotFound:
message = _("Flavor with id '%s' not found") % flavor_id
return ValidationResult(False, message)
image_id = kwargs.get(image_name)
glanceclient = kwargs["clients"].glance()
image_id = types.ImageResourceType.transform(clients=clients,
resource_config=
kwargs.get(image_name))
try:
image = glanceclient.images.get(image=image_id)
image = clients.glance().images.get(image=image_id)
except glance_exc.HTTPNotFound:
message = _("Image with id '%s' not found") % image_id
return ValidationResult(False, message)
@ -216,7 +223,6 @@ def image_valid_on_flavor(flavor_name, image_name):
message = _("The disk size for flavor '%s' is too small "
"for requested image '%s'") % (flavor_id, image_id)
return ValidationResult(False, message)
return ValidationResult()
return image_valid_on_flavor_validator

View File

@ -204,14 +204,16 @@ class ScenarioRunnerTestCase(test.TestCase):
config,
serial.SerialScenarioRunner.CONFIG_SCHEMA)
@mock.patch("rally.benchmark.runners.base.osclients")
@mock.patch("rally.benchmark.runners.base.base_ctx.ContextManager")
def test_run(self, mock_ctx_manager):
def test_run(self, mock_ctx_manager, mock_osclients):
runner = constant.ConstantScenarioRunner(mock.MagicMock(),
self.fake_endpoints,
mock.MagicMock())
mock_ctx_manager.run.return_value = base.ScenarioRunnerResult([])
scenario_name = "NovaServers.boot_server_from_volume_and_delete"
result = runner.run(scenario_name, {"some_ctx": 2}, [1, 2, 3])
config_kwargs = {"image": {"id": 1}, "flavor": {"id": 1}}
result = runner.run(scenario_name, {"some_ctx": 2}, config_kwargs)
self.assertEqual(result, mock_ctx_manager.run.return_value)
@ -228,7 +230,7 @@ class ScenarioRunnerTestCase(test.TestCase):
}
expected = [context_obj, runner._run_scenario, cls, method_name,
context_obj, [1, 2, 3]]
context_obj, config_kwargs]
mock_ctx_manager.run.assert_called_once_with(*expected)
@mock.patch("rally.benchmark.runners.base.base_ctx.ContextManager")

View File

@ -158,8 +158,8 @@ class BenchmarkEngineTestCase(test.TestCase):
def test__validate_config_semantic_helper(self, mock_validate):
task = mock.MagicMock()
eng = engine.BenchmarkEngine(mock.MagicMock(), mock.MagicMock())
eng._validate_config_sematic_helper("admin", "user", "name", "pos",
task, {"args": "args"})
eng._validate_config_semantic_helper("admin", "user", "name", "pos",
task, {"args": "args"})
mock_validate.assert_called_once_with(
"name", "args", admin="admin", users=["user"],
task=task)
@ -170,15 +170,15 @@ class BenchmarkEngineTestCase(test.TestCase):
eng = engine.BenchmarkEngine(mock.MagicMock(), mock.MagicMock())
self.assertRaises(exceptions.InvalidBenchmarkConfig,
eng._validate_config_sematic_helper, "a", "u", "n",
eng._validate_config_semantic_helper, "a", "u", "n",
"p", mock.MagicMock(), {})
@mock.patch("rally.benchmark.engine.osclients.Clients")
@mock.patch("rally.benchmark.engine.users_ctx")
@mock.patch("rally.benchmark.engine.BenchmarkEngine"
"._validate_config_sematic_helper")
def test__validate_config_sematic(self, mock_helper, mock_userctx,
mock_osclients):
"._validate_config_semantic_helper")
def test__validate_config_semantic(self, mock_helper, mock_userctx,
mock_osclients):
mock_userctx.UserGenerator = fakes.FakeUserContext
mock_osclients.return_value = mock.MagicMock()
config = {

View File

@ -0,0 +1,122 @@
# Copyright (C) 2014 Yahoo! Inc. All Rights Reserved.
# 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.benchmark import types
from rally import exceptions
from tests import fakes
from tests import test
class FlavorResourceTypeTestCase(test.TestCase):
def setUp(self):
super(FlavorResourceTypeTestCase, self).setUp()
self.clients = fakes.FakeClients()
self.clients.nova().flavors._cache(fakes.FakeResource(name='m1.tiny',
id=1))
self.clients.nova().flavors._cache(fakes.FakeResource(name='m1.nano',
id=42))
def test_transform_by_id(self):
resource_config = {"id": 42}
flavor_id = types.FlavorResourceType.transform(clients=self.clients,
resource_config=
resource_config)
self.assertEqual(flavor_id, 42)
def test_transform_by_name(self):
resource_config = {"name": "m1.nano"}
flavor_id = types.FlavorResourceType.transform(clients=self.clients,
resource_config=
resource_config)
self.assertEqual(flavor_id, 42)
def test_transform_by_name_to_dest(self):
resource_config = {"name": "m1.nano"}
flavor_id = types.FlavorResourceType.transform(clients=self.clients,
resource_config=
resource_config)
self.assertEqual(flavor_id, 42)
def test_transform_by_name_no_match(self):
resource_config = {"name": "m1.medium"}
self.assertRaises(exceptions.InvalidScenarioArgument,
types.FlavorResourceType.transform, self.clients,
resource_config)
def test_transform_by_regex(self):
resource_config = {"regex": "m(1|2)\.nano"}
flavor_id = types.FlavorResourceType.transform(clients=self.clients,
resource_config=
resource_config)
self.assertEqual(flavor_id, 42)
def test_transform_by_regex_no_match(self):
resource_config = {"regex": "m(2|3)\.nano"}
self.assertRaises(exceptions.InvalidScenarioArgument,
types.FlavorResourceType.transform, self.clients,
resource_config)
class ImageResourceTypeTestCase(test.TestCase):
def setUp(self):
super(ImageResourceTypeTestCase, self).setUp()
self.clients = fakes.FakeClients()
image1 = fakes.FakeResource(name='cirros-0.3.1-uec', id=100)
self.clients.glance().images._cache(image1)
image2 = fakes.FakeResource(name='cirros-0.3.1-uec-ramdisk', id=101)
self.clients.glance().images._cache(image2)
def test_transform_by_id(self):
resource_config = {"id": 100}
image_id = types.ImageResourceType.transform(clients=self.clients,
resource_config=
resource_config)
self.assertEqual(image_id, 100)
def test_transform_by_name(self):
resource_config = {"name": "cirros-0.3.1-uec"}
image_id = types.ImageResourceType.transform(clients=self.clients,
resource_config=
resource_config)
self.assertEqual(image_id, 100)
def test_transform_by_name_to_dest(self):
resource_config = {"name": "cirros-0.3.1-uec"}
image_id = types.ImageResourceType.transform(clients=self.clients,
resource_config=
resource_config)
self.assertEqual(image_id, 100)
def test_transform_by_name_no_match(self):
resource_config = {"name": "cirros-0.3.1-uec-boot"}
self.assertRaises(exceptions.InvalidScenarioArgument,
types.ImageResourceType.transform, self.clients,
resource_config)
def test_transform_by_regex(self):
resource_config = {"regex": "-uec$"}
image_id = types.ImageResourceType.transform(clients=self.clients,
resource_config=
resource_config)
self.assertEqual(image_id, 100)
def test_transform_by_regex_no_match(self):
resource_config = {"regex": "-boot$"}
self.assertRaises(exceptions.InvalidScenarioArgument,
types.ImageResourceType.transform, self.clients,
resource_config)

View File

@ -124,10 +124,11 @@ class ValidationUtilsTestCase(test.TestCase):
fakegclient = fakes.FakeGlanceClient()
fakegclient.images.get = mock.MagicMock()
mock_osclients.glance.return_value = fakegclient
validator = validation.image_exists("image_id")
validator = validation.image_exists("image")
test_img_id = "test_image_id"
resource = {"id": test_img_id}
result = validator(clients=mock_osclients,
image_id=test_img_id)
image=resource)
fakegclient.images.get.assert_called_once_with(image=test_img_id)
self.assertTrue(result.is_valid)
self.assertIsNone(result.msg)
@ -138,10 +139,11 @@ class ValidationUtilsTestCase(test.TestCase):
fakegclient.images.get = mock.MagicMock()
fakegclient.images.get.side_effect = glance_exc.HTTPNotFound
mock_osclients.glance.return_value = fakegclient
validator = validation.image_exists("image_id")
validator = validation.image_exists("image")
test_img_id = "test_image_id"
resource = {"id": test_img_id}
result = validator(clients=mock_osclients,
image_id=test_img_id)
image=resource)
fakegclient.images.get.assert_called_once_with(image=test_img_id)
self.assertFalse(result.is_valid)
self.assertIsNotNone(result.msg)
@ -151,10 +153,11 @@ class ValidationUtilsTestCase(test.TestCase):
fakenclient = fakes.FakeNovaClient()
fakenclient.flavors = mock.MagicMock()
mock_osclients.nova.return_value = fakenclient
validator = validation.flavor_exists("flavor_id")
validator = validation.flavor_exists("flavor")
test_flavor_id = 1
resource = {"id": test_flavor_id}
result = validator(clients=mock_osclients,
flavor_id=test_flavor_id)
flavor=resource)
fakenclient.flavors.get.assert_called_once_with(flavor=test_flavor_id)
self.assertTrue(result.is_valid)
self.assertIsNone(result.msg)
@ -165,10 +168,11 @@ class ValidationUtilsTestCase(test.TestCase):
fakenclient.flavors = mock.MagicMock()
fakenclient.flavors.get.side_effect = nova_exc.NotFound(code=404)
mock_osclients.nova.return_value = fakenclient
validator = validation.flavor_exists("flavor_id")
validator = validation.flavor_exists("flavor")
test_flavor_id = 101
resource = {"id": test_flavor_id}
result = validator(clients=mock_osclients,
flavor_id=test_flavor_id)
flavor=resource)
fakenclient.flavors.get.assert_called_once_with(flavor=test_flavor_id)
self.assertFalse(result.is_valid)
self.assertIsNotNone(result.msg)
@ -190,11 +194,11 @@ class ValidationUtilsTestCase(test.TestCase):
fakenclient.flavors.get = mock.MagicMock(return_value=flavor)
mock_osclients.nova.return_value = fakenclient
validator = validation.image_valid_on_flavor("flavor_id", "image_id")
validator = validation.image_valid_on_flavor("flavor", "image")
result = validator(clients=mock_osclients,
flavor_id=flavor.id,
image_id=image.id)
flavor={"id": flavor.id},
image={"id": image.id})
fakenclient.flavors.get.assert_called_once_with(flavor=flavor.id)
fakegclient.images.get.assert_called_once_with(image=image.id)
@ -219,11 +223,11 @@ class ValidationUtilsTestCase(test.TestCase):
fakenclient.flavors.get = mock.MagicMock(return_value=flavor)
mock_osclients.nova.return_value = fakenclient
validator = validation.image_valid_on_flavor("flavor_id", "image_id")
validator = validation.image_valid_on_flavor("flavor", "image")
result = validator(clients=mock_osclients,
flavor_id=flavor.id,
image_id=image.id)
flavor={"id": flavor.id},
image={"id": image.id})
fakenclient.flavors.get.assert_called_once_with(flavor=flavor.id)
fakegclient.images.get.assert_called_once_with(image=image.id)
@ -243,13 +247,13 @@ class ValidationUtilsTestCase(test.TestCase):
fakenclient.flavors.get = mock.MagicMock(return_value=flavor)
mock_osclients.nova.return_value = fakenclient
validator = validation.image_valid_on_flavor("flavor_id", "image_id")
validator = validation.image_valid_on_flavor("flavor", "image")
test_img_id = "test_image_id"
result = validator(clients=mock_osclients,
flavor_id=flavor.id,
image_id=test_img_id)
flavor={"id": flavor.id},
image={"id": test_img_id})
fakenclient.flavors.get.assert_called_once_with(flavor=flavor.id)
fakegclient.images.get.assert_called_once_with(image=test_img_id)
@ -266,14 +270,14 @@ class ValidationUtilsTestCase(test.TestCase):
fakenclient.flavors.get.side_effect = nova_exc.NotFound(code=404)
mock_osclients.nova.return_value = fakenclient
validator = validation.image_valid_on_flavor("flavor_id", "image_id")
validator = validation.image_valid_on_flavor("flavor", "image")
test_img_id = "test_image_id"
test_flavor_id = 101
result = validator(clients=mock_osclients,
flavor_id=test_flavor_id,
image_id=test_img_id)
flavor={"id": test_flavor_id},
image={"id": test_img_id})
fakenclient.flavors.get.assert_called_once_with(flavor=test_flavor_id)