Add benchmark tests for glance

The patch adds the following benchmark tests for glance:
* Add and delete image
* Boot the several instances from added image

Also some changes are added related with using
fake Glance client the for following unit tests:
* test_generic_cleanup in tests/benchmark/test_runner.py
* test_snapshot_server in tests/benchmark/scenarios/nova/test_servers.py

Implements: blueprint benchmark-scenarios
Change-Id: I31471a91455bd20eea000a66c59320178455cb50
This commit is contained in:
Sergey Novikov 2014-01-31 20:30:08 +04:00 committed by Boris Pavlovic
parent 7bd24f74e7
commit 23971d6b7f
11 changed files with 344 additions and 10 deletions

View File

@ -0,0 +1,11 @@
{
"GlanceImages.create_and_delete_image": [
{"args": {"image_url": "http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-disk.img",
"container_format": "bare",
"disk_format": "qcow2"},
"execution": "continuous",
"config": {"times": 1, "active_users": 1,
"tenants": 1, "users_per_tenant": 1}}
]
}

View File

@ -0,0 +1,12 @@
{
"GlanceImages.create_image_and_boot_instances": [
{"args": {"image_url": "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,
"number_instances": 2},
"execution": "continuous",
"config": {"times": 1, "active_users": 1,
"tenants": 1, "users_per_tenant": 1}}
]
}

View File

@ -0,0 +1,48 @@
# 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.scenarios.glance import utils
from rally.benchmark.scenarios.nova import utils as nova_utils
class GlanceImages(utils.GlanceScenario, nova_utils.NovaScenario):
def create_and_delete_image(self, container_format,
image_url, disk_format, **kwargs):
"""Test adds and then deletes image."""
image_name = self._generate_random_name(16)
image = self._create_image(image_name,
container_format,
image_url,
disk_format,
**kwargs)
self._delete_image(image)
def create_image_and_boot_instances(self, container_format,
image_url, disk_format,
flavor_id, number_instances,
**kwargs):
"""Test adds image, boots instance from it and then deletes them."""
image_name = self._generate_random_name(16)
image = self._create_image(image_name,
container_format,
image_url,
disk_format,
**kwargs)
image_id = image.id
server_name = self._generate_random_name(16)
self._boot_servers(server_name, image_id,
flavor_id, number_instances, **kwargs)

View File

@ -0,0 +1,68 @@
# 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.
import random
import string
import time
from rally.benchmark import base
from rally.benchmark import utils as bench_utils
from rally import utils
class GlanceScenario(base.Scenario):
def _create_image(self, image_name, container_format,
image_url, disk_format, **kwargs):
"""Create a new image.
:param image_name: String used to name the image
:param container_format: Container format of image.
Acceptable formats: ami, ari, aki, bare, and ovf.
:param image_url: URL for download image
:param disk_format: Disk format of image. Acceptable formats:
ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, and iso.
:param **kwargs: optional parameters to create image
returns: object of image
"""
image = self.clients("glance").images.create(
name=image_name,
copy_from=image_url,
container_format=container_format,
disk_format=disk_format,
**kwargs)
time.sleep(5)
image = utils.wait_for(image,
is_ready=bench_utils.resource_is("active"),
update_resource=bench_utils.get_from_manager(),
timeout=120, check_interval=3)
return image
def _delete_image(self, image):
"""Deletes the given image.
Returns when the image is actually deleted.
:param image: Image object
"""
image.delete()
utils.wait_for_delete(image,
update_resource=bench_utils.get_from_manager(),
timeout=120, check_interval=3)
def _generate_random_name(self, length):
name = ''.join(random.choice(string.lowercase) for i in range(length))
return 'test-rally-image' + name

View File

