Skip to content

Commit

Permalink
Add the flavor context
Browse files Browse the repository at this point in the history
Add the flavor context (moved from samples) that is creating and
destroying a context specified by the configuration.

Change-Id: I8072867c1af49faeaf299d4da07930e42b3060bf
  • Loading branch information
Pavel Boldin committed Jan 3, 2015
1 parent a818e33 commit 9f18969
Show file tree
Hide file tree
Showing 8 changed files with 424 additions and 8 deletions.
31 changes: 31 additions & 0 deletions doc/samples/tasks/contexts/flavors/boot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"NovaServers.boot_server": [
{
"args": {
"flavor": {
"name": "^ram64$"
},
"image": {
"name": "^cirros.*uec$"
}
},
"runner": {
"type": "constant",
"times": 10,
"concurrency": 2
},
"context": {
"users": {
"tenants": 3,
"users_per_tenant": 2
},
"flavors": [
{
"name": "ram64",
"ram": 64
}
]
}
}
]
}
20 changes: 20 additions & 0 deletions doc/samples/tasks/contexts/flavors/boot.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
NovaServers.boot_server:
-
args:
flavor:
name: "^ram64$"
image:
name: "^cirros.*uec$"
runner:
type: "constant"
times: 10
concurrency: 2
context:
users:
tenants: 3
users_per_tenant: 2
flavors:
-
name: "ram64"
ram: 64
6 changes: 5 additions & 1 deletion rally-jobs/rally.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,7 @@
-
args:
flavor:
name: "m1.tiny"
name: "^ram64$"
image:
name: "^cirros.*uec$"
auto_assign_nics: false
Expand All @@ -964,6 +964,10 @@
users:
tenants: 3
users_per_tenant: 2
flavors:
-
name: "ram64"
ram: 64
sla:
failure_rate:
max: 0
Expand Down
131 changes: 131 additions & 0 deletions rally/benchmark/context/flavors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Copyright 2014: Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from rally.benchmark.context import base
from rally.common import utils as rutils
from rally import log as logging
from rally import osclients

LOG = logging.getLogger(__name__)


@base.context(name="flavors", order=340)
class FlavorsGenerator(base.Context):
"""Context creates a list of flavors."""

CONFIG_SCHEMA = {
"type": "array",
"$schema": rutils.JSON_SCHEMA,
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
},
"ram": {
"type": "integer",
"minimum": 1
},
"vcpus": {
"type": "integer",
"minimum": 1
},
"disk": {
"type": "integer",
"minimum": 0
},
"swap": {
"type": "integer",
"minimum": 0
},
"ephemeral": {
"type": "integer",
"minimum": 0
},
"extra_specs": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"additionalProperties": False,
"required": ["name", "ram"]
}
}

@rutils.log_task_wrapper(LOG.info, _("Enter context: `flavors`"))
def setup(self):
"""Create list of flavors."""
self.context["flavors"] = {}

clients = osclients.Clients(self.context["admin"]["endpoint"])
for flavor_config in self.config:

extra_specs = flavor_config.get("extra_specs")

flavor_config = FlavorConfig(**flavor_config)
try:
flavor = clients.nova().flavors.create(**flavor_config)
except osclients.nova.exceptions.Conflict as e:
LOG.warning("Using already existing flavor %s" %
flavor_config["name"])
if logging.is_debug():
LOG.exception(e)
continue

if extra_specs:
flavor.set_keys(extra_specs)

self.context["flavors"][flavor_config["name"]] = flavor.to_dict()
LOG.debug("Created flavor with id '%s'" % flavor.id)

@rutils.log_task_wrapper(LOG.info, _("Exit context: `flavors`"))
def cleanup(self):
"""Delete created flavors."""
clients = osclients.Clients(self.context["admin"]["endpoint"])
for flavor in self.context["flavors"].values():
try:
rutils.retry(3, clients.nova().flavors.delete, flavor["id"])
LOG.debug("Flavor is deleted %s" % flavor["id"])
except Exception as e:
LOG.error(
"Can't delete flavor %s: %s" % (flavor["id"], e.message))
if logging.is_debug():
LOG.exception(e)


