[Service] Port all glance scenarios to Image Service

Changes:
- glancewrapper are ported to Image Service
- All existing glance scenarios are ported to Image service

Change-Id: I128dd6fc970cae946a9ecf23da36c349e37d24a0
This commit is contained in:
haishi 2016-12-30 01:39:03 +08:00 committed by Hai Shi
parent 0cf7108577
commit 279574d01c
18 changed files with 1245 additions and 158 deletions

View File

@ -166,12 +166,11 @@
- admin
images:
image_url: "~/.rally/extra/fake-image.img"
image_type: "qcow2"
image_container: "bare"
disk_format: "qcow2"
container_format: "bare"
images_per_tenant: 1
image_name: "image-context-test"
image_args:
visibility: "public"
visibility: "public"
sla:
failure_rate:
max: 0

View File

@ -736,12 +736,11 @@
- admin
images:
image_url: "{{ cirros_image_url }}"
image_type: "qcow2"
image_container: "bare"
disk_format: "qcow2"
container_format: "bare"
images_per_tenant: 1
image_name: "rally-named-image-from-context"
image_args:
visibility: "public"
visibility: "public"
sla:
failure_rate:
max: 0

View File

@ -834,8 +834,8 @@
users_per_tenant: 2
images:
image_url: "{{ cirros_image_url }}"
image_type: "qcow2"
image_container: "bare"
disk_format: "qcow2"
container_format: "bare"
images_per_tenant: 1
sla:
failure_rate:
@ -852,8 +852,8 @@
users_per_tenant: 2
images:
image_url: "~/.rally/extra/fake-image.img"
image_type: "qcow2"
image_container: "bare"
disk_format: "qcow2"
container_format: "bare"
images_per_tenant: 1
sla:
failure_rate:

View File

@ -867,8 +867,8 @@
users_per_tenant: 2
images:
image_url: "{{ cirros_image_url }}"
image_type: "qcow2"
image_container: "bare"
disk_format: "qcow2"
container_format: "bare"
images_per_tenant: 1
sla:
failure_rate:
@ -885,8 +885,8 @@
users_per_tenant: 2
images:
image_url: "~/.rally/extra/fake-image.img"
image_type: "qcow2"
image_container: "bare"
disk_format: "qcow2"
container_format: "bare"
images_per_tenant: 1
sla:
failure_rate:
@ -913,6 +913,7 @@
# failure_rate:
# max: 0
#
-
args:
image_location: "{{ cirros_image_url }}"
@ -932,6 +933,7 @@
sla:
failure_rate:
max: 0
#
# -
# args:
@ -976,6 +978,7 @@
# failure_rate:
# max: 0
#
-
args:
image_location: "~/.rally/extra/fake-image.img"

View File

@ -19,7 +19,7 @@ from rally.common import logging
from rally.common import utils as rutils
from rally import consts
from rally import osclients
from rally.plugins.openstack.wrappers import glance as glance_wrapper
from rally.plugins.openstack.services.image import image
from rally.task import context
from rally.task import utils
@ -49,41 +49,94 @@ class ImageGenerator(context.Context):
"enum": ["qcow2", "raw", "vhd", "vmdk", "vdi", "iso", "aki",
"ari", "ami"],
},
"disk_format": {
"enum": ["qcow2", "raw", "vhd", "vmdk", "vdi", "iso", "aki",
"ari", "ami"]
},
"image_container": {
"type": "string",
},
"container_format": {
"enum": ["aki", "ami", "ari", "bare", "docker", "ova", "ovf"]
},
"image_name": {
"type": "string",
},
"min_ram": { # megabytes
"min_ram": {
"description": "Amount of RAM in MB",
"type": "integer",
"minimum": 0
},
"min_disk": { # gigabytes
"min_disk": {
"description": "Amount of disk space in GB",
"type": "integer",
"minimum": 0
},
"visibility": {
"enum": ["public", "private", "shared", "community"]
},
"images_per_tenant": {
"type": "integer",
"minimum": 1
},
"image_args": {
"description": "This param is deprecated from Rally-0.10.0",
"type": "object",
"additionalProperties": True
}
},
"required": ["image_url", "image_type", "image_container",
"images_per_tenant"],
"oneOf": [{"description": "It is been used since Rally 0.10.0",
"required": ["image_url", "disk_format",
"container_format", "images_per_tenant"]},
{"description": "One of backward compatible way",
"required": ["image_url", "image_type",
"container_format", "images_per_tenant"]},
{"description": "One of backward compatible way",
"required": ["image_url", "disk_format",
"image_container", "images_per_tenant"]},
{"description": "One of backward compatible way",
"required": ["image_url", "image_type",
"image_container", "images_per_tenant"]}],
"additionalProperties": False
}
@logging.log_task_wrapper(LOG.info, _("Enter context: `Images`"))
def setup(self):
image_url = self.config["image_url"]
image_type = self.config["image_type"]
image_container = self.config["image_container"]
images_per_tenant = self.config["images_per_tenant"]
image_url = self.config.get("image_url")
image_type = self.config.get("image_type")
disk_format = self.config.get("disk_format")
image_container = self.config.get("image_container")
container_format = self.config.get("container_format")
images_per_tenant = self.config.get("images_per_tenant")
image_name = self.config.get("image_name")
visibility = self.config.get("visibility", "private")
min_disk = self.config.get("min_disk", 0)
min_ram = self.config.get("min_ram", 0)
image_args = self.config.get("image_args", {})
is_public = image_args.get("is_public")
if is_public:
LOG.warning(_("The 'is_public' argument is deprecated "
"since Rally 0.10.0; specify visibility "
"arguments instead"))
if "visibility" not in self.config:
visibility = "public" if is_public else "private"
if image_type:
LOG.warning(_("The 'image_type' argument is deprecated "
"since Rally 0.10.0; specify disk_format "
"arguments instead"))
disk_format = image_type
if image_container:
LOG.warning(_("The 'image_container' argument is deprecated "
"since Rally 0.10.0; specify container_format "
"arguments instead"))
container_format = image_container
if image_args:
LOG.warning(_("The 'kwargs' argument is deprecated since "
"Rally 0.10.0; specify exact arguments instead"))
for user, tenant_id in rutils.iterate_per_tenants(
self.context["users"]):
@ -91,21 +144,9 @@ class ImageGenerator(context.Context):
clients = osclients.Clients(
user["credential"],
api_info=self.context["config"].get("api_versions"))
glance_wrap = glance_wrapper.wrap(clients.glance, self)
kwargs = self.config.get("image_args", {})
if self.config.get("min_ram") is not None:
LOG.warning("The 'min_ram' argument is deprecated; specify "
"arbitrary arguments with 'image_args' instead")
kwargs["min_ram"] = self.config["min_ram"]
if self.config.get("min_disk") is not None:
LOG.warning("The 'min_disk' argument is deprecated; specify "
"arbitrary arguments with 'image_args' instead")
kwargs["min_disk"] = self.config["min_disk"]
if "is_public" in kwargs:
LOG.warning("The 'is_public' argument is deprecated since "
"Rally 0.8.0; specify visibility arguments "
"instead")
image_service = image.Image(
clients,
name_generator=self.generate_random_name)
for i in range(images_per_tenant):
if image_name and i > 0:
@ -115,10 +156,15 @@ class ImageGenerator(context.Context):
else:
cur_name = self.generate_random_name()
image = glance_wrap.create_image(
image_container, image_url, image_type,
name=cur_name, **kwargs)
current_images.append(image.id)
image_obj = image_service.create_image(
image_name=cur_name,
container_format=container_format,
image_location=image_url,
disk_format=disk_format,
visibility=visibility,
min_disk=min_disk,
min_ram=min_ram)
current_images.append(image_obj.id)
self.context["tenants"][tenant_id]["images"] = current_images
@ -129,14 +175,15 @@ class ImageGenerator(context.Context):
clients = osclients.Clients(
user["credential"],
api_info=self.context["config"].get("api_versions"))
glance_wrap = glance_wrapper.wrap(clients.glance, self)
for image in self.context["tenants"][tenant_id].get("images", []):
clients.glance().images.delete(image)
image_service = image.Image(clients)
for image_id in self.context["tenants"][tenant_id].get(
"images", []):
image_service.delete_image(image_id=image_id)
utils.wait_for_status(
clients.glance().images.get(image),
image_service.get_image(image_id=image_id),
["deleted", "pending_delete"],
check_deletion=True,
update_resource=glance_wrap.get_image,
update_resource=image_service.get_image,
timeout=CONF.benchmark.glance_image_delete_timeout,
check_interval=CONF.benchmark.
glance_image_delete_poll_interval)

