Merge "Ephemeral volume support"

This commit is contained in:
Jenkins 2013-05-23 23:26:24 +00:00 committed by Gerrit Code Review
commit ce0d39fd2a
20 changed files with 351 additions and 127 deletions

View File

@ -101,6 +101,18 @@
"id": 10,
"name": "m1.rd-tiny",
"ram": 512
},
{
"id": 11,
"name": "eph.rd-tiny",
"ram": 512,
"local_storage": 1
},
{
"id": 12,
"name": "eph.rd-smaller",
"ram": 768,
"local_storage": 2
}
],

View File

@ -157,6 +157,26 @@ class VolumeCreationFailure(ReddwarfError):
message = _("Failed to create a volume in Nova.")
class VolumeSizeNotSpecified(BadRequest):
message = _("Volume size was not specified.")
class LocalStorageNotSpecified(BadRequest):
message = _("Local storage not specified in flavor ID: %(flavor)s.")
class LocalStorageNotSupported(ReddwarfError):
message = _("Local storage support is not enabled.")
class VolumeNotSupported(ReddwarfError):
message = _("Volume support is not enabled.")
class TaskManagerError(ReddwarfError):
message = _("An error occurred communicating with the task manager: "

View File

@ -376,6 +376,7 @@ class Controller(object):
exception.BadValue,
exception.DatabaseAlreadyExists,
exception.UserAlreadyExists,
exception.LocalStorageNotSpecified,
],
webob.exc.HTTPNotFound: [
exception.NotFound,
@ -398,6 +399,10 @@ class Controller(object):
exception.VolumeCreationFailure,
exception.UpdateGuestError,
],
webob.exc.HTTPNotImplemented: [
exception.VolumeNotSupported,
exception.LocalStorageNotSupported
],
}
def create_resource(self):

View File