@ -0,0 +1,80 @@
# 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.
import mock
from rally.benchmark.scenarios.glance import images
from rally.benchmark.scenarios.nova import servers
from rally.benchmark import utils as butils
from tests import fakes
from tests import test
GLANCE_IMAGES = "rally.benchmark.scenarios.glance.images.GlanceImages"
class GlanceImagesTestCase(test.TestCase):
@mock.patch(GLANCE_IMAGES + "._generate_random_name")
@mock.patch(GLANCE_IMAGES + "._delete_image")
@mock.patch(GLANCE_IMAGES + "._create_image")
def test_create_and_delete_image(self, mock_create, mock_delete,
mock_random_name):
glance_scenario = images.GlanceImages()
fake_image = object()
mock_create.return_value = fake_image
mock_random_name.return_value = "test-rally-image"
glance_scenario.create_and_delete_image("cf", "url", "df",
fakearg="f")
mock_create.assert_called_once_with("test-rally-image", "cf",
"url", "df", fakearg="f")
mock_delete.assert_called_once_with(fake_image)
@mock.patch(GLANCE_IMAGES + "._generate_random_name")
@mock.patch(GLANCE_IMAGES + "._boot_servers")
@mock.patch(GLANCE_IMAGES + "._create_image")
@mock.patch("rally.benchmark.utils.osclients")
def test_create_image_and_boot_instances(self,
mock_osclients,
mock_create_image,
mock_boot_servers,
mock_random_name):
glance_scenario = images.GlanceImages()
nova_scenario = servers.NovaServers()
fc = fakes.FakeClients()
mock_osclients.Clients.return_value = fc
fake_glance = fakes.FakeGlanceClient()
fc.get_glance_client = lambda: fake_glance
fake_nova = fakes.FakeNovaClient()
fc.get_nova_client = lambda: fake_nova
temp_keys = ["username", "password", "tenant_name", "uri"]
users_endpoints = [dict(zip(temp_keys, temp_keys))]
nova_scenario._clients = butils.create_openstack_clients(
users_endpoints, temp_keys)[0]
fake_image = fakes.FakeImage()
fake_servers = [object() for i in range(5)]
mock_create_image.return_value = fake_image
mock_boot_servers.return_value = fake_servers
mock_random_name.return_value = "random_name"
kwargs = {'fakearg': 'f'}
with mock.patch("rally.benchmark.scenarios.glance.utils.time.sleep"):
glance_scenario.\
create_image_and_boot_instances("cf", "url", "df",
"fid", 5, **kwargs)
mock_create_image.assert_called_once_with("random_name", "cf",
"url", "df", **kwargs)
mock_boot_servers.assert_called_once_with("random_name",
"image-id-0",
"fid", 5, **kwargs)

View File

@ -0,0 +1,81 @@
# 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 mock
from rally.benchmark.scenarios.glance import utils
from rally.benchmark import utils as butils
from rally import exceptions as rally_exceptions
from rally.openstack.common.fixture import mockpatch
from tests import fakes
from tests import test
BM_UTILS = 'rally.benchmark.utils'
GLANCE_UTILS = 'rally.benchmark.scenarios.glance.utils'
class GlanceScenarioTestCase(test.TestCase):
def setUp(self):
super(GlanceScenarioTestCase, self).setUp()
self.image = mock.Mock()
self.image1 = mock.Mock()
self.res_is = mockpatch.Patch(BM_UTILS + ".resource_is")
self.get_fm = mockpatch.Patch(BM_UTILS + '.get_from_manager')
self.wait_for = mockpatch.Patch(GLANCE_UTILS + ".utils.wait_for")
self.wait_for_delete = mockpatch.Patch(
GLANCE_UTILS + ".utils.wait_for_delete")
self.useFixture(self.wait_for)
self.useFixture(self.wait_for_delete)
self.useFixture(self.res_is)
self.useFixture(self.get_fm)
self.gfm = self.get_fm.mock
self.useFixture(mockpatch.Patch('time.sleep'))
self.scenario = utils.GlanceScenario()
def test_generate_random_name(self):
for length in [8, 16, 32, 64]:
name = self.scenario._generate_random_name(length)
self.assertEqual(len(name), 16 + length)
def test_failed_image_status(self):
self.get_fm.cleanUp()
image_manager = fakes.FakeFailedImageManager()
self.assertRaises(rally_exceptions.GetResourceFailure,
butils.get_from_manager(),
image_manager.create('fails', 'url', 'cf', 'df'))
@mock.patch(GLANCE_UTILS + '.GlanceScenario.clients')
def test__create_image(self, mock_clients):
mock_clients("glance").images.create.return_value = self.image
return_image = utils.GlanceScenario()._create_image('image_name',
'image_url',
'container_format',
'disk_format')
self.wait_for.mock.assert_called_once_with(self.image,
update_resource=self.gfm(),
is_ready=self.res_is.mock(),
check_interval=3,
timeout=120)
self.res_is.mock.assert_has_calls(mock.call('active'))
self.assertEqual(self.wait_for.mock(), return_image)
def test__delete_image(self):
utils.GlanceScenario()._delete_image(self.image)
self.image.delete.assert_called_once_with()
self.wait_for_delete.\
mock.assert_called_once_with(self.image,
update_resource=self.gfm(),
check_interval=3,
timeout=120)

