Merge "Refactor ImageGenerator and validation"

This commit is contained in:
Jenkins 2015-02-28 22:39:59 +00:00 committed by Gerrit Code Review
commit 8995027941
6 changed files with 163 additions and 38 deletions

View File

@ -894,6 +894,54 @@
failure_rate: failure_rate:
max: 0 max: 0
-
args:
flavor:
name: "m1.tiny"
image:
name: "from_context_uploaded"
runner:
type: "constant"
times: 1
concurrency: 1
context:
users:
tenants: 1
users_per_tenant: 1
images:
image_name: "from_context_uploaded"
image_url: "http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-disk.img"
image_type: "qcow2"
image_container: "bare"
images_per_tenant: 1
sla:
failure_rate:
max: 0
-
args:
flavor:
name: "m1.tiny"
image:
regex: "^from_context_uploaded$"
runner:
type: "constant"
times: 1
concurrency: 1
context:
users:
tenants: 1
users_per_tenant: 1
images:
image_name: "from_context_uploaded"
image_url: "http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-disk.img"
image_type: "qcow2"
image_container: "bare"
images_per_tenant: 1
sla:
failure_rate:
max: 0
- -
args: args:
flavor: flavor:

View File

@ -14,7 +14,6 @@
from rally.benchmark.context import base from rally.benchmark.context import base
from rally.benchmark.context.cleanup import manager as resource_manager from rally.benchmark.context.cleanup import manager as resource_manager
from rally.benchmark.scenarios import base as scenario_base
from rally.benchmark.scenarios.glance import utils as glance_utils from rally.benchmark.scenarios.glance import utils as glance_utils
from rally.common.i18n import _ from rally.common.i18n import _
from rally.common import log as logging from rally.common import log as logging
@ -44,6 +43,17 @@ class ImageGenerator(base.Context):
"image_container": { "image_container": {
"type": "string", "type": "string",
}, },
"image_name": {
"type": "string",
},
"min_ram": { # megabytes
"type": "integer",
"minimum": 0
},
"min_disk": { # gigabytes
"type": "integer",
"minimum": 0
},
"images_per_tenant": { "images_per_tenant": {
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
@ -63,20 +73,27 @@ class ImageGenerator(base.Context):
image_type = self.config["image_type"] image_type = self.config["image_type"]
image_container = self.config["image_container"] image_container = self.config["image_container"]
images_per_tenant = self.config["images_per_tenant"] images_per_tenant = self.config["images_per_tenant"]
image_name = self.config.get("image_name")
for user, tenant_id in rutils.iterate_per_tenants( for user, tenant_id in rutils.iterate_per_tenants(
self.context["users"]): self.context["users"]):
current_images = [] current_images = []
clients = osclients.Clients(user["endpoint"]) clients = osclients.Clients(user["endpoint"])
glance_util_class = glance_utils.GlanceScenario( glance_scenario = glance_utils.GlanceScenario(
clients=clients) clients=clients)
for i in range(images_per_tenant): for i in range(images_per_tenant):
rnd_name = scenario_base.Scenario._generate_random_name() if image_name and i > 0:
cur_name = image_name + str(i)
elif image_name:
cur_name = image_name
else:
cur_name = glance_scenario._generate_random_name(
prefix="rally_ctx_image_")
image = glance_util_class._create_image(rnd_name, image = glance_scenario._create_image(
image_container, cur_name, image_container, image_url, image_type,
image_url, min_ram=self.config.get("min_ram", 0),
image_type) min_disk=self.config.get("min_disk", 0))
current_images.append(image.id) current_images.append(image.id)
self.context["tenants"][tenant_id]["images"] = current_images self.context["tenants"][tenant_id]["images"] = current_images

View File

@ -16,6 +16,7 @@
import functools import functools
import os import os
import re
from glanceclient import exc as glance_exc from glanceclient import exc as glance_exc
from novaclient import exceptions as nova_exc from novaclient import exceptions as nova_exc
@ -151,17 +152,33 @@ def file_exists(config, clients, deployment, param_name, mode=os.R_OK):
def _get_validated_image(config, clients, param_name): def _get_validated_image(config, clients, param_name):
image_value = config.get("args", {}).get(param_name) image_context = config.get("context", {}).get("images", {})
if not image_value: image_args = config.get("args", {}).get(param_name)
msg = "Parameter %s is not specified." % param_name image_ctx_name = image_context.get("image_name")
if not image_args:
msg = _("Parameter %s is not specified.") % param_name
return (ValidationResult(False, msg), None) return (ValidationResult(False, msg), None)
if "image_name" in image_context:
# NOTE(rvasilets) check string is "exactly equal to" a regex
# or image name from context equal to image name from args
if "regex" in image_args:
match = re.match(image_args.get("regex"), image_ctx_name)
if image_ctx_name == image_args.get("name") or (
"regex" in image_args and match):
image = {
"size": image_context.get("min_disk", 0),
"min_ram": image_context.get("min_ram", 0),
"min_disk": image_context.get("min_disk", 0)
}
return (ValidationResult(True), image)
try: try:
image_id = types.ImageResourceType.transform( image_id = types.ImageResourceType.transform(
clients=clients, resource_config=image_value) clients=clients, resource_config=image_args)
image = clients.glance().images.get(image=image_id) image = clients.glance().images.get(image=image_id).to_dict()
return (ValidationResult(True), image) return (ValidationResult(True), image)
except (glance_exc.HTTPNotFound, exceptions.InvalidScenarioArgument): except (glance_exc.HTTPNotFound, exceptions.InvalidScenarioArgument):
message = _("Image '%s' not found") % image_value message = _("Image '%s' not found") % image_args
return (ValidationResult(False, message), None) return (ValidationResult(False, message), None)
@ -240,20 +257,20 @@ def image_valid_on_flavor(config, clients, deployment, flavor_name,
if not valid_result.is_valid: if not valid_result.is_valid:
return valid_result return valid_result
if flavor.ram < (image.min_ram or 0): if flavor.ram < (image["min_ram"] or 0):
message = _("The memory size for flavor '%s' is too small " message = _("The memory size for flavor '%s' is too small "
"for requested image '%s'") % (flavor.id, image.id) "for requested image '%s'") % (flavor.id, image["id"])
return ValidationResult(False, message) return ValidationResult(False, message)
if flavor.disk: if flavor.disk:
if (image.size or 0) > flavor.disk * (1024 ** 3): if (image["size"] or 0) > flavor.disk * (1024 ** 3):
message = _("The disk size for flavor '%s' is too small " message = _("The disk size for flavor '%s' is too small "
"for requested image '%s'") % (flavor.id, image.id) "for requested image '%s'") % (flavor.id, image["id"])
return ValidationResult(False, message) return ValidationResult(False, message)
if (image.min_disk or 0) > flavor.disk: if (image["min_disk"] or 0) > flavor.disk:
message = _("The disk size for flavor '%s' is too small " message = _("The disk size for flavor '%s' is too small "
"for requested image '%s'") % (flavor.id, image.id) "for requested image '%s'") % (flavor.id, image["id"])
return ValidationResult(False, message) return ValidationResult(False, message)

View File

@ -102,7 +102,7 @@ def print_list(objs, fields, formatters=None, sortby_index=0,
if len(field_labels) != len(fields): if len(field_labels) != len(fields):
raise ValueError(_("Field labels list %(labels)s has different number " raise ValueError(_("Field labels list %(labels)s has different number "
"of elements than fields list %(fields)s"), "of elements than fields list %(fields)s"),
{'labels': field_labels, 'fields': fields}) {"labels": field_labels, "fields": fields})
if sortby_index is None: if sortby_index is None:
kwargs = {} kwargs = {}

View File

@ -44,6 +44,9 @@ class ImageGeneratorTestCase(test.TestCase):
"image_type": "qcow2", "image_type": "qcow2",
"image_container": "bare", "image_container": "bare",
"images_per_tenant": 4, "images_per_tenant": 4,
"image_name": "some_name",
"min_ram": 128,
"min_disk": 1,
} }
} }
@ -93,6 +96,9 @@ class ImageGeneratorTestCase(test.TestCase):
"image_type": "qcow2", "image_type": "qcow2",
"image_container": "bare", "image_container": "bare",
"images_per_tenant": images_per_tenant, "images_per_tenant": images_per_tenant,
"image_name": "some_name",
"min_ram": 128,
"min_disk": 1,
} }
}, },
"admin": { "admin": {
@ -143,6 +149,9 @@ class ImageGeneratorTestCase(test.TestCase):
"image_type": "qcow2", "image_type": "qcow2",
"image_container": "bare", "image_container": "bare",
"images_per_tenant": 5, "images_per_tenant": 5,
"image_name": "some_name",
"min_ram": 128,
"min_disk": 1,
} }
}, },
"admin": { "admin": {

View File

@ -120,19 +120,47 @@ class ValidatorsTestCase(test.TestCase):
result = validation._get_validated_image({}, None, "non_existing") result = validation._get_validated_image({}, None, "non_existing")
self.assertFalse(result[0].is_valid, result[0].msg) self.assertFalse(result[0].is_valid, result[0].msg)
def test__get_validated_image_from_context(self):
clients = mock.MagicMock()
image = {
"size": 0,
"min_ram": 0,
"min_disk": 0
}
result = validation._get_validated_image({"args": {
"image": {"name": "foo"}}, "context": {
"images": {
"image_name": "foo"}
}}, clients, "image")
self.assertTrue(result[0].is_valid, result[0].msg)
self.assertEqual(result[1], image)
result = validation._get_validated_image({"args": {
"image": {"regex": r"^foo$"}}, "context": {
"images": {
"image_name": "foo"}
}}, clients, "image")
self.assertTrue(result[0].is_valid, result[0].msg)
self.assertEqual(result[1], image)
@mock.patch("rally.benchmark.validation.types.ImageResourceType.transform") @mock.patch("rally.benchmark.validation.types.ImageResourceType.transform")
def test__get_validated_image(self, mock_transform): def test__get_validated_image(self, mock_transform):
mock_transform.return_value = "image_id" mock_transform.return_value = "image_id"
clients = mock.MagicMock() clients = mock.MagicMock()
clients.glance().images.get.return_value = "image" clients.glance().images.get().to_dict.return_value = {
"image": "image_id"}
result = validation._get_validated_image({"args": {"a": "test"}}, result = validation._get_validated_image({"args": {"a": "test"},
"context": {
"image_name": "foo"}},
clients, "a") clients, "a")
self.assertTrue(result[0].is_valid, result[0].msg) self.assertTrue(result[0].is_valid, result[0].msg)
self.assertEqual(result[1], "image") self.assertEqual(result[1], {"image": "image_id"})
mock_transform.assert_called_once_with(clients=clients, mock_transform.assert_called_once_with(clients=clients,
resource_config="test") resource_config="test")
clients.glance().images.get.assert_called_once_with(image="image_id") clients.glance().images.get.assert_called_with(image="image_id")
@mock.patch("rally.benchmark.validation.types.ImageResourceType.transform") @mock.patch("rally.benchmark.validation.types.ImageResourceType.transform")
def test__get_validated_image_transform_error(self, mock_transform): def test__get_validated_image_transform_error(self, mock_transform):
@ -144,7 +172,8 @@ class ValidatorsTestCase(test.TestCase):
@mock.patch("rally.benchmark.validation.types.ImageResourceType.transform") @mock.patch("rally.benchmark.validation.types.ImageResourceType.transform")
def test__get_validated_image_not_found(self, mock_transform): def test__get_validated_image_not_found(self, mock_transform):
clients = mock.MagicMock() clients = mock.MagicMock()
clients.glance().images.get.side_effect = glance_exc.HTTPNotFound("") clients.glance().images.get().to_dict.side_effect = (
glance_exc.HTTPNotFound(""))
result = validation._get_validated_image({"args": {"a": "test"}}, result = validation._get_validated_image({"args": {"a": "test"}},
clients, "a") clients, "a")
self.assertFalse(result[0].is_valid, result[0].msg) self.assertFalse(result[0].is_valid, result[0].msg)
@ -254,7 +283,12 @@ class ValidatorsTestCase(test.TestCase):
@mock.patch("rally.benchmark.validation._get_validated_image") @mock.patch("rally.benchmark.validation._get_validated_image")
@mock.patch("rally.benchmark.validation._get_validated_flavor") @mock.patch("rally.benchmark.validation._get_validated_flavor")
def test_image_valid_on_flavor(self, mock_get_flavor, mock_get_image): def test_image_valid_on_flavor(self, mock_get_flavor, mock_get_image):
image = mock.MagicMock() image = {
"id": "fake_id",
"min_ram": None,
"size": 2,
"min_disk": 0
}
flavor = mock.MagicMock() flavor = mock.MagicMock()
success = validation.ValidationResult(True) success = validation.ValidationResult(True)
mock_get_flavor.return_value = (success, flavor) mock_get_flavor.return_value = (success, flavor)
@ -265,27 +299,27 @@ class ValidatorsTestCase(test.TestCase):
# test ram # test ram
flavor.disk = None flavor.disk = None
flavor.ram = 2 flavor.ram = 2
image.min_ram = None image["min_ram"] = None
result = validator(None, None, None) result = validator(None, None, None)
self.assertTrue(result.is_valid, result.msg) self.assertTrue(result.is_valid, result.msg)
image.min_ram = 4 image["min_ram"] = 4
result = validator(None, None, None) result = validator(None, None, None)
self.assertFalse(result.is_valid, result.msg) self.assertFalse(result.is_valid, result.msg)
image.min_ram = 1 image["min_ram"] = 1
result = validator(None, None, None) result = validator(None, None, None)
self.assertTrue(result.is_valid, result.msg) self.assertTrue(result.is_valid, result.msg)
# test disk (flavor.disk not None) # test disk (flavor.disk not None)
image.size = 2 image["size"] = 2
image.min_disk = 0 image["min_disk"] = 0
flavor.disk = 5.0 / (1024 ** 3) flavor.disk = 5.0 / (1024 ** 3)
result = validator(None, None, None) result = validator(None, None, None)
self.assertTrue(result.is_valid, result.msg) self.assertTrue(result.is_valid, result.msg)
image.min_disk = flavor.disk * 2 image["min_disk"] = flavor.disk * 2
result = validator(None, None, None) result = validator(None, None, None)
self.assertFalse(result.is_valid, result.msg) self.assertFalse(result.is_valid, result.msg)
image.min_disk = flavor.disk / 4 image["min_disk"] = flavor.disk / 4
image.size = 1000 image["size"] = 1000
result = validator(None, None, None) result = validator(None, None, None)
self.assertFalse(result.is_valid, result.msg) self.assertFalse(result.is_valid, result.msg)
@ -297,7 +331,7 @@ class ValidatorsTestCase(test.TestCase):
clients = mock.MagicMock() clients = mock.MagicMock()
clients.nova().flavors.get.side_effect = nova_exc.NotFound("") clients.nova().flavors.get.side_effect = nova_exc.NotFound("")
image = mock.MagicMock() image = {"min_ram": 24, "id": "fake_id"}
success = validation.ValidationResult(True) success = validation.ValidationResult(True)
mock_get_image.return_value = (success, image) mock_get_image.return_value = (success, image)
@ -314,11 +348,11 @@ class ValidatorsTestCase(test.TestCase):
} }
# test ram # test ram
image.min_ram = None image["min_ram"] = None
result = validator(config, clients, None) result = validator(config, clients, None)
self.assertTrue(result.is_valid, result.msg) self.assertTrue(result.is_valid, result.msg)
image.min_ram = 64 image["min_ram"] = 64
result = validator(config, clients, None) result = validator(config, clients, None)
self.assertFalse(result.is_valid, result.msg) self.assertFalse(result.is_valid, result.msg)