class FlavorConfig(dict):
def __init__(self, name, ram, vcpus=1, disk=0, swap=0, ephemeral=0,
extra_specs=None):
"""Flavor configuration for context and flavor & image validation code.
Context code uses this code to provide default values for flavor
creation. Validation code uses this class as a Flavor instance to
check image validity against a flavor that is to be created by
the context.
:param name: name of the newly created flavor
:param ram: RAM amount for the flavor (MBs)
:param vcpus: VCPUs amount for the flavor
:param disk: disk amount for the flavor (GBs)
:param swap: swap amount for the flavor (MBs)
:param ephemeral: ephemeral disk amount for the flavor (GBs)
:param extra_specs: is ignored
"""
super(FlavorConfig, self).__init__(
name=name, ram=ram, vcpus=vcpus, disk=disk,
swap=swap, ephemeral=ephemeral)
self.__dict__.update(self)
30 changes: 24 additions & 6 deletions rally/benchmark/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ def transform(cls, clients, resource_config):
"""


def _id_from_name(resource_config, resources, typename):
"""Return the id of the resource whose name matches the pattern.
def obj_from_name(resource_config, resources, typename):
"""Return the resource whose name matches the pattern.
resource_config has to contain `name`, as it is used to lookup an id.
resource_config has to contain `name`, as it is used to lookup a resource.
Value of the name will be treated as regexp.
An `InvalidScenarioArgument` is thrown if the pattern does
Expand All @@ -89,14 +89,14 @@ def _id_from_name(resource_config, resources, typename):
:param resources: iterable containing all resources
:param typename: name which describes the type of resource
:returns: resource id uniquely mapped to `name` or `regex`
:returns: resource object uniquely mapped to `name` or `regex`
"""
if "name" in resource_config:
# In a case of pattern string exactly maches resource name
matching_exact = filter(lambda r: r.name == resource_config["name"],
resources)
if len(matching_exact) == 1:
return matching_exact[0].id
return matching_exact[0]
elif len(matching_exact) > 1:
raise exceptions.InvalidScenarioArgument(
"{typename} with name '{pattern}' "
Expand Down Expand Up @@ -130,7 +130,25 @@ def _id_from_name(resource_config, resources, typename):
pattern=pattern.pattern,
ids=", ".join(map(operator.attrgetter("id"),
matching))))
return matching[0].id
return matching[0]


def _id_from_name(resource_config, resources, typename):
"""Return the id of the resource whose name matches the pattern.
resource_config has to contain `name`, as it is used to lookup an id.
Value of the name will be treated as regexp.
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`
"""
return obj_from_name(resource_config, resources, typename).id


class FlavorResourceType(ResourceType):
Expand Down
18 changes: 18 additions & 0 deletions rally/benchmark/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from glanceclient import exc as glance_exc
from novaclient import exceptions as nova_exc

from rally.benchmark.context import flavors as flavors_ctx
from rally.benchmark import types as types
from rally.common.i18n import _
from rally import consts
Expand Down Expand Up @@ -166,6 +167,19 @@ def _get_validated_image(config, clients, param_name):
return (ValidationResult(False, message), None)


def _get_flavor_from_context(config, flavor_value):
if "flavors" not in config.get("context", {}):
raise exceptions.InvalidScenarioArgument("No flavors context")

flavors = [flavors_ctx.FlavorConfig(**f)
for f in config["context"]["flavors"]]
resource = types.obj_from_name(resource_config=flavor_value,
resources=flavors, typename="flavor")
flavor = flavors_ctx.FlavorConfig(**resource)
flavor.id = "<context flavor: %s>" % flavor.name
return (ValidationResult(), flavor)


def _get_validated_flavor(config, clients, param_name):
flavor_value = config.get("args", {}).get(param_name)
if not flavor_value:
Expand All @@ -177,6 +191,10 @@ def _get_validated_flavor(config, clients, param_name):
flavor = clients.nova().flavors.get(flavor=flavor_id)
return (ValidationResult(), flavor)
except (nova_exc.NotFound, exceptions.InvalidScenarioArgument):
try:
return _get_flavor_from_context(config, flavor_value)
except exceptions.InvalidScenarioArgument:
pass
message = _("Flavor '%s' not found") % flavor_value
return (ValidationResult(False, message), None)

Expand Down

0 comments on commit 9f18969

Please sign in to comment.