View File

@ -319,7 +319,7 @@ class NovaServersTestCase(test.TestCase):
def test_snapshot_server(self):
fake_server = object()
fake_image = fakes.FakeImageManager().create()
fake_image = fakes.FakeImageManager()._create()
fake_image.id = "image_id"
scenario = servers.NovaServers()

View File

@ -378,9 +378,10 @@ class ResourceCleanerTestCase(test.TestCase):
client = clients[index]
nova = client["nova"]
cinder = client["cinder"]
glance = client["glance"]
for count in range(3):
uid = index + count
img = nova.images.create()
img = glance.images._create()
nova.servers.create("svr-%s" % (uid), img.uuid, index)
nova.keypairs.create("keypair-%s" % (uid))
nova.security_groups.create("secgroup-%s" % (uid))
@ -402,6 +403,7 @@ class ResourceCleanerTestCase(test.TestCase):
for client in clients:
nova = client["nova"]
cinder = client["cinder"]
glance = client["glance"]
_assert_purged(nova.servers, "servers")
_assert_purged(nova.keypairs, "key pairs")
_assert_purged(nova.security_groups, "security groups")
@ -413,7 +415,7 @@ class ResourceCleanerTestCase(test.TestCase):
_assert_purged(cinder.transfers, "volume transfers")
_assert_purged(cinder.volume_snapshots, "volume snapshots")
for image in nova.images.list():
for image in glance.images.list():
self.assertEqual("DELETED", image.status,
"image not purged: %s" % (image))

View File

@ -15,6 +15,7 @@
import uuid
from glanceclient import exc
from novaclient import exceptions
from rally.benchmark import base
from rally import utils as rally_utils
@ -56,6 +57,14 @@ class FakeImage(FakeResource):
def __init__(self, manager=None):
super(FakeImage, self).__init__(manager)
self.id = "image-id-0"
class FakeFailedImage(FakeResource):
def __init__(self, manager=None):
super(FakeFailedImage, self).__init__(manager)
self.status = "error"
class FakeFloatingIP(FakeResource):
@ -174,7 +183,7 @@ class FakeServerManager(FakeManager):
return self._create(name=name)
def create_image(self, server, name):
image = self.images.create()
image = self.images._create()
return image.uuid
def add_floating_ip(self, server, fip):
@ -192,8 +201,29 @@ class FakeFailedServerManager(FakeServerManager):
class FakeImageManager(FakeManager):
def create(self):
return self._cache(FakeImage(self))
def __init__(self):
super(FakeImageManager, self).__init__()
def get(self, resource_uuid):
image = self.cache.get(resource_uuid, None)
if image is not None:
return image
raise exc.HTTPNotFound("Image %s not found" % (resource_uuid))
def _create(self, image_class=FakeImage, name=None):
image = self._cache(image_class(self))
if name is not None:
image.name = name
return image
def create(self, name, copy_from, container_format, disk_format):
return self._create(name=name)
class FakeFailedImageManager(FakeImageManager):
def create(self, name, copy_from, container_format, disk_format):
return self._create(FakeFailedImage, name)
class FakeFloatingIPsManager(FakeManager):
@ -309,9 +339,11 @@ class FakeServiceCatalog(object):
class FakeGlanceClient(object):
def __init__(self, nova_client=None):
if nova_client:
self.images = nova_client.images
def __init__(self, failed_image_manager=False):
if failed_image_manager:
self.images = FakeFailedImageManager()
else:
self.images = FakeImageManager()
class FakeCinderClient(object):
@ -383,7 +415,7 @@ class FakeClients(object):
def get_glance_client(self):
if self.glance is not None:
return self.glance
self.glance = FakeGlanceClient(self.get_nova_client())
self.glance = FakeGlanceClient()
return self.glance
def get_cinder_client(self):