@ -68,6 +68,8 @@ class InstanceStatus(object):
def validate_volume_size(size):
if size is None:
raise exception.VolumeSizeNotSpecified()
max_size = CONF.max_accepted_volume_size
if long(size) > max_size:
msg = ("Volume 'size' cannot exceed maximum "
@ -336,9 +338,11 @@ class BaseInstance(SimpleInstance):
self.update_db(task_status=InstanceTasks.DELETING)
task_api.API(self.context).delete_instance(self.id)
deltas = {'instances': -1}
if CONF.reddwarf_volume_support:
deltas['volumes'] = -self.volume_size
return run_with_quotas(self.tenant_id,
{'instances': -1,
'volumes': -self.volume_size},
deltas,
_delete_resources)
def _delete_resources(self):
@ -415,13 +419,25 @@ class Instance(BuiltInstance):
def create(cls, context, name, flavor_id, image_id,
databases, users, service_type, volume_size, backup_id):
client = create_nova_client(context)
try:
flavor = client.flavors.get(flavor_id)
except nova_exceptions.NotFound:
raise exception.FlavorNotFound(uuid=flavor_id)
deltas = {'instances': 1}
if CONF.reddwarf_volume_support:
validate_volume_size(volume_size)
deltas['volumes'] = volume_size
else:
if volume_size is not None:
raise exception.VolumeNotSupported()
ephemeral_support = CONF.device_path
if ephemeral_support and flavor.ephemeral == 0:
raise exception.LocalStorageNotSpecified(flavor=flavor_id)
def _create_resources():
client = create_nova_client(context)
security_groups = None
try:
flavor = client.flavors.get(flavor_id)
except nova_exceptions.NotFound:
raise exception.FlavorNotFound(uuid=flavor_id)
if backup_id is not None:
backup_info = Backup.get_by_id(backup_id)
@ -464,9 +480,8 @@ class Instance(BuiltInstance):
return SimpleInstance(context, db_info, service_status)
validate_volume_size(volume_size)
return run_with_quotas(context.tenant,
{'instances': 1, 'volumes': volume_size},
deltas,
_create_resources)
def resize_flavor(self, new_flavor_id):
@ -483,8 +498,18 @@ class Instance(BuiltInstance):
old_flavor = client.flavors.get(self.flavor_id)
new_flavor_size = new_flavor.ram
old_flavor_size = old_flavor.ram
if new_flavor_size == old_flavor_size:
raise exception.CannotResizeToSameSize()
if CONF.reddwarf_volume_support:
if new_flavor.ephemeral != 0:
raise exception.LocalStorageNotSupported()
if new_flavor_size == old_flavor_size:
raise exception.CannotResizeToSameSize()
elif CONF.device_path is not None:
# ephemeral support enabled
if new_flavor.ephemeral == 0:
raise exception.LocalStorageNotSpecified(flavor=new_flavor_id)
if (new_flavor_size == old_flavor_size and
new_flavor.ephemeral == new_flavor.ephemeral):
raise exception.CannotResizeToSameSize()
# Set the task to RESIZING and begin the async call before returning.
self.update_db(task_status=InstanceTasks.RESIZING)

View File

@ -196,7 +196,7 @@ class InstanceController(wsgi.Controller):
users = populate_users(body['instance'].get('users', []))
except ValueError as ve:
raise exception.BadRequest(msg=ve)
if body['instance'].get('volume', None) is not None:
if 'volume' in body['instance']:
try:
volume_size = int(body['instance']['volume']['size'])
except ValueError as e:

View File

@ -50,8 +50,8 @@ class LimitViews(object):
data = []
abs_view = dict()
abs_view["verb"] = "ABSOLUTE"
abs_view["maxTotalInstances"] = self.abs_limits.get("instances", 0)
abs_view["maxTotalVolumes"] = self.abs_limits.get("volumes", 0)
for resource_name, abs_limit in self.abs_limits.items():
abs_view["max_" + resource_name] = abs_limit
data.append(abs_view)
for l in self.rate_limits:

View File

@ -310,8 +310,9 @@ QUOTAS = QuotaEngine()
''' Define all kind of resources here '''
resources = [Resource(Resource.INSTANCES, 'max_instances_per_user'),
Resource(Resource.VOLUMES, 'max_volumes_per_user'),
Resource(Resource.BACKUPS, 'max_backups_per_user')]
if CONF.reddwarf_volume_support:
resources.append(Resource(Resource.VOLUMES, 'max_volumes_per_user'))
QUOTAS.register_resources(resources)

View File

@ -219,17 +219,9 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin):
def _create_server_volume_individually(self, flavor_id, image_id,
security_groups, service_type,
volume_size):
volume_info = None
block_device_mapping = None
server = None
try:
volume_info = self._create_volume(volume_size)
block_device_mapping = volume_info['block_device']
except Exception as e:
msg = "Error provisioning volume for instance."
err = inst_models.InstanceTasks.BUILDING_ERROR_VOLUME
self._log_and_raise(e, msg, err)
volume_info = self._build_volume_info(volume_size)
block_device_mapping = volume_info['block_device']
try:
server = self._create_server(flavor_id, image_id, security_groups,
service_type, block_device_mapping)
@ -242,6 +234,28 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin):
self._log_and_raise(e, msg, err)
return server, volume_info
def _build_volume_info(self, volume_size=None):
volume_info = None
volume_support = CONF.reddwarf_volume_support
LOG.debug(_("reddwarf volume support = %s") % volume_support)
if volume_support:
try:
volume_info = self._create_volume(volume_size)
except Exception as e:
msg = "Error provisioning volume for instance."
err = inst_models.InstanceTasks.BUILDING_ERROR_VOLUME
self._log_and_raise(e, msg, err)
else:
LOG.debug(_("device_path = %s") % CONF.device_path)
LOG.debug(_("mount_point = %s") % CONF.mount_point)
volume_info = {
'block_device': None,
'device_path': CONF.device_path,
'mount_point': CONF.mount_point,
'volumes': None,
}
return volume_info
def _log_and_raise(self, exc, message, task_status):
LOG.error(message)
LOG.error(exc)
@ -253,18 +267,6 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin):
LOG.info("Entering create_volume")
LOG.debug(_("Starting to create the volume for the instance"))
volume_support = CONF.reddwarf_volume_support
LOG.debug(_("reddwarf volume support = %s") % volume_support)
if (volume_size is None or
volume_support is False):
volume_info = {
'block_device': None,
'device_path': None,
'mount_point': None,
'volumes': None,
}
return volume_info
volume_client = create_nova_volume_client(self.context)
volume_desc = ("mysql volume for %s" % self.id)
volume_ref = volume_client.volumes.create(

View File

@ -67,7 +67,7 @@ class AfterBackupCreation(object):
@test
def test_instance_action_right_after_backup_create(self):
"""test any instance action while backup is running"""
assert_unprocessable(instance_info.dbaas.instances.resize_volume,
assert_unprocessable(instance_info.dbaas.instances.resize_instance,
instance_info.id, 1)
@test

View File

@ -114,6 +114,9 @@ class InstanceTestInfo(object):
instance_info = InstanceTestInfo()
dbaas = None # Rich client used throughout this test.
dbaas_admin = None # Same as above, with admin privs.
VOLUME_SUPPORT = CONFIG.get('reddwarf_volume_support', False)
EPHEMERAL_SUPPORT = not VOLUME_SUPPORT and CONFIG.get('device_path',
'/dev/vdb') is not None
# This is like a cheat code which allows the tests to skip creating a new
@ -186,7 +189,11 @@ class InstanceSetup(object):
@test
def test_find_flavor(self):
flavor_name = CONFIG.values.get('instance_flavor_name', 'm1.tiny')
if EPHEMERAL_SUPPORT:
flavor_name = CONFIG.values.get('instance_eph_flavor_name',
'eph.rd-tiny')
else:
flavor_name = CONFIG.values.get('instance_flavor_name', 'm1.tiny')
flavors = dbaas.find_flavors_by_name(flavor_name)
assert_equal(len(flavors), 1, "Number of flavors with name '%s' "
"found was '%d'." % (flavor_name, len(flavors)))
@ -245,14 +252,15 @@ class CreateInstanceQuotaTest(unittest.TestCase):
self.test_info = copy.deepcopy(instance_info)
def tearDown(self):
quota_dict = {'instances': CONFIG.reddwarf_max_instances_per_user,
'volumes': CONFIG.reddwarf_max_volumes_per_user}
quota_dict = {'instances': CONFIG.reddwarf_max_instances_per_user}
if VOLUME_SUPPORT:
quota_dict['volumes'] = CONFIG.reddwarf_max_volumes_per_user
dbaas_admin.quota.update(self.test_info.user.tenant_id,
quota_dict)
def test_instance_size_too_big(self):
vol_ok = CONFIG.get('reddwarf_volume_support', False)
if 'reddwarf_max_accepted_volume_size' in CONFIG.values and vol_ok:
if ('reddwarf_max_accepted_volume_size' in CONFIG.values and
VOLUME_SUPPORT):
too_big = CONFIG.reddwarf_max_accepted_volume_size
self.test_info.volume = {'size': too_big + 1}
@ -263,6 +271,18 @@ class CreateInstanceQuotaTest(unittest.TestCase):
self.test_info.dbaas_flavor_href,
self.test_info.volume)
def test_update_quota_invalid_resource_should_fail(self):
quota_dict = {'invalid_resource': 100}
assert_raises(exceptions.NotFound, dbaas_admin.quota.update,
self.test_info.user.tenant_id, quota_dict)
def test_update_quota_volume_should_fail_volume_not_supported(self):
if VOLUME_SUPPORT:
raise SkipTest("Volume support needs to be disabled")
quota_dict = {'volumes': 100}
assert_raises(exceptions.NotFound, dbaas_admin.quota.update,
self.test_info.user.tenant_id, quota_dict)
def test_create_too_many_instances(self):
instance_quota = 0
quota_dict = {'instances': instance_quota}
@ -273,10 +293,13 @@ class CreateInstanceQuotaTest(unittest.TestCase):
assert_equal(new_quotas['instances'], quota_dict['instances'])
assert_equal(0, verify_quota['instances'])
assert_equal(CONFIG.reddwarf_max_volumes_per_user,
verify_quota['volumes'])
self.test_info.volume = None
if VOLUME_SUPPORT:
assert_equal(CONFIG.reddwarf_max_volumes_per_user,
verify_quota['volumes'])
self.test_info.volume = {'size': 1}
self.test_info.volume = {'size': 1}
self.test_info.name = "too_many_instances"
assert_raises(exceptions.OverLimit,
dbaas.instances.create,
@ -287,6 +310,8 @@ class CreateInstanceQuotaTest(unittest.TestCase):
assert_equal(413, dbaas.last_http_code)
def test_create_instances_total_volume_exceeded(self):
if not VOLUME_SUPPORT:
raise SkipTest("Volume support not enabled")
volume_quota = 3
quota_dict = {'volumes': volume_quota}
self.test_info.volume = {'size': volume_quota + 1}
@ -307,13 +332,14 @@ class CreateInstanceQuotaTest(unittest.TestCase):
@test(depends_on_classes=[InstanceSetup],
groups=[GROUP, GROUP_START, GROUP_START_SIMPLE, tests.INSTANCES],
runs_after_groups=[tests.PRE_INSTANCES, 'dbaas_quotas'])
class CreateInstance(unittest.TestCase):
class CreateInstance(object):
"""Test to create a Database Instance
If the call returns without raising an exception this test passes.
"""
@test
def test_create(self):
databases = []
databases.append({"name": "firstdb", "character_set": "latin2",
@ -324,7 +350,7 @@ class CreateInstance(unittest.TestCase):
users.append({"name": "lite", "password": "litepass",
"databases": [{"name": "firstdb"}]})
instance_info.users = users
if CONFIG.values['reddwarf_volume_support']:
if VOLUME_SUPPORT:
instance_info.volume = {'size': 1}
else:
instance_info.volume = None
@ -361,7 +387,7 @@ class CreateInstance(unittest.TestCase):
# Check these attrs only are returned in create response
expected_attrs = ['created', 'flavor', 'addresses', 'id', 'links',
'name', 'status', 'updated']
if CONFIG.values['reddwarf_volume_support']:
if VOLUME_SUPPORT:
expected_attrs.append('volume')
if CONFIG.reddwarf_dns_support:
expected_attrs.append('hostname')
@ -373,31 +399,52 @@ class CreateInstance(unittest.TestCase):
# Don't CheckInstance if the instance already exists.
check.flavor()
check.links(result._info['links'])
if CONFIG.values['reddwarf_volume_support']:
if VOLUME_SUPPORT:
check.volume()
@test(enabled=VOLUME_SUPPORT)
def test_create_failure_with_empty_volume(self):
if CONFIG.values['reddwarf_volume_support']:
instance_name = "instance-failure-with-no-volume-size"
databases = []
volume = {}
assert_raises(exceptions.BadRequest, dbaas.instances.create,
instance_name, instance_info.dbaas_flavor_href,
volume, databases)
assert_equal(400, dbaas.last_http_code)
instance_name = "instance-failure-with-no-volume-size"
databases = []
volume = {}
assert_raises(exceptions.BadRequest, dbaas.instances.create,
instance_name, instance_info.dbaas_flavor_href,
volume, databases)
assert_equal(400, dbaas.last_http_code)
@test(enabled=VOLUME_SUPPORT)
def test_create_failure_with_no_volume_size(self):
if CONFIG.values['reddwarf_volume_support']:
instance_name = "instance-failure-with-no-volume-size"
databases = []
volume = {'size': None}
assert_raises(exceptions.BadRequest, dbaas.instances.create,
instance_name, instance_info.dbaas_flavor_href,
volume, databases)
assert_equal(400, dbaas.last_http_code)
instance_name = "instance-failure-with-no-volume-size"
databases = []
volume = {'size': None}
assert_raises(exceptions.BadRequest, dbaas.instances.create,
instance_name, instance_info.dbaas_flavor_href,
volume, databases)
assert_equal(400, dbaas.last_http_code)
@test(enabled=not VOLUME_SUPPORT)
def test_create_failure_with_volume_size_and_volume_disabled(self):
instance_name = "instance-failure-volume-size_and_volume_disabled"
databases = []
volume = {'size': 2}
assert_raises(exceptions.HTTPNotImplemented, dbaas.instances.create,
instance_name, instance_info.dbaas_flavor_href,
volume, databases)
assert_equal(501, dbaas.last_http_code)
@test(enabled=EPHEMERAL_SUPPORT)
def test_create_failure_with_no_ephemeral_flavor(self):
instance_name = "instance-failure-with-no-ephemeral-flavor"
databases = []
flavor_name = CONFIG.values.get('instance_flavor_name', 'm1.tiny')
flavors = dbaas.find_flavors_by_name(flavor_name)
assert_raises(exceptions.BadRequest, dbaas.instances.create,
instance_name, flavors[0].id, None, databases)
assert_equal(400, dbaas.last_http_code)
@test
def test_create_failure_with_no_name(self):
if CONFIG.values['reddwarf_volume_support']:
if VOLUME_SUPPORT:
volume = {'size': 1}
else:
volume = None
@ -408,8 +455,9 @@ class CreateInstance(unittest.TestCase):
volume, databases)
assert_equal(400, dbaas.last_http_code)
@test
def test_create_failure_with_spaces_for_name(self):
if CONFIG.values['reddwarf_volume_support']:
if VOLUME_SUPPORT:
volume = {'size': 1}
else:
volume = None
@ -420,6 +468,7 @@ class CreateInstance(unittest.TestCase):
volume, databases)
assert_equal(400, dbaas.last_http_code)
@test
def test_mgmt_get_instance_on_create(self):
if CONFIG.test_mgmt:
result = dbaas_admin.management.show(instance_info.id)
@ -777,7 +826,9 @@ class TestInstanceListing(object):
@test
def test_index_list(self):
expected_attrs = ['id', 'links', 'name', 'status', 'flavor', 'volume']
expected_attrs = ['id', 'links', 'name', 'status', 'flavor']
if VOLUME_SUPPORT:
expected_attrs.append('volume')
instances = dbaas.instances.list()
assert_equal(200, dbaas.last_http_code)
for instance in instances:
@ -793,7 +844,9 @@ class TestInstanceListing(object):
@test
def test_get_instance(self):
expected_attrs = ['created', 'databases', 'flavor', 'hostname', 'id',
'links', 'name', 'status', 'updated', 'volume', 'ip']
'links', 'name', 'status', 'updated', 'ip']
if VOLUME_SUPPORT:
expected_attrs.append('volume')
instance = dbaas.instances.get(instance_info.id)
assert_equal(200, dbaas.last_http_code)
instance_dict = instance._info
@ -829,7 +882,7 @@ class TestInstanceListing(object):
def test_get_legacy_status_notfound(self):
assert_raises(exceptions.NotFound, dbaas.instances.get, -2)
@test(enabled=CONFIG.values["reddwarf_volume_support"])
@test(enabled=VOLUME_SUPPORT)
def test_volume_found(self):
instance = dbaas.instances.get(instance_info.id)
if create_new_instance():
@ -960,7 +1013,7 @@ class DeleteInstance(object):
" time: %s" % (str(instance_info.id), attempts, str(ex)))
@time_out(30)
@test(enabled=CONFIG.values["reddwarf_volume_support"],
@test(enabled=VOLUME_SUPPORT,
depends_on=[test_delete])
def test_volume_is_deleted(self):
raise SkipTest("Cannot test volume is deleted from db.")
@ -1105,14 +1158,13 @@ class CheckInstance(AttrCheck):
self.links(self.instance['flavor']['links'])
def volume_key_exists(self):
if CONFIG.values['reddwarf_volume_support']:
if 'volume' not in self.instance:
self.fail("'volume' not found in instance.")
return False
return True
if 'volume' not in self.instance:
self.fail("'volume' not found in instance.")
return False
return True
def volume(self):
if not CONFIG.values["reddwarf_volume_support"]:
if not VOLUME_SUPPORT:
return
if self.volume_key_exists():
expected_attrs = ['size']
@ -1122,7 +1174,7 @@ class CheckInstance(AttrCheck):
msg="Volumes")
def used_volume(self):
if not CONFIG.values["reddwarf_volume_support"]:
if not VOLUME_SUPPORT:
return
if self.volume_key_exists():
expected_attrs = ['size', 'used']
@ -1131,6 +1183,8 @@ class CheckInstance(AttrCheck):
msg="Volumes")
def volume_mgmt(self):
if not VOLUME_SUPPORT:
return
if self.volume_key_exists():
expected_attrs = ['description', 'id', 'name', 'size']
self.attrs_exist(self.instance['volume'], expected_attrs,
@ -1152,6 +1206,8 @@ class CheckInstance(AttrCheck):
msg="Guest status")
def mgmt_volume(self):
if not VOLUME_SUPPORT:
return
expected_attrs = ['description', 'id', 'name', 'size']
self.attrs_exist(self.instance['volume'], expected_attrs,
msg="Volume")
@ -1183,7 +1239,13 @@ class BadInstanceStatusBug():
# can be used as another case. This all boils back to the same
# piece of code so I'm not sure if it's relevant or not but could
# be done.
result = self.client.instances.create('testbox', 1, {'size': 5})
size = None
if VOLUME_SUPPORT:
size = {'size': 5}
result = self.client.instances.create('testbox',
instance_info.dbaas_flavor_href,
size)
id = result.id
self.instances.append(id)

