diff --git a/rally-jobs/rally.yaml b/rally-jobs/rally.yaml index 9c6aef535c..65ca66ee6f 100755 --- a/rally-jobs/rally.yaml +++ b/rally-jobs/rally.yaml @@ -894,6 +894,54 @@ failure_rate: 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: flavor: diff --git a/rally/benchmark/context/images.py b/rally/benchmark/context/images.py index 094d481d33..0b8e02ae1b 100644 --- a/rally/benchmark/context/images.py +++ b/rally/benchmark/context/images.py @@ -14,7 +14,6 @@ from rally.benchmark.context import base 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.common.i18n import _ from rally.common import log as logging @@ -44,6 +43,17 @@ class ImageGenerator(base.Context): "image_container": { "type": "string", }, + "image_name": { + "type": "string", + }, + "min_ram": { # megabytes + "type": "integer", + "minimum": 0 + }, + "min_disk": { # gigabytes + "type": "integer", + "minimum": 0 + }, "images_per_tenant": { "type": "integer", "minimum": 1 @@ -63,20 +73,27 @@ class ImageGenerator(base.Context): image_type = self.config["image_type"] image_container = self.config["image_container"] images_per_tenant = self.config["images_per_tenant"] + image_name = self.config.get("image_name") for user, tenant_id in rutils.iterate_per_tenants( self.context["users"]): current_images = [] clients = osclients.Clients(user["endpoint"]) - glance_util_class = glance_utils.GlanceScenario( - clients=clients) + glance_scenario = glance_utils.GlanceScenario( + clients=clients) 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_container, - image_url, - image_type) + image = glance_scenario._create_image( + cur_name, image_container, image_url, image_type, + min_ram=self.config.get("min_ram", 0), + min_disk=self.config.get("min_disk", 0)) current_images.append(image.id) self.context["tenants"][tenant_id]["images"] = current_images diff --git a/rally/benchmark/validation.py b/rally/benchmark/validation.py index e8ee233859..5482ecd161 100644 --- a/rally/benchmark/validation.py +++ b/rally/benchmark/validation.py @@ -16,6 +16,7 @@ import functools import os +import re from glanceclient import exc as glance_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): - image_value = config.get("args", {}).get(param_name) - if not image_value: - msg = "Parameter %s is not specified." % param_name + image_context = config.get("context", {}).get("images", {}) + image_args = config.get("args", {}).get(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) + 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: image_id = types.ImageResourceType.transform( - clients=clients, resource_config=image_value) - image = clients.glance().images.get(image=image_id) + clients=clients, resource_config=image_args) + image = clients.glance().images.get(image=image_id).to_dict() return (ValidationResult(True), image) 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) @@ -240,20 +257,20 @@ def image_valid_on_flavor(config, clients, deployment, flavor_name, if not valid_result.is_valid: 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 " - "for requested image '%s'") % (flavor.id, image.id) + "for requested image '%s'") % (flavor.id, image["id"]) return ValidationResult(False, message) 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 " - "for requested image '%s'") % (flavor.id, image.id) + "for requested image '%s'") % (flavor.id, image["id"]) 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 " - "for requested image '%s'") % (flavor.id, image.id) + "for requested image '%s'") % (flavor.id, image["id"]) return ValidationResult(False, message) diff --git a/rally/cmd/cliutils.py b/rally/cmd/cliutils.py index 5b2a243edc..39fea77d04 100644 --- a/rally/cmd/cliutils.py +++ b/rally/cmd/cliutils.py @@ -102,7 +102,7 @@ def print_list(objs, fields, formatters=None, sortby_index=0, if len(field_labels) != len(fields): raise ValueError(_("Field labels list %(labels)s has different number " "of elements than fields list %(fields)s"), - {'labels': field_labels, 'fields': fields}) + {"labels": field_labels, "fields": fields}) if sortby_index is None: kwargs = {} diff --git a/tests/unit/benchmark/context/test_images.py b/tests/unit/benchmark/context/test_images.py index cd0f3863ff..4d0436c644 100644 --- a/tests/unit/benchmark/context/test_images.py +++ b/tests/unit/benchmark/context/test_images.py @@ -44,6 +44,9 @@ class ImageGeneratorTestCase(test.TestCase): "image_type": "qcow2", "image_container": "bare", "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_container": "bare", "images_per_tenant": images_per_tenant, + "image_name": "some_name", + "min_ram": 128, + "min_disk": 1, } }, "admin": { @@ -143,6 +149,9 @@ class ImageGeneratorTestCase(test.TestCase): "image_type": "qcow2", "image_container": "bare", "images_per_tenant": 5, + "image_name": "some_name", + "min_ram": 128, + "min_disk": 1, } }, "admin": { diff --git a/tests/unit/benchmark/test_validation.py b/tests/unit/benchmark/test_validation.py index a175495bbd..ec0e94789f 100644 --- a/tests/unit/benchmark/test_validation.py +++ b/tests/unit/benchmark/test_validation.py @@ -120,19 +120,47 @@ class ValidatorsTestCase(test.TestCase): result = validation._get_validated_image({}, None, "non_existing") 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") def test__get_validated_image(self, mock_transform): mock_transform.return_value = "image_id" 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") 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, 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") 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") def test__get_validated_image_not_found(self, mock_transform): 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"}}, clients, "a") 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_flavor") 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() success = validation.ValidationResult(True) mock_get_flavor.return_value = (success, flavor) @@ -265,27 +299,27 @@ class ValidatorsTestCase(test.TestCase): # test ram flavor.disk = None flavor.ram = 2 - image.min_ram = None + image["min_ram"] = None result = validator(None, None, None) self.assertTrue(result.is_valid, result.msg) - image.min_ram = 4 + image["min_ram"] = 4 result = validator(None, None, None) self.assertFalse(result.is_valid, result.msg) - image.min_ram = 1 + image["min_ram"] = 1 result = validator(None, None, None) self.assertTrue(result.is_valid, result.msg) # test disk (flavor.disk not None) - image.size = 2 - image.min_disk = 0 + image["size"] = 2 + image["min_disk"] = 0 flavor.disk = 5.0 / (1024 ** 3) result = validator(None, None, None) self.assertTrue(result.is_valid, result.msg) - image.min_disk = flavor.disk * 2 + image["min_disk"] = flavor.disk * 2 result = validator(None, None, None) self.assertFalse(result.is_valid, result.msg) - image.min_disk = flavor.disk / 4 - image.size = 1000 + image["min_disk"] = flavor.disk / 4 + image["size"] = 1000 result = validator(None, None, None) self.assertFalse(result.is_valid, result.msg) @@ -297,7 +331,7 @@ class ValidatorsTestCase(test.TestCase): clients = mock.MagicMock() clients.nova().flavors.get.side_effect = nova_exc.NotFound("") - image = mock.MagicMock() + image = {"min_ram": 24, "id": "fake_id"} success = validation.ValidationResult(True) mock_get_image.return_value = (success, image) @@ -314,11 +348,11 @@ class ValidatorsTestCase(test.TestCase): } # test ram - image.min_ram = None + image["min_ram"] = None result = validator(config, clients, None) self.assertTrue(result.is_valid, result.msg) - image.min_ram = 64 + image["min_ram"] = 64 result = validator(config, clients, None) self.assertFalse(result.is_valid, result.msg)