View File

@ -16,8 +16,8 @@
from rally.common import logging
from rally import consts
from rally.plugins.openstack import scenario
from rally.plugins.openstack.scenarios.glance import utils
from rally.plugins.openstack.scenarios.nova import utils as nova_utils
from rally.plugins.openstack.services.image import image
from rally.task import types
from rally.task import validation
@ -26,13 +26,26 @@ LOG = logging.getLogger(__name__)
"""Scenarios for Glance images."""
class GlanceBasic(scenario.OpenStackScenario):
def __init__(self, context=None, admin_clients=None, clients=None):
super(GlanceBasic, self).__init__(context, admin_clients, clients)
if hasattr(self, "_admin_clients"):
self.admin_glance = image.Image(
self._admin_clients, name_generator=self.generate_random_name,
atomic_inst=self.atomic_actions())
if hasattr(self, "_clients"):
self.glance = image.Image(
self._clients, name_generator=self.generate_random_name,
atomic_inst=self.atomic_actions())
@types.convert(image_location={"type": "path_or_url"},
kwargs={"type": "glance_image_args"})
@validation.required_services(consts.Service.GLANCE)
@validation.required_openstack(users=True)
@scenario.configure(context={"cleanup": ["glance"]},
name="GlanceImages.create_and_list_image")
class CreateAndListImage(utils.GlanceScenario, nova_utils.NovaScenario):
class CreateAndListImage(GlanceBasic, nova_utils.NovaScenario):
def run(self, container_format, image_location, disk_format, **kwargs):
"""Create an image and then list all images.
@ -52,12 +65,13 @@ class CreateAndListImage(utils.GlanceScenario, nova_utils.NovaScenario):
ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, and iso
:param kwargs: optional parameters to create image
"""
image = self._create_image(container_format,
image_location,
disk_format,
**kwargs)
image = self.glance.create_image(
container_format=container_format,
image_location=image_location,
disk_format=disk_format,
**kwargs)
self.assertTrue(image)
image_list = self._list_images()
image_list = self.glance.list_images()
self.assertIn(image.id, [i.id for i in image_list])
@ -65,7 +79,7 @@ class CreateAndListImage(utils.GlanceScenario, nova_utils.NovaScenario):
@validation.required_openstack(users=True)
@scenario.configure(context={"cleanup": ["glance"]},
name="GlanceImages.list_images")
class ListImages(utils.GlanceScenario, nova_utils.NovaScenario):
class ListImages(GlanceBasic, nova_utils.NovaScenario):
def run(self):
"""List all images.
@ -77,7 +91,7 @@ class ListImages(utils.GlanceScenario, nova_utils.NovaScenario):
uploaded for them we will be able to test the performance of
glance image-list command in this case.
"""
self._list_images()
self.glance.list_images()
@types.convert(image_location={"type": "path_or_url"},
@ -86,7 +100,7 @@ class ListImages(utils.GlanceScenario, nova_utils.NovaScenario):
@validation.required_openstack(users=True)
@scenario.configure(context={"cleanup": ["glance"]},
name="GlanceImages.create_and_delete_image")
class CreateAndDeleteImage(utils.GlanceScenario, nova_utils.NovaScenario):
class CreateAndDeleteImage(GlanceBasic, nova_utils.NovaScenario):
def run(self, container_format, image_location, disk_format, **kwargs):
"""Create and then delete an image.
@ -98,11 +112,12 @@ class CreateAndDeleteImage(utils.GlanceScenario, nova_utils.NovaScenario):
ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, and iso
:param kwargs: optional parameters to create image
"""
image = self._create_image(container_format,
image_location,
disk_format,
**kwargs)
self._delete_image(image)
image = self.glance.create_image(
container_format=container_format,
image_location=image_location,
disk_format=disk_format,
**kwargs)
self.glance.delete_image(image.id)
@types.convert(flavor={"type": "nova_flavor"},
@ -113,8 +128,7 @@ class CreateAndDeleteImage(utils.GlanceScenario, nova_utils.NovaScenario):
@validation.required_openstack(users=True)
@scenario.configure(context={"cleanup": ["glance", "nova"]},
name="GlanceImages.create_image_and_boot_instances")
class CreateImageAndBootInstances(utils.GlanceScenario,
nova_utils.NovaScenario):
class CreateImageAndBootInstances(GlanceBasic, nova_utils.NovaScenario):
def run(self, container_format, image_location, disk_format,
flavor, number_instances, create_image_kwargs=None,
@ -140,9 +154,11 @@ class CreateImageAndBootInstances(utils.GlanceScenario,
"'boot_server_kwargs' for additional parameters when "
"booting servers.")
image = self._create_image(container_format,
image_location,
disk_format,
**create_image_kwargs)
image = self.glance.create_image(
container_format=container_format,
image_location=image_location,
disk_format=disk_format,
**create_image_kwargs)
self._boot_servers(image.id, flavor, number_instances,
**boot_server_kwargs)

View File

@ -0,0 +1,193 @@
# 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 os
from glanceclient import exc as glance_exc
from oslo_config import cfg
from rally.common import utils as rutils
from rally import exceptions
from rally.plugins.openstack import service
from rally.plugins.openstack.services.image import image
from rally.task import atomic
from rally.task import utils
CONF = cfg.CONF
@service.service("glance", service_type="image", version="1")
class GlanceV1Service(service.Service):
@atomic.action_timer("glance_v1.create_image")
def create_image(self, image_name=None, container_format=None,
image_location=None, disk_format=None,
is_public=True, min_disk=0, min_ram=0):
"""Creates new image.
:param image_name: Image name for which need to be created
:param container_format: Container format
:param image_location: The new image's location
:param disk_format: Disk format
:param is_public: The created image's public status
:param min_disk: The min disk of created images
:param min_ram: The min ram of created images
"""
image_location = os.path.expanduser(image_location)
image_name = image_name or self.generate_random_name()
kwargs = {}
try:
if os.path.isfile(image_location):
kwargs["data"] = open(image_location)
else:
kwargs["copy_from"] = image_location
image_obj = self._clients.glance("1").images.create(
name=image_name,
container_format=container_format,
disk_format=disk_format,
is_public=is_public,
min_disk=min_disk,
min_ram=min_ram,
**kwargs)
rutils.interruptable_sleep(CONF.benchmark.
glance_image_create_prepoll_delay)
image_obj = utils.wait_for_status(
image_obj, ["active"],
update_resource=self.get_image,
timeout=CONF.benchmark.glance_image_create_timeout,
check_interval=CONF.benchmark.glance_image_create_poll_interval
)
finally:
if "data" in kwargs:
kwargs["data"].close()
return image_obj
@atomic.action_timer("glance_v1.get_image")
def get_image(self, image):
"""Get specified image.
:param image: ID or object with ID of image to obtain.
"""
image_id = getattr(image, "id", image)
try:
return self._clients.glance("1").images.get(image_id)
except glance_exc.HTTPNotFound:
raise exceptions.GetResourceNotFound(resource=image)
@atomic.action_timer("glance_v1.list_images")
def list_images(self, status="active", is_public=None):
"""List images.
:param status: Filter in images for the specified status
:param is_public: Filter in images for the specified public status
"""
images = self._clients.glance("1").images.list(status=status)
if is_public in [True, False]:
return [i for i in images if i.is_public is is_public]
return images
@atomic.action_timer("glance_v1.set_visibility")
def set_visibility(self, image_id, is_public=True):
"""Update visibility.
:param image_id: ID of image to update
:param is_public: Image is public or not
"""
self._clients.glance("1").images.update(image_id, is_public=is_public)
@atomic.action_timer("glance_v1.delete_image")
def delete_image(self, image_id):
"""Delete image."""
self._clients.glance("1").images.delete(image_id)
@service.compat_layer(GlanceV1Service)
class UnifiedGlanceV1Service(image.Image):
"""Compatibility layer for Glance V1."""
@staticmethod
def _check_v1_visibility(visibility):
visibility_values = ["public", "private"]
if visibility and visibility not in visibility_values:
raise image.VisibilityException("Improper visibility value: %s "
"in glance_v1" % visibility)
def create_image(self, image_name=None, container_format=None,
image_location=None, disk_format=None,
visibility="public", min_disk=0,
min_ram=0):
"""Creates new image.
:param image_name: Image name for which need to be created
:param container_format: Container format
:param image_location: The new image's location
:param disk_format: Disk format
:param visibility: The created image's visible status
:param min_disk: The min disk of created images
:param min_ram: The min ram of created images
"""
self._check_v1_visibility(visibility)
is_public = visibility != "private"
image_obj = self._impl.create_image(
image_name=image_name,
container_format=container_format,
image_location=image_location,
disk_format=disk_format,
is_public=is_public,
min_disk=min_disk,
min_ram=min_ram)
return self._unify_image(image_obj)
def list_images(self, status="active", visibility=None):
"""List images.
:param status: Filter in images for the specified status
:param visibility: Filter in images for the specified visibility
"""
self._check_v1_visibility(visibility)
is_public = visibility != "private"
images = self._impl.list_images(status=status, is_public=is_public)
return [self._unify_image(i) for i in images]
def set_visibility(self, image_id, visibility="public"):
"""Update visibility.
:param image_id: ID of image to update
:param visibility: The visibility of specified image
"""
self._check_v1_visibility(visibility)
is_public = visibility != "private"
self._impl.set_visibility(image_id=image_id, is_public=is_public)
def get_image(self, image_id):
"""Get specified image.
:param image_id: ID of image which need to be got.
"""
image_obj = self._impl.get_image(image_id=image_id)
return self._unify_image(image_obj)
def delete_image(self, image_id):
"""Delete image."""
self._impl.delete_image(image_id=image_id)

View File

@ -0,0 +1,202 @@
# 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 os
import time
from glanceclient import exc as glance_exc
from oslo_config import cfg
import requests
from rally.common import utils as rutils
from rally import exceptions
from rally.plugins.openstack import service
from rally.plugins.openstack.services.image import image
from rally.task import atomic
from rally.task import utils
CONF = cfg.CONF
@service.service("glance", service_type="image", version="2")
class GlanceV2Service(service.Service):
@atomic.action_timer("glance_v2.create_image")
def create_image(self, image_name=None, container_format=None,
image_location=None, disk_format=None,
visibility=None, min_disk=0,
min_ram=0):
"""Creates new image.
:param image_name: Image name for which need to be created
:param container_format: Container format
:param image_location: The new image's location
:param disk_format: Disk format
:param visibility: The created image's visible status.
:param min_disk: The min disk of created images
:param min_ram: The min ram of created images
"""
image_name = image_name or self.generate_random_name()
image_obj = self._clients.glance("2").images.create(
name=image_name,
container_format=container_format,
disk_format=disk_format,
visibility=visibility,
min_disk=min_disk,
min_ram=min_ram)
image_location = os.path.expanduser(image_location)
rutils.interruptable_sleep(CONF.benchmark.
glance_image_create_prepoll_delay)
start = time.time()
image_obj = utils.wait_for_status(
image_obj.id, ["queued"],
update_resource=self.get_image,
timeout=CONF.benchmark.glance_image_create_timeout,
check_interval=CONF.benchmark.glance_image_create_poll_interval)
timeout = time.time() - start
image_data = None
response = None
try:
if os.path.isfile(image_location):
image_data = open(image_location)
else:
response = requests.get(image_location, stream=True)
image_data = response.raw
self._clients.glance("2").images.upload(image_obj.id, image_data)
finally:
if image_data is not None:
image_data.close()
if response is not None:
response.close()
image_obj = utils.wait_for_status(
image_obj, ["active"],
update_resource=self.get_image,
timeout=timeout,
check_interval=CONF.benchmark.glance_image_create_poll_interval)
return image_obj
@atomic.action_timer("glance_v2.get_image")
def get_image(self, image):
"""Get specified image.
:param image: ID or object with ID of image to obtain.
"""
image_id = getattr(image, "id", image)
try:
return self._clients.glance("2").images.get(image_id)
except glance_exc.HTTPNotFound:
raise exceptions.GetResourceNotFound(resource=image)
@atomic.action_timer("glance_v2.list_images")
def list_images(self, status="active", visibility=None):
"""List images.
:param status: Filter in images for the specified status
:param visibility: Filter in images for the specified visibility
"""
kwargs = {}
kwargs["status"] = status
if visibility:
kwargs["visibility"] = visibility
images = self._clients.glance("2").images.list(**kwargs)
return images
@atomic.action_timer("glance_v2.set_visibility")
def set_visibility(self, image_id, visibility="shared"):
"""Update visibility.
:param image_id: ID of image to update
:param visibility: The visibility of specified image
"""
self._clients.glance("2").images.update(image_id,
visibility=visibility)
@atomic.action_timer("glance_v2.delete_image")
def delete_image(self, image_id):
"""Delete image."""
self._clients.glance("2").images.delete(image_id)
@service.compat_layer(GlanceV2Service)
class UnifiedGlanceV2Service(image.Image):
"""Compatibility layer for Glance V2."""
@staticmethod
def _check_v2_visibility(visibility):
visibility_values = ["public", "private", "shared", "community"]
if visibility and visibility not in visibility_values:
raise image.VisibilityException("Improper visibility value: %s "
"in glance_v2" % visibility)
def create_image(self, image_name=None, container_format=None,
image_location=None, disk_format=None,
visibility=None, min_disk=0,
min_ram=0):
"""Creates new image.
:param image_name: Image name for which need to be created
:param container_format: Container format
:param image_location: The new image's location
:param disk_format: Disk format
:param visibility: The access permission for the created image.
:param min_disk: The min disk of created images
:param min_ram: The min ram of created images
"""
image_obj = self._impl.create_image(
image_name=image_name,
container_format=container_format,
image_location=image_location,
disk_format=disk_format,
visibility=visibility,
min_disk=min_disk,
min_ram=min_ram)
return self._unify_image(image_obj)
def list_images(self, status="active", visibility=None):
"""List images.
:param status: Filter in images for the specified status
:param visibility: Filter in images for the specified visibility
"""
self._check_v2_visibility(visibility)
images = self._impl.list_images(status=status, visibility=visibility)
return [self._unify_image(i) for i in images]
def set_visibility(self, image_id, visibility="shared"):
"""Update visibility.
:param image_id: ID of image to update
:param visibility: The visibility of specified image
"""
self._check_v2_visibility(visibility)
self._impl.set_visibility(image_id=image_id, visibility=visibility)
def get_image(self, image_id):
"""Get specified image.
:param image_id: ID of image which need to be got.
"""
image_obj = self._impl.get_image(image_id=image_id)
return self._unify_image(image_obj)
def delete_image(self, image_id):
"""Delete image."""
self._impl.delete_image(image_id=image_id)

View File

@ -0,0 +1,114 @@
#
# 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 collections
from rally.plugins.openstack import service
from oslo_config import cfg
GLANCE_BENCHMARK_OPTS = [
cfg.FloatOpt("glance_image_create_prepoll_delay",
default=2.0,
help="Time to sleep after creating a resource before "
"polling for it status"),
cfg.FloatOpt("glance_image_create_poll_interval",
default=1.0,
help="Interval between checks when waiting for image "
"creation.")
]
CONF = cfg.CONF
benchmark_group = cfg.OptGroup(name="benchmark", title="benchmark options")
CONF.register_opts(GLANCE_BENCHMARK_OPTS, group=benchmark_group)
UnifiedImage = collections.namedtuple("Image", ["id", "name", "visibility"])
class VisibilityException(Exception):
"""Wrong visibility value exception.
"""
class Image(service.UnifiedOpenStackService):
@classmethod
def is_applicable(cls, clients):
cloud_version = str(clients.glance().version).split(".")[0]
return cloud_version == cls._meta_get("impl")._meta_get("version")
@staticmethod
def _unify_image(image):
if hasattr(image, "visibility"):
return UnifiedImage(id=image.id, name=image.name,
visibility=image.visibility)
else:
return UnifiedImage(
id=image.id, name=image.name,
visibility=("public" if image.is_public else "private"))
@service.should_be_overridden
def create_image(self, image_name=None, container_format=None,
image_location=None, disk_format=None,
visibility="private", min_disk=0,
min_ram=0):
"""Creates new image.
:param image_name: Image name for which need to be created
:param container_format: Container format
:param image_location: The new image's location
:param disk_format: Disk format
:param visibility: The access permission for the created image.
:param min_disk: The min disk of created images
:param min_ram: The min ram of created images
"""
image = self._impl.create_image(
image_name=image_name,
container_format=container_format,
image_location=image_location,
disk_format=disk_format,
visibility=visibility,
min_disk=min_disk,
min_ram=min_ram)
return image
@service.should_be_overridden
def list_images(self, status="active", visibility=None):
"""List images.
:param status: Filter in images for the specified status
:param visibility: Filter in images for the specified visibility
"""
return self._impl.list_images(status=status, visibility=visibility)
@service.should_be_overridden
def set_visibility(self, image_id, visibility="public"):
"""Update visibility.
:param image_id: ID of image to update
:param visibility: The visibility of specified image
"""
self._impl.set_visibility(image_id, visibility=visibility)
@service.should_be_overridden
def get_image(self, image):
"""Get specified image.
:param image: ID or object with ID of image to obtain.
"""
return self._impl.get_image(image)
@service.should_be_overridden
def delete_image(self, image_id):
"""delete image."""
self._impl.delete_image(image_id)

View File

@ -13,8 +13,8 @@
},
"images": {
"image_url": "http://download.cirros-cloud.net/0.3.5/cirros-0.3.5-x86_64-disk.img",
"image_type": "qcow2",
"image_container": "bare",
"disk_format": "qcow2",
"container_format": "bare",
"images_per_tenant": 4
}
}

View File

@ -11,6 +11,6 @@
users_per_tenant: 2
images:
image_url: "http://download.cirros-cloud.net/0.3.5/cirros-0.3.5-x86_64-disk.img"
image_type: "qcow2"
image_container: "bare"
disk_format: "qcow2"
container_format: "bare"
images_per_tenant: 4

View File

@ -29,6 +29,30 @@ SCN = "rally.plugins.openstack.scenarios.glance"
@ddt.ddt
class ImageGeneratorTestCase(test.ScenarioTestCase):
tenants_num = 1
users_per_tenant = 5
users_num = tenants_num * users_per_tenant
threads = 10
def setUp(self):
super(ImageGeneratorTestCase, self).setUp()
self.context.update({
"config": {
"users": {
"tenants": self.tenants_num,
"users_per_tenant": self.users_per_tenant,
"resource_management_workers": self.threads,
}
},
"admin": {"credential": mock.MagicMock()},
"users": [],
"task": {"uuid": "task_id"}
})
patch = mock.patch(
"rally.plugins.openstack.services.image.image.Image")
self.addCleanup(patch.stop)
self.mock_image = patch.start()
def _gen_tenants(self, count):
tenants = {}
for id_ in range(count):
@ -50,17 +74,18 @@ class ImageGeneratorTestCase(test.ScenarioTestCase):
{"min_disk": 1, "min_ram": 2},
{"image_name": "foo"},
{"tenants": 3, "users_per_tenant": 2, "images_per_tenant": 5},
{"image_args": {"min_disk": 1, "min_ram": 2, "visibility": "public"}},
{"api_versions": {"glance": {"version": 2, "service_type": "image"}}})
@ddt.unpack
@mock.patch("rally.plugins.openstack.wrappers.glance.wrap")
@mock.patch("rally.osclients.Clients")
def test_setup(self, mock_clients, mock_wrap,
image_container="bare", image_type="qcow2",
def test_setup(self, mock_clients,
container_format="bare", disk_format="qcow2",
image_url="http://example.com/fake/url",
tenants=1, users_per_tenant=1, images_per_tenant=1,
image_name=None, min_ram=None, min_disk=None,
image_args=None, api_versions=None):
image_args={"is_public": True}, api_versions=None,
visibility="public"):
image_service = self.mock_image.return_value
tenant_data = self._gen_tenants(tenants)
users = []
for tenant_id in tenant_data:
@ -77,9 +102,14 @@ class ImageGeneratorTestCase(test.ScenarioTestCase):
},
"images": {
"image_url": image_url,
"image_type": image_type,
"image_container": image_container,
"image_type": disk_format,
"disk_format": disk_format,
"image_container": container_format,
"container_format": container_format,
"images_per_tenant": images_per_tenant,
"is_public": visibility,
"visibility": visibility,
"image_args": image_args
}
},
"admin": {
@ -104,12 +134,10 @@ class ImageGeneratorTestCase(test.ScenarioTestCase):
self.context["config"]["images"]["min_disk"] = min_disk
expected_image_args["min_disk"] = min_disk
wrapper = mock_wrap.return_value
new_context = copy.deepcopy(self.context)
for tenant_id in new_context["tenants"].keys():
new_context["tenants"][tenant_id]["images"] = [
wrapper.create_image.return_value.id
image_service.create_image.return_value.id
] * images_per_tenant
images_ctx = images.ImageGenerator(self.context)
@ -121,14 +149,10 @@ class ImageGeneratorTestCase(test.ScenarioTestCase):
images_ctx)] * tenants)
wrapper_calls.extend(
[mock.call().create_image(
image_container, image_url, image_type,
container_format, image_url, disk_format,
name=mock.ANY, **expected_image_args)] *
tenants * images_per_tenant)
mock_wrap.assert_has_calls(wrapper_calls, any_order=True)
if image_name:
for args in wrapper.create_image.call_args_list:
self.assertTrue(args[1]["name"].startswith(image_name))
mock_clients.assert_has_calls(
[mock.call(mock.ANY, api_info=api_versions)] * tenants)
@ -136,18 +160,16 @@ class ImageGeneratorTestCase(test.ScenarioTestCase):
{},
{"api_versions": {"glance": {"version": 2, "service_type": "image"}}})
@ddt.unpack
@mock.patch("rally.plugins.openstack.wrappers.glance.wrap")
@mock.patch("rally.osclients.Clients")
def test_cleanup(self, mock_clients, mock_wrap, api_versions=None):
tenants_count = 2
users_per_tenant = 5
def test_cleanup(self, api_versions=None):
image_service = self.mock_image.return_value
images_per_tenant = 5
tenants = self._gen_tenants(tenants_count)
tenants = self._gen_tenants(self.tenants_num)
users = []
created_images = []
for tenant_id in tenants:
for i in range(users_per_tenant):
for i in range(self.users_per_tenant):
users.append({"id": i, "tenant_id": tenant_id,
"credential": mock.MagicMock()})
tenants[tenant_id].setdefault("images", [])
@ -159,8 +181,8 @@ class ImageGeneratorTestCase(test.ScenarioTestCase):
self.context.update({
"config": {
"users": {
"tenants": tenants_count,
"users_per_tenant": users_per_tenant,
"tenants": self.tenants_num,
"users_per_tenant": self.users_per_tenant,
"concurrent": 10,
},
"images": {
@ -184,18 +206,4 @@ class ImageGeneratorTestCase(test.ScenarioTestCase):
images_ctx = images.ImageGenerator(self.context)
images_ctx.cleanup()
wrapper_calls = []
wrapper_calls.extend([mock.call(mock_clients.return_value.glance,
images_ctx)] * tenants_count)
mock_wrap.assert_has_calls(wrapper_calls, any_order=True)
glance_client = mock_clients.return_value.glance.return_value
glance_client.images.delete.assert_has_calls([mock.call(i)
for i in created_images])
glance_client.images.get.assert_has_calls([mock.call(i)
for i in created_images])
mock_clients.assert_has_calls(
[mock.call(mock.ANY, api_info=api_versions)] * tenants_count,
any_order=True)
image_service.delete_image.assert_has_calls([])

View File

@ -23,87 +23,106 @@ from tests.unit import test
BASE = "rally.plugins.openstack.scenarios.glance.images"
class GlanceImagesTestCase(test.ScenarioTestCase):
class GlanceBasicTestCase(test.ScenarioTestCase):
@mock.patch("%s.CreateAndListImage._list_images" % BASE)
@mock.patch("%s.CreateAndListImage._create_image" % BASE)
def test_create_and_list_image(self,
mock_create_image,
mock_list_images):
def get_test_context(self):
context = super(GlanceBasicTestCase, self).get_test_context()
context.update({
"admin": {
"id": "fake_user_id",
"credential": mock.MagicMock()
},
"user": {
"id": "fake_user_id",
"credential": mock.MagicMock()
},
"tenant": {"id": "fake_tenant_id",
"name": "fake_tenant_name"}
})
return context
fake_image = fakes.FakeImage(id=1, name="img_name1")
mock_create_image.return_value = fake_image
mock_list_images.return_value = [
fakes.FakeImage(id=0, name="img_name1"),
def setUp(self):
super(GlanceBasicTestCase, self).setUp()
patch = mock.patch(
"rally.plugins.openstack.services.image.image.Image")
self.addCleanup(patch.stop)
self.mock_image = patch.start()
def test_create_and_list_image(self):
image_service = self.mock_image.return_value
fake_image = mock.Mock(id=1, name="img_2")
image_service.create_image.return_value = fake_image
image_service.list_images.return_value = [
mock.Mock(id=0, name="img_1"),
fake_image,
fakes.FakeImage(id=2, name="img_name1")
]
mock.Mock(id=2, name="img_3")]
call_args = {"container_format": "cf",
"image_location": "url",
"disk_format": "df",
"fakearg": "f"}
# Positive case
images.CreateAndListImage(self.context).run(
"cf", "url", "df", fakearg="f")
mock_create_image.assert_called_once_with(
"cf", "url", "df", fakearg="f")
mock_list_images.assert_called_once_with()
image_service.create_image.assert_called_once_with(**call_args)
# Negative case: image isn't created
mock_create_image.return_value = None
image_service.create_image.return_value = None
self.assertRaises(exceptions.RallyAssertionError,
images.CreateAndListImage(self.context).run,
"cf", "url", "df", fakearg="f")
mock_create_image.assert_called_with(
"cf", "url", "df", fakearg="f")
image_service.create_image.assert_called_with(**call_args)
# Negative case: created image n ot in the list of available images
mock_create_image.return_value = fakes.FakeImage(
image_service.create_image.return_value = mock.Mock(
id=12, name="img_nameN")
self.assertRaises(exceptions.RallyAssertionError,
images.CreateAndListImage(self.context).run,
"cf", "url", "df", fakearg="f")
mock_create_image.assert_called_with(
"cf", "url", "df", fakearg="f")
mock_list_images.assert_called_with()
image_service.create_image.assert_called_with(**call_args)
image_service.list_images.assert_called_with()
def test_list_images(self):
image_service = self.mock_image.return_value
@mock.patch("%s.ListImages._list_images" % BASE)
def test_list_images(self, mock_list_images__list_images):
images.ListImages(self.context).run()
mock_list_images__list_images.assert_called_once_with()
image_service.list_images.assert_called_once_with()
@mock.patch("%s.CreateAndDeleteImage._delete_image" % BASE)
@mock.patch("%s.CreateAndDeleteImage._create_image" % BASE)
@mock.patch("%s.CreateAndDeleteImage.generate_random_name" % BASE,
return_value="test-rally-image")
def test_create_and_delete_image(self,
mock_random_name,
mock_create_image,
mock_delete_image):
fake_image = object()
mock_create_image.return_value = fake_image
def test_create_and_delete_image(self):
image_service = self.mock_image.return_value
fake_image = fakes.FakeImage(id=1, name="imagexxx")
image_service.create_image.return_value = fake_image
call_args = {"container_format": "cf",
"image_location": "url",
"disk_format": "df",
"fakearg": "f"}
images.CreateAndDeleteImage(self.context).run(
"cf", "url", "df", fakearg="f")
mock_create_image.assert_called_once_with(
"cf", "url", "df", fakearg="f")
mock_delete_image.assert_called_once_with(fake_image)
image_service.create_image.assert_called_once_with(**call_args)
image_service.delete_image.assert_called_once_with(fake_image.id)
@mock.patch("%s.CreateImageAndBootInstances._boot_servers" % BASE)
@mock.patch("%s.CreateImageAndBootInstances._create_image" % BASE)
def test_create_image_and_boot_instances(self,
mock_create_image,
mock_boot_servers):
def test_create_image_and_boot_instances(self, mock_boot_servers):
image_service = self.mock_image.return_value
fake_image = fakes.FakeImage()
fake_servers = [mock.Mock() for i in range(5)]
mock_create_image.return_value = fake_image
image_service.create_image.return_value = fake_image
mock_boot_servers.return_value = fake_servers
create_image_kwargs = {"fakeimagearg": "f"}
boot_server_kwargs = {"fakeserverarg": "f"}
call_args = {"container_format": "cf",
"image_location": "url",
"disk_format": "df",
"fakeimagearg": "f"}
images.CreateImageAndBootInstances(self.context).run(
"cf", "url", "df", "fid", 5,
create_image_kwargs=create_image_kwargs,
boot_server_kwargs=boot_server_kwargs)
mock_create_image.assert_called_once_with("cf", "url", "df",
**create_image_kwargs)
image_service.create_image.assert_called_once_with(**call_args)
mock_boot_servers.assert_called_once_with("image-id-0", "fid",
5, **boot_server_kwargs)

View File

@ -0,0 +1,191 @@
# 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 tempfile
import ddt
from glanceclient import exc as glance_exc
import mock
from rally import exceptions
from rally.plugins.openstack.services.image import glance_v1
from rally.plugins.openstack.services.image import image
from tests.unit import test
from oslotest import mockpatch
PATH = "rally.plugins.openstack.services.image.image.Image._unify_image"
@ddt.ddt
class GlanceV1ServiceTestCase(test.TestCase):
_tempfile = tempfile.NamedTemporaryFile()
def setUp(self):
super(GlanceV1ServiceTestCase, self).setUp()
self.clients = mock.MagicMock()
self.gc = self.clients.glance.return_value
self.name_generator = mock.MagicMock()
self.service = glance_v1.GlanceV1Service(
self.clients, name_generator=self.name_generator)
self.mock_wait_for_status = mockpatch.Patch(
"rally.task.utils.wait_for_status")
self.useFixture(self.mock_wait_for_status)
@ddt.data({"location": "image_location", "is_public": True},
{"location": _tempfile.name, "is_public": False})
@ddt.unpack
@mock.patch("six.moves.builtins.open")
def test_create_image(self, mock_open, location, is_public):
image_name = "image_name"
container_format = "container_format"
disk_format = "disk_format"
image = self.service.create_image(
image_name=image_name,
container_format=container_format,
image_location=location,
disk_format=disk_format,
is_public=is_public)
call_args = {"container_format": container_format,
"disk_format": disk_format,
"is_public": is_public,
"name": image_name,
"min_disk": 0,
"min_ram": 0}
if location.startswith("/"):
call_args["data"] = mock_open.return_value
mock_open.assert_called_once_with(location)
mock_open.return_value.close.assert_called_once_with()
else:
call_args["copy_from"] = location
self.gc.images.create.assert_called_once_with(**call_args)
self.assertEqual(image, self.mock_wait_for_status.mock.return_value)
def test_get_image(self):
image_id = "image_id"
self.service.get_image(image_id)
self.gc.images.get.assert_called_once_with(image_id)
def test_get_image_exception(self):
image_id = "image_id"
self.clients.glance(
"1").images.get.side_effect = glance_exc.HTTPNotFound
self.assertRaises(exceptions.GetResourceNotFound,
self.service.get_image, image_id)
@ddt.data({"status": "activate", "is_public": True},
{"status": "activate", "is_public": False},
{"status": "activate", "is_public": None})
@ddt.unpack
def test_list_images(self, status, is_public):
self.service.list_images(is_public=is_public, status=status)
self.gc.images.list.assert_called_once_with(status=status)
def test_set_visibility(self):
image_id = "image_id"
is_public = True
self.service.set_visibility(image_id=image_id)
self.gc.images.update.assert_called_once_with(
image_id, is_public=is_public)
def test_delete_image(self):
image_id = "image_id"
self.service.delete_image(image_id)
self.gc.images.delete.assert_called_once_with(image_id)
@ddt.ddt
class UnifiedGlanceV1ServiceTestCase(test.TestCase):
def setUp(self):
super(UnifiedGlanceV1ServiceTestCase, self).setUp()
self.clients = mock.MagicMock()
self.service = glance_v1.UnifiedGlanceV1Service(self.clients)
self.service._impl = mock.MagicMock()
@ddt.data({"visibility": "public"},
{"visibility": "private"})
@ddt.unpack
@mock.patch(PATH)
def test_create_image(self, mock_image__unify_image, visibility):
image_name = "image_name"
container_format = "container_format"
image_location = "image_location"
disk_format = "disk_format"
image = self.service.create_image(image_name=image_name,
container_format=container_format,
image_location=image_location,
disk_format=disk_format,
visibility=visibility)
is_public = visibility == "public"
callargs = {"image_name": image_name,
"container_format": container_format,
"image_location": image_location,
"disk_format": disk_format,
"is_public": is_public,
"min_disk": 0,
"min_ram": 0}
self.service._impl.create_image.assert_called_once_with(**callargs)
self.assertEqual(mock_image__unify_image.return_value, image)
@mock.patch(PATH)
def test_get_image(self, mock_image__unify_image):
image_id = "image_id"
image = self.service.get_image(image_id=image_id)
self.assertEqual(mock_image__unify_image.return_value, image)
self.service._impl.get_image.assert_called_once_with(
image_id=image_id)
@mock.patch(PATH)
def test_list_images(self, mock_image__unify_image):
images = [mock.MagicMock()]
self.service._impl.list_images.return_value = images
status = "active"
visibility = "public"
is_public = visibility == "public"
self.assertEqual([mock_image__unify_image.return_value],
self.service.list_images(status,
visibility=visibility))
self.service._impl.list_images.assert_called_once_with(
status=status,
is_public=is_public)
def test_set_visibility(self):
image_id = "image_id"
visibility = "private"
is_public = visibility == "public"
self.service.set_visibility(image_id=image_id, visibility=visibility)
self.service._impl.set_visibility.assert_called_once_with(
image_id=image_id, is_public=is_public)
def test_set_visibility_failure(self):
image_id = "image_id"
visibility = "error"
self.assertRaises(image.VisibilityException,
self.service.set_visibility,
image_id=image_id,
visibility=visibility)
def test_delete_image(self):
image_id = "image_id"
self.service.delete_image(image_id)
self.service._impl.delete_image.assert_called_once_with(
image_id=image_id)

View File

@ -0,0 +1,178 @@
# 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 tempfile
import ddt
from glanceclient import exc as glance_exc
import mock
from rally import exceptions
from rally.plugins.openstack.services.image import glance_v2
from tests.unit import test
from oslotest import mockpatch
PATH = "rally.plugins.openstack.services.image.image.Image._unify_image"
@ddt.ddt
class GlanceV2ServiceTestCase(test.TestCase):
_tempfile = tempfile.NamedTemporaryFile()
def setUp(self):
super(GlanceV2ServiceTestCase, self).setUp()
self.clients = mock.MagicMock()
self.gc = self.clients.glance.return_value
self.name_generator = mock.MagicMock()
self.service = glance_v2.GlanceV2Service(
self.clients, name_generator=self.name_generator)
self.mock_wait_for_status = mockpatch.Patch(
"rally.task.utils.wait_for_status")
self.useFixture(self.mock_wait_for_status)
@ddt.data({"location": "image_location"},
{"location": _tempfile.name})
@ddt.unpack
@mock.patch("requests.get")
@mock.patch("six.moves.builtins.open")
def test_create_image(self, mock_open, mock_requests_get, location):
image_name = "image_name"
container_format = "container_format"
disk_format = "disk_format"
visibility = "public"
image = self.service.create_image(
image_name=image_name,
container_format=container_format,
image_location=location,
disk_format=disk_format,
visibility=visibility)
call_args = {"container_format": container_format,
"disk_format": disk_format,
"name": image_name,
"visibility": visibility,
"min_disk": 0,
"min_ram": 0}
if location.startswith("/"):
mock_open.assert_called_once_with(location)
mock_open.return_value.close.assert_called_once_with()
else:
mock_requests_get.assert_called_once_with(location, stream=True)
self.gc.images.create.assert_called_once_with(**call_args)
self.assertEqual(image, self.mock_wait_for_status.mock.return_value)
def test_get_image(self):
image_id = "image_id"
self.service.get_image(image_id)
self.gc.images.get.assert_called_once_with(image_id)
def test_get_image_exception(self):
image_id = "image_id"
self.clients.glance(
"1").images.get.side_effect = glance_exc.HTTPNotFound
self.assertRaises(exceptions.GetResourceNotFound,
self.service.get_image, image_id)
def test_list_images(self):
status = "active"
kwargs = {"status": status}
self.assertEqual(self.gc.images.list.return_value,
self.service.list_images())
self.gc.images.list.assert_called_once_with(**kwargs)
def test_set_visibility(self):
image_id = "image_id"
visibility = "shared"
self.service.set_visibility(image_id=image_id)
self.gc.images.update.assert_called_once_with(
image_id,
visibility=visibility)
def test_delete_image(self):
image_id = "image_id"
self.service.delete_image(image_id)
self.gc.images.delete.assert_called_once_with(image_id)
@ddt.ddt
class UnifiedGlanceV2ServiceTestCase(test.TestCase):
def setUp(self):
super(UnifiedGlanceV2ServiceTestCase, self).setUp()
self.clients = mock.MagicMock()
self.service = glance_v2.UnifiedGlanceV2Service(self.clients)
self.service._impl = mock.MagicMock()
@mock.patch(PATH)
def test_create_image(self, mock_image__unify_image):
image_name = "image_name"
container_format = "container_format"
image_location = "image_location"
disk_format = "disk_format"
visibility = "public"
callargs = {"image_name": image_name,
"container_format": container_format,
"image_location": image_location,
"disk_format": disk_format,
"visibility": visibility,
"min_disk": 0,
"min_ram": 0}
image = self.service.create_image(image_name=image_name,
container_format=container_format,
image_location=image_location,
disk_format=disk_format,
visibility=visibility)
self.assertEqual(mock_image__unify_image.return_value, image)
self.service._impl.create_image.assert_called_once_with(**callargs)
@mock.patch(PATH)
def test_get_image(self, mock_image__unify_image):
image_id = "image_id"
image = self.service.get_image(image_id=image_id)
self.assertEqual(mock_image__unify_image.return_value, image)
self.service._impl.get_image.assert_called_once_with(
image_id=image_id)
@mock.patch(PATH)
def test_list_images(self, mock_image__unify_image):
images = [mock.MagicMock()]
self.service._impl.list_images.return_value = images
status = "active"
self.assertEqual([mock_image__unify_image.return_value],
self.service.list_images())
self.service._impl.list_images.assert_called_once_with(
status=status,
visibility=None)
def test_set_visibility(self):
image_id = "image_id"
visibility = "private"
self.service.set_visibility(image_id=image_id, visibility=visibility)
self.service._impl.set_visibility.assert_called_once_with(
image_id=image_id, visibility=visibility)
def test_delete_image(self):
image_id = "image_id"
self.service.delete_image(image_id)
self.service._impl.delete_image.assert_called_once_with(
image_id=image_id)

View File

@ -0,0 +1,118 @@
# 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 uuid
import ddt
import mock
from rally.plugins.openstack.services.image import glance_v1
from rally.plugins.openstack.services.image import glance_v2
from rally.plugins.openstack.services.image import image
from tests.unit import test
@ddt.ddt
class ImageTestCase(test.TestCase):
def setUp(self):
super(ImageTestCase, self).setUp()
self.clients = mock.MagicMock()
def get_service_with_fake_impl(self):
path = "rally.plugins.openstack.services.image.image"
with mock.patch("%s.Image.discover_impl" % path) as mock_discover:
mock_discover.return_value = mock.MagicMock(), None
service = image.Image(self.clients)
return service
@ddt.data(("image_name", "container_format", "image_location",
"disk_format", "visibility", "min_disk", "min_ram"))
def test_create_image(self, params):
(image_name, container_format, image_location, disk_format,
visibility, min_disk, min_ram) = params
service = self.get_service_with_fake_impl()
service.create_image(image_name=image_name,
container_format=container_format,
image_location=image_location,
disk_format=disk_format,
visibility=visibility,
min_disk=min_disk,
min_ram=min_ram)
service._impl.create_image.assert_called_once_with(
image_name=image_name, container_format=container_format,
image_location=image_location, disk_format=disk_format,
visibility=visibility, min_disk=min_disk, min_ram=min_ram)
@ddt.data("image_id")
def test_get_image(self, param):
image_id = param
service = self.get_service_with_fake_impl()
service.get_image(image=image_id)
service._impl.get_image.assert_called_once_with(image_id)
@ddt.data(("status", "visibility"))
def test_list_images(self, params):
status, visibility = params
service = self.get_service_with_fake_impl()
service.list_images(status=status, visibility=visibility)
service._impl.list_images.assert_called_once_with(
status=status, visibility=visibility)
@ddt.data(("image_id", "visibility"))
def test_set_visibility(self, params):
image_id, visibility = params
service = self.get_service_with_fake_impl()
service.set_visibility(image_id=image_id, visibility=visibility)
service._impl.set_visibility.assert_called_once_with(
image_id, visibility=visibility)
def test_unify_image(self):
class Image(object):
def __init__(self, visibility=None, is_public=None):
self.id = uuid.uuid4()
self.name = str(uuid.uuid4())
self.visibility = visibility
self.is_public = is_public
service = self.get_service_with_fake_impl()
visibility = "private"
image_obj = Image(visibility=visibility)
unified_image = service._unify_image(image_obj)
self.assertIsInstance(unified_image, image.UnifiedImage)
self.assertEqual(image_obj.id, unified_image.id)
self.assertEqual(image_obj.visibility, unified_image.visibility)
image_obj = Image(is_public="public")
del image_obj.visibility
unified_image = service._unify_image(image_obj)
self.assertEqual(image_obj.id, unified_image.id)
self.assertEqual(image_obj.is_public, unified_image.visibility)
def test_delete_image(self):
image_id = "image_id"
service = self.get_service_with_fake_impl()
service.delete_image(image_id=image_id)
service._impl.delete_image.assert_called_once_with(image_id)
def test_is_applicable(self):
clients = mock.Mock()
clients.glance().version = "1.0"
self.assertTrue(
glance_v1.UnifiedGlanceV1Service.is_applicable(clients))
clients.glance().version = "2.0"
self.assertTrue(
glance_v2.UnifiedGlanceV2Service.is_applicable(clients))