View File

@ -25,11 +25,14 @@ from proboscis import SkipTest
from reddwarf import tests
from reddwarf.tests.util.check import Checker
from reddwarfclient.exceptions import BadRequest
from reddwarfclient.exceptions import HTTPNotImplemented
from reddwarfclient.exceptions import UnprocessableEntity
from reddwarf.tests.api.instances import GROUP as INSTANCE_GROUP
from reddwarf.tests.api.instances import GROUP_START
from reddwarf.tests.api.instances import instance_info
from reddwarf.tests.api.instances import assert_unprocessable
from reddwarf.tests.api.instances import VOLUME_SUPPORT
from reddwarf.tests.api.instances import EPHEMERAL_SUPPORT
from reddwarf.tests import util
from reddwarf.tests.util.server_connection import create_server_connection
from reddwarf.tests.util import poll_until
@ -326,7 +329,7 @@ class StopTests(RebootTestBase):
Confirms the get call behaves appropriately while an instance is
down.
"""
if not CONFIG.reddwarf_volume_support:
if not VOLUME_SUPPORT:
raise SkipTest("Not testing volumes.")
instance = self.dbaas.instances.get(self.instance_id)
with TypeCheck("instance", instance) as check:
@ -416,14 +419,33 @@ class ResizeInstanceTest(ActionTestBase):
assert_raises(BadRequest, self.dbaas.instances.resize_instance,
self.instance_id, self.flavor_id)
@test(enabled=VOLUME_SUPPORT)
def test_instance_resize_to_ephemeral_in_volume_support_should_fail(self):
flavor_name = CONFIG.values.get('instance_bigger_eph_flavor_name',
'eph.rd-smaller')
flavors = self.dbaas.find_flavors_by_name(flavor_name)
assert_raises(HTTPNotImplemented, self.dbaas.instances.resize_instance,
self.instance_id, flavors[0].id)
@test(enabled=EPHEMERAL_SUPPORT)
def test_instance_resize_to_non_ephemeral_flavor_should_fail(self):
flavor_name = CONFIG.values.get('instance_bigger_flavor_name',
'm1-small')
flavors = self.dbaas.find_flavors_by_name(flavor_name)
assert_raises(BadRequest, self.dbaas.instances.resize_instance,
self.instance_id, flavors[0].id)
def obtain_flavor_ids(self):
old_id = self.instance.flavor['id']
self.expected_old_flavor_id = old_id
res = instance_info.dbaas.find_flavor_and_self_href(old_id)
self.expected_dbaas_flavor, _dontcare_ = res
flavor_name = CONFIG.values.get('instance_bigger_flavor_name',
'm1.small')
if EPHEMERAL_SUPPORT:
flavor_name = CONFIG.values.get('instance_bigger_eph_flavor_name',
'eph.rd-smaller')
else:
flavor_name = CONFIG.values.get('instance_bigger_flavor_name',
'm1.small')
flavors = self.dbaas.find_flavors_by_name(flavor_name)
assert_equal(len(flavors), 1, "Number of flavors with name '%s' "
"found was '%d'." % (flavor_name, len(flavors)))
@ -548,7 +570,7 @@ def resize_should_not_delete_users():
@test(runs_after=[ResizeInstanceTest], depends_on=[create_user],
groups=[GROUP, tests.INSTANCES],
enabled=CONFIG.reddwarf_volume_support)
enabled=VOLUME_SUPPORT)
class ResizeInstanceVolume(object):
""" Resize the volume of the instance """

View File

@ -1,6 +1,5 @@
import time
from proboscis import after_class
from proboscis import before_class
from proboscis import test
from proboscis.asserts import *
@ -11,6 +10,8 @@ from reddwarf.tests.util import create_dbaas_client
from reddwarf.tests.util import poll_until
from reddwarf.tests.util import test_config
from reddwarf.tests.util.users import Requirements
from reddwarf.tests.api.instances import instance_info
from reddwarf.tests.api.instances import VOLUME_SUPPORT
class TestBase(object):
@ -21,7 +22,12 @@ class TestBase(object):
self.dbaas = create_dbaas_client(self.user)
def create_instance(self, name, size=1):
result = self.dbaas.instances.create(name, 1, {'size': size}, [], [])
volume = None
if VOLUME_SUPPORT:
volume = {'size': size}
result = self.dbaas.instances.create(name,
instance_info.dbaas_flavor_href,
volume, [], [])
return result.id
def wait_for_instance_status(self, instance_id, status="ACTIVE"):
@ -73,8 +79,12 @@ class ErroredInstanceDelete(TestBase):
super(ErroredInstanceDelete, self).set_up()
# Create an instance that fails during server prov.
self.server_error = self.create_instance('test_SERVER_ERROR')
# Create an instance that fails during volume prov.
self.volume_error = self.create_instance('test_VOLUME_ERROR', size=9)
if VOLUME_SUPPORT:
# Create an instance that fails during volume prov.
self.volume_error = self.create_instance('test_VOLUME_ERROR',
size=9)
else:
self.volume_error = None
# Create an instance that fails during DNS prov.
#self.dns_error = self.create_instance('test_DNS_ERROR')
# Create an instance that fails while it's been deleted the first time.
@ -85,7 +95,7 @@ class ErroredInstanceDelete(TestBase):
def delete_server_error(self):
self.delete_errored_instance(self.server_error)
@test
@test(enabled=VOLUME_SUPPORT)
@time_out(20)
def delete_volume_error(self):
self.delete_errored_instance(self.volume_error)

View File

@ -30,6 +30,8 @@ from reddwarf.tests import util
from reddwarf.tests.util import create_client
from reddwarf.tests.util import poll_until
from reddwarf.tests.util import test_config
from reddwarf.tests.api.instances import VOLUME_SUPPORT
from reddwarf.tests.api.instances import EPHEMERAL_SUPPORT
@test(groups=["dbaas.api.instances.down"])
@ -40,13 +42,21 @@ class TestBase(object):
def set_up(self):
self.client = create_client(is_admin=False)
self.mgmt_client = create_client(is_admin=True)
flavor_name = test_config.values.get('instance_flavor_name', 'm1.tiny')
if EPHEMERAL_SUPPORT:
flavor_name = test_config.values.get('instance_eph_flavor_name',
'eph.rd-tiny')
flavor2_name = test_config.values.get(
'instance_bigger_eph_flavor_name', 'eph.rd-smaller')
else:
flavor_name = test_config.values.get('instance_flavor_name',
'm1.tiny')
flavor2_name = test_config.values.get(
'instance_bigger_flavor_name', 'm1.small')
flavors = self.client.find_flavors_by_name(flavor_name)
self.flavor_id = flavors[0].id
self.name = "TEST_" + str(datetime.now())
# Get the resize to flavor.
flavor2_name = test_config.values.get('instance_bigger_flavor_name',
'm1.small')
flavors2 = self.client.find_flavors_by_name(flavor2_name)
self.new_flavor_id = flavors2[0].id
assert_not_equal(self.flavor_id, self.new_flavor_id)
@ -58,8 +68,11 @@ class TestBase(object):
@test
def create_instance(self):
volume = None
if VOLUME_SUPPORT:
volume = {'size': 1}
initial = self.client.instances.create(self.name, self.flavor_id,
{'size': 1}, [], [])
volume, [], [])
self.id = initial.id
self._wait_for_active()
@ -82,7 +95,8 @@ class TestBase(object):
def put_into_shutdown_state_2(self):
self._shutdown_instance()
@test(depends_on=[put_into_shutdown_state_2])
@test(depends_on=[put_into_shutdown_state_2],
enabled=VOLUME_SUPPORT)
@time_out(60 * 5)
def resize_volume_in_shutdown_state(self):
self.client.instances.resize_volume(self.id, 2)

View File

@ -10,11 +10,13 @@ from reddwarf.tests.util import create_dbaas_client
from reddwarfclient import exceptions
from datetime import datetime
from reddwarf.tests.util.users import Users
from reddwarf.tests.config import CONFIG
GROUP = "dbaas.api.limits"
DEFAULT_RATE = 200
DEFAULT_MAX_VOLUMES = 100
DEFAULT_MAX_INSTANCES = 55
DEFAULT_MAX_BACKUPS = 5
@test(groups=[GROUP])
@ -72,8 +74,10 @@ class Limits(object):
# remove the abs_limits from the rate limits
abs_limits = d.pop("ABSOLUTE", None)
assert_equal(abs_limits.verb, "ABSOLUTE")
assert_equal(int(abs_limits.maxTotalInstances), DEFAULT_MAX_INSTANCES)
assert_equal(int(abs_limits.maxTotalVolumes), DEFAULT_MAX_VOLUMES)
assert_equal(int(abs_limits.max_instances), DEFAULT_MAX_INSTANCES)
assert_equal(int(abs_limits.max_backups), DEFAULT_MAX_BACKUPS)
if CONFIG.reddwarf_volume_support:
assert_equal(int(abs_limits.max_volumes), DEFAULT_MAX_VOLUMES)
for k in d:
assert_equal(d[k].verb, k)
@ -93,8 +97,10 @@ class Limits(object):
abs_limits = d["ABSOLUTE"]
get = d["GET"]
assert_equal(int(abs_limits.maxTotalInstances), DEFAULT_MAX_INSTANCES)
assert_equal(int(abs_limits.maxTotalVolumes), DEFAULT_MAX_VOLUMES)
assert_equal(int(abs_limits.max_instances), DEFAULT_MAX_INSTANCES)
assert_equal(int(abs_limits.max_backups), DEFAULT_MAX_BACKUPS)
if CONFIG.reddwarf_volume_support:
assert_equal(int(abs_limits.max_volumes), DEFAULT_MAX_VOLUMES)
assert_equal(get.verb, "GET")
assert_equal(get.unit, "MINUTE")
assert_true(int(get.remaining) <= DEFAULT_RATE - 5)
@ -119,10 +125,13 @@ class Limits(object):
assert_equal(get.verb, "GET")
assert_equal(get.unit, "MINUTE")
assert_equal(int(abs_limits.maxTotalInstances),
assert_equal(int(abs_limits.max_instances),
DEFAULT_MAX_INSTANCES)
assert_equal(int(abs_limits.maxTotalVolumes),
DEFAULT_MAX_VOLUMES)
assert_equal(int(abs_limits.max_backups),
DEFAULT_MAX_BACKUPS)
if CONFIG.reddwarf_volume_support:
assert_equal(int(abs_limits.max_volumes),
DEFAULT_MAX_VOLUMES)
except exceptions.OverLimit:
encountered = True

View File

@ -27,6 +27,7 @@ from reddwarf.tests.api.instances import instance_info
from reddwarf.tests.util import test_config
from reddwarf.tests.util import create_dbaas_client
from reddwarf.tests.util import poll_until
from reddwarf.tests.config import CONFIG
from reddwarf.tests.util.users import Requirements
from reddwarf.tests.api.instances import existing_instance
@ -188,8 +189,14 @@ class AccountWithBrokenInstance(object):
self.client = create_dbaas_client(self.user)
self.name = 'test_SERVER_ERROR'
# Create an instance with a broken compute instance.
self.response = self.client.instances.create(self.name, 1,
{'size': 1}, [])
volume = None
if CONFIG.reddwarf_volume_support:
volume = {'size': 1}
self.response = self.client.instances.create(
self.name,
instance_info.dbaas_flavor_href,
volume,
[])
poll_until(lambda: self.client.instances.get(self.response.id),
lambda instance: instance.status == 'ERROR',
time_out=10)

View File

@ -110,7 +110,8 @@ def mgmt_instance_get():
server.has_element("name", basestring)
server.has_element("status", basestring)
server.has_element("tenant_id", basestring)
if CONFIG.reddwarf_main_instance_has_volume:
if (CONFIG.reddwarf_volume_support and
CONFIG.reddwarf_main_instance_has_volume):
with CollectionCheck("volume", api_instance.volume) as volume:
volume.has_element("attachments", list)
volume.has_element("availability_zone", basestring)
@ -134,8 +135,14 @@ class WhenMgmtInstanceGetIsCalledButServerIsNotReady(object):
# Fake volume will fail if the size is 13.
# TODO(tim.simpson): This would be a lot nicer looking if we used a
# traditional mock framework.
response = self.client.instances.create('test_SERVER_ERROR', 1,
{'size': 13}, [])
body = None
if CONFIG.reddwarf_volume_support:
body = {'size': 13}
response = self.client.instances.create(
'test_SERVER_ERROR',
instance_info.dbaas_flavor_href,
body,
[])
poll_until(lambda: self.client.instances.get(response.id),
lambda instance: instance.status == 'ERROR',
time_out=10)
@ -201,8 +208,10 @@ class MgmtInstancesIndex(object):
'task_description',
'tenant_id',
'updated',
'volume',
]
if CONFIG.reddwarf_volume_support:
expected_fields.append('volume')
index = self.client.management.index()
for instance in index:
with Check() as check:

View File

@ -4,6 +4,8 @@ from proboscis import after_class
from proboscis import before_class
from proboscis.asserts import Check
from reddwarf.tests.config import CONFIG
from reddwarf.tests.api.instances import instance_info
from reddwarf.tests.api.instances import VOLUME_SUPPORT
from reddwarfclient import exceptions
import json
import requests
@ -22,10 +24,13 @@ class MalformedJson(object):
self.reqs = Requirements(is_admin=False)
self.user = CONFIG.users.find_user(self.reqs)
self.dbaas = create_dbaas_client(self.user)
volume = None
if VOLUME_SUPPORT:
volume = {"size": 1}
self.instance = self.dbaas.instances.create(
name="qe_instance",
flavor_id=1,
volume={"size": 1},
flavor_id=instance_info.dbaas_flavor_href,
volume=volume,
databases=[{"name": "firstdb", "character_set": "latin2",
"collate": "latin2_general_ci"}])

View File

@ -32,13 +32,13 @@ FAKE_HOSTS = ["fake_host_1", "fake_host_2"]
class FakeFlavor(object):
def __init__(self, id, disk, name, ram):
def __init__(self, id, disk, name, ram, ephemeral=0, vcpus=10):
self.id = id
self.disk = disk
self.name = name
self.ram = ram
self.vcpus = 10
self.ephemeral = 0
self.vcpus = vcpus
self.ephemeral = ephemeral
@property
def links(self):
@ -69,6 +69,8 @@ class FakeFlavors(object):
self._add(8, 2, "m1.rd-smaller", 768)
self._add(9, 10, "tinier", 506)
self._add(10, 2, "m1.rd-tiny", 512)
self._add(11, 0, "eph.rd-tiny", 512, 1)
self._add(12, 20, "eph.rd-smaller", 768, 2)
def _add(self, *args, **kwargs):
new_flavor = FakeFlavor(*args, **kwargs)

View File

@ -44,8 +44,9 @@ class BaseLimitTestSuite(testtools.TestCase):
def setUp(self):
super(BaseLimitTestSuite, self).setUp()
self.absolute_limits = {"maxTotalInstances": 55,
"maxTotalVolumes": 100}
self.absolute_limits = {"max_instances": 55,
"max_volumes": 100,
"max_backups": 40}
class LimitsControllerTest(BaseLimitTestSuite):
@ -60,8 +61,7 @@ class LimitsControllerTest(BaseLimitTestSuite):
when(QUOTAS).get_all_quotas_by_tenant(any()).thenReturn({})
view = limit_controller.index(req, "test_tenant_id")
expected = {'limits': [{'maxTotalInstances': 0,
'verb': 'ABSOLUTE', 'maxTotalVolumes': 0}]}
expected = {'limits': [{'verb': 'ABSOLUTE'}]}
self.assertEqual(expected, view._data)
def test_limit_index(self):
@ -111,6 +111,10 @@ class LimitsControllerTest(BaseLimitTestSuite):
resource="instances",
hard_limit=100),
"backups": Quota(tenant_id=tenant_id,
resource="backups",
hard_limit=40),
"volumes": Quota(tenant_id=tenant_id,
resource="volumes",
hard_limit=55)}
@ -124,9 +128,10 @@ class LimitsControllerTest(BaseLimitTestSuite):
expected = {
'limits': [
{
'maxTotalInstances': 100,
'max_instances': 100,
'max_backups': 40,
'verb': 'ABSOLUTE',
'maxTotalVolumes': 55
'max_volumes': 55
},
{
'regex': '.*',
@ -752,9 +757,7 @@ class LimitsViewsTest(testtools.TestCase):
self.assertIsNotNone(view_data)
data = view_data.data()
expected = {'limits': [{'maxTotalInstances': 0,
'verb': 'ABSOLUTE',
'maxTotalVolumes': 0}]}
expected = {'limits': [{'verb': 'ABSOLUTE'}]}
self.assertEqual(expected, data)
@ -797,15 +800,16 @@ class LimitsViewsTest(testtools.TestCase):
"resetTime": 1311272226
}
]
abs_view = {"instances": 55, "volumes": 100}
abs_view = {"instances": 55, "volumes": 100, "backups": 40}
view_data = views.LimitViews(abs_view, rate_limits)
self.assertIsNotNone(view_data)
data = view_data.data()
expected = {'limits': [{'maxTotalInstances': 55,
expected = {'limits': [{'max_instances': 55,
'max_backups': 40,
'verb': 'ABSOLUTE',
'maxTotalVolumes': 100},
'max_volumes': 100},
{'regex': '.*',
'nextAvailable': '2011-07-21T18:17:06Z',
'uri': '*',

View File

@ -110,7 +110,22 @@ class QuotaControllerTest(testtools.TestCase):
verify(quota, never).save()
self.assertEquals(200, result.status)
def test_update_resource_(self):
def test_update_resource_instance(self):
instance_quota = mock(Quota)
when(DatabaseModelBase).find_by(
tenant_id=FAKE_TENANT2,
resource='instances').thenReturn(instance_quota)
body = {'quotas': {'instances': 2}}
result = self.controller.update(self.req, body, FAKE_TENANT1,
FAKE_TENANT2)
verify(instance_quota, times=1).save()
self.assertTrue('instances' in result._data['quotas'])
self.assertEquals(200, result.status)
self.assertEquals(2, result._data['quotas']['instances'])
@testtools.skipIf(not CONF.reddwarf_volume_support,
'Volume support is not enabled')
def test_update_resource_volume(self):
instance_quota = mock(Quota)
when(DatabaseModelBase).find_by(
tenant_id=FAKE_TENANT2,
@ -123,7 +138,7 @@ class QuotaControllerTest(testtools.TestCase):
result = self.controller.update(self.req, body, FAKE_TENANT1,
FAKE_TENANT2)
verify(instance_quota, never).save()
self.assertFalse('instances' in result._data)
self.assertFalse('instances' in result._data['quotas'])
verify(volume_quota, times=1).save()
self.assertEquals(200, result.status)
self.assertEquals(10, result._data['quotas']['volumes'])