diff options
Diffstat (limited to 'trove')
-rw-r--r-- | trove/common/apischema.py | 3 | ||||
-rw-r--r-- | trove/common/cfg.py | 26 | ||||
-rw-r--r-- | trove/instance/models.py | 33 | ||||
-rw-r--r-- | trove/instance/service.py | 4 | ||||
-rw-r--r-- | trove/instance/views.py | 4 | ||||
-rw-r--r-- | trove/quota/quota.py | 5 | ||||
-rw-r--r-- | trove/taskmanager/models.py | 42 | ||||
-rw-r--r-- | trove/tests/api/instances.py | 16 | ||||
-rw-r--r-- | trove/tests/api/limits.py | 12 | ||||
-rw-r--r-- | trove/tests/api/mgmt/instances.py | 15 | ||||
-rw-r--r-- | trove/tests/config.py | 6 | ||||
-rw-r--r-- | trove/tests/unittests/instance/test_instance_views.py | 1 | ||||
-rw-r--r-- | trove/tests/unittests/taskmanager/test_models.py | 1 |
13 files changed, 111 insertions, 57 deletions
diff --git a/trove/common/apischema.py b/trove/common/apischema.py index 518da734..82ac0460 100644 --- a/trove/common/apischema.py +++ b/trove/common/apischema.py @@ -188,8 +188,7 @@ instance = { "properties": { "instance": { "type": "object", - "required": ["name", "flavorRef", - "volume" if CONF.trove_volume_support else None], + "required": ["name", "flavorRef"], "additionalProperties": True, "properties": { "name": non_empty_string, diff --git a/trove/common/cfg.py b/trove/common/cfg.py index 0c198153..fff15230 100644 --- a/trove/common/cfg.py +++ b/trove/common/cfg.py @@ -313,6 +313,10 @@ mysql_opts = [ default='trove.guestagent.strategies.backup.mysql_impl'), cfg.StrOpt('restore_namespace', default='trove.guestagent.strategies.restore.mysql_impl'), + cfg.BoolOpt('volume_support', + default=True, + help='Whether to provision a cinder volume for datadir.'), + cfg.StrOpt('device_path', default='/dev/vdb'), ] # Percona @@ -344,6 +348,10 @@ percona_opts = [ default='trove.guestagent.strategies.backup.mysql_impl'), cfg.StrOpt('restore_namespace', default='trove.guestagent.strategies.restore.mysql_impl'), + cfg.BoolOpt('volume_support', + default=True, + help='Whether to provision a cinder volume for datadir.'), + cfg.StrOpt('device_path', default='/dev/vdb'), ] # Redis @@ -366,6 +374,10 @@ redis_opts = [ "volumes if volume support is enabled."), cfg.IntOpt('usage_timeout', default=450, help='Timeout to wait for a guest to become active.'), + cfg.BoolOpt('volume_support', + default=False, + help='Whether to provision a cinder volume for datadir.'), + cfg.StrOpt('device_path', default=None), ] # Cassandra @@ -388,6 +400,10 @@ cassandra_opts = [ "volumes if volume support is enabled."), cfg.IntOpt('usage_timeout', default=600, help='Timeout to wait for a guest to become active.'), + cfg.BoolOpt('volume_support', + default=True, + help='Whether to provision a cinder volume for datadir.'), + cfg.StrOpt('device_path', default='/dev/vdb'), ] # Couchbase @@ -420,7 +436,11 @@ couchbase_opts = [ cfg.StrOpt('backup_namespace', default='trove.guestagent.strategies.backup.couchbase_impl'), cfg.StrOpt('restore_namespace', - default='trove.guestagent.strategies.restore.couchbase_impl') + default='trove.guestagent.strategies.restore.couchbase_impl'), + cfg.BoolOpt('volume_support', + default=True, + help='Whether to provision a cinder volume for datadir.'), + cfg.StrOpt('device_path', default='/dev/vdb'), ] # MongoDB @@ -443,6 +463,10 @@ mongodb_opts = [ "volumes if volume support is enabled."), cfg.IntOpt('usage_timeout', default=450, help='Timeout to wait for a guest to become active.'), + cfg.BoolOpt('volume_support', + default=True, + help='Whether to provision a cinder volume for datadir.'), + cfg.StrOpt('device_path', default='/dev/vdb'), ] CONF = cfg.CONF diff --git a/trove/instance/models.py b/trove/instance/models.py index 4da31e8e..f532c307 100644 --- a/trove/instance/models.py +++ b/trove/instance/models.py @@ -335,6 +335,14 @@ class SimpleInstance(object): return self.ds @property + def volume_support(self): + return CONF.get(self.datastore_version.manager).volume_support + + @property + def device_path(self): + return CONF.get(self.datastore_version.manager).device_path + + @property def root_password(self): return self.root_pass @@ -510,7 +518,7 @@ class BaseInstance(SimpleInstance): task_api.API(self.context).delete_instance(self.id) deltas = {'instances': -1} - if CONF.trove_volume_support: + if self.volume_support: deltas['volumes'] = -self.volume_size return run_with_quotas(self.tenant_id, deltas, @@ -616,15 +624,17 @@ class Instance(BuiltInstance): raise exception.FlavorNotFound(uuid=flavor_id) deltas = {'instances': 1} - if CONF.trove_volume_support: + volume_support = CONF.get(datastore_version.manager).volume_support + if 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) + ephemeral_support = CONF.get(datastore_version.manager).device_path + if ephemeral_support: + if flavor.ephemeral == 0: + raise exception.LocalStorageNotSpecified(flavor=flavor_id) if backup_id is not None: backup_info = Backup.get_by_id(context, backup_id) @@ -726,18 +736,21 @@ class Instance(BuiltInstance): old_flavor = client.flavors.get(self.flavor_id) new_flavor_size = new_flavor.ram old_flavor_size = old_flavor.ram - if CONF.trove_volume_support: + if self.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: + elif self.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() + elif new_flavor_size == old_flavor_size: + # uses local storage + raise exception.CannotResizeToSameSize() # Set the task to RESIZING and begin the async call before returning. self.update_db(task_status=InstanceTasks.RESIZING) @@ -749,9 +762,6 @@ class Instance(BuiltInstance): def _resize_resources(): self.validate_can_perform_action() LOG.info("Resizing volume of instance %s..." % self.id) - if not self.volume_size: - raise exception.BadRequest(_("Instance %s has no volume.") - % self.id) old_size = self.volume_size if int(new_size) <= old_size: raise exception.BadRequest(_("The new volume 'size' must be " @@ -761,6 +771,9 @@ class Instance(BuiltInstance): self.update_db(task_status=InstanceTasks.RESIZING) task_api.API(self.context).resize_volume(new_size, self.id) + if not self.volume_size: + raise exception.BadRequest(_("Instance %s has no volume.") + % self.id) new_size_l = long(new_size) validate_volume_size(new_size_l) return run_with_quotas(self.tenant_id, diff --git a/trove/instance/service.py b/trove/instance/service.py index 64cae223..9428ac12 100644 --- a/trove/instance/service.py +++ b/trove/instance/service.py @@ -39,10 +39,6 @@ class InstanceController(wsgi.Controller): """Controller for instance functionality.""" schemas = apischema.instance.copy() - if not CONF.trove_volume_support: - # see instance.models.create for further validation around this - LOG.info("Removing volume attributes from schema") - schemas['create']['properties']['instance']['required'].pop() @classmethod def get_action_schema(cls, body, action_schema): diff --git a/trove/instance/views.py b/trove/instance/views.py index 55634272..57e9d91c 100644 --- a/trove/instance/views.py +++ b/trove/instance/views.py @@ -39,7 +39,7 @@ class InstanceView(object): "datastore": {"type": self.instance.datastore.name, "version": self.instance.datastore_version.name}, } - if CONF.trove_volume_support: + if self.instance.volume_support: instance_dict['volume'] = {'size': self.instance.volume_size} if self.instance.hostname: @@ -88,7 +88,7 @@ class InstanceDetailView(InstanceView): if (isinstance(self.instance, models.DetailInstance) and self.instance.volume_used): used = self.instance.volume_used - if CONF.trove_volume_support: + if self.instance.volume_support: result['instance']['volume']['used'] = used else: # either ephemeral or root partition diff --git a/trove/quota/quota.py b/trove/quota/quota.py index 59bc072f..b9ec3bb5 100644 --- a/trove/quota/quota.py +++ b/trove/quota/quota.py @@ -312,9 +312,8 @@ QUOTAS = QuotaEngine() ''' Define all kind of resources here ''' resources = [Resource(Resource.INSTANCES, 'max_instances_per_user'), - Resource(Resource.BACKUPS, 'max_backups_per_user')] -if CONF.trove_volume_support: - resources.append(Resource(Resource.VOLUMES, 'max_volumes_per_user')) + Resource(Resource.BACKUPS, 'max_backups_per_user'), + Resource(Resource.VOLUMES, 'max_volumes_per_user')] QUOTAS.register_resources(resources) diff --git a/trove/taskmanager/models.py b/trove/taskmanager/models.py index f6a261dd..055c8919 100644 --- a/trove/taskmanager/models.py +++ b/trove/taskmanager/models.py @@ -118,7 +118,7 @@ class NotifyMixin(object): 'user_id': self.context.user, } - if CONF.trove_volume_support: + if CONF.get(self.datastore_version.manager).volume_support: payload.update({ 'volume_size': self.volume_size, 'nova_volume_id': self.volume_id @@ -365,7 +365,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): err = inst_models.InstanceTasks.BUILDING_ERROR_SERVER self._log_and_raise(e, msg, err) - device_path = CONF.device_path + device_path = self.device_path mount_point = CONF.get(datastore_manager).mount_point volume_info = {'device_path': device_path, 'mount_point': mount_point} LOG.debug("end _create_server_volume for id: %s" % self.id) @@ -395,7 +395,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): ifaces, ports = self._build_heat_nics(nics) template_obj = template.load_heat_template(datastore_manager) heat_template_unicode = template_obj.render( - volume_support=CONF.trove_volume_support, + volume_support=self.volume_support, ifaces=ifaces, ports=ports, tcp_rules=tcp_rules_mapping_list, udp_rules=udp_ports_mapping_list, @@ -438,7 +438,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): raise TroveError("Heat Resource Provisioning Failed.") instance_id = resource.physical_resource_id - if CONF.trove_volume_support: + if self.volume_support: resource = client.resources.get(stack.id, 'DataVolume') if resource.resource_status != HEAT_RESOURCE_SUCCESSFUL_STATE: raise TroveError("Heat Resource Provisioning Failed.") @@ -469,7 +469,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): err = inst_models.InstanceTasks.BUILDING_ERROR_SERVER self._log_and_raise(e, msg, err) - device_path = CONF.device_path + device_path = self.device_path mount_point = CONF.get(datastore_manager).mount_point volume_info = {'device_path': device_path, 'mount_point': mount_point} @@ -504,7 +504,9 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): def _build_volume_info(self, datastore_manager, volume_size=None): volume_info = None - volume_support = CONF.trove_volume_support + volume_support = self.volume_support + device_path = self.device_path + mount_point = CONF.get(datastore_manager).mount_point LOG.debug("trove volume support = %s" % volume_support) if volume_support: try: @@ -515,13 +517,12 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): 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.get(datastore_manager).mount_point) + LOG.debug("device_path = %s" % device_path) + LOG.debug("mount_point = %s" % mount_point) volume_info = { 'block_device': None, - 'device_path': CONF.device_path, - 'mount_point': CONF.get(datastore_manager).mount_point, + 'device_path': device_path, + 'mount_point': mount_point, 'volumes': None, } return volume_info @@ -570,7 +571,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): LOG.debug("block_device = %s" % block_device) LOG.debug("volume = %s" % created_volumes) - device_path = CONF.device_path + device_path = self.device_path mount_point = CONF.get(datastore_manager).mount_point LOG.debug("device_path = %s" % device_path) LOG.debug("mount_point = %s" % mount_point) @@ -1021,6 +1022,9 @@ class ResizeVolumeAction(object): self.instance.datastore_version.manager).mount_point return mount_point + def get_device_path(self): + return self.instance.device_path + def _fail(self, orig_func): LOG.exception(_("%(func)s encountered an error when attempting to " "resize the volume for instance %(id)s. Setting service " @@ -1066,7 +1070,8 @@ class ResizeVolumeAction(object): LOG.debug("Unmounting the volume on instance %(id)s" % { 'id': self.instance.id}) mount_point = self.get_mount_point() - self.instance.guest.unmount_volume(device_path=CONF.device_path, + device_path = self.get_device_path() + self.instance.guest.unmount_volume(device_path=device_path, mount_point=mount_point) LOG.debug("Successfully unmounted the volume %(vol_id)s for " "instance %(id)s" % {'vol_id': self.instance.volume_id, @@ -1094,11 +1099,12 @@ class ResizeVolumeAction(object): @try_recover def _attach_volume(self): + device_path = self.get_device_path() LOG.debug("Attach volume %(vol_id)s to instance %(id)s at " "%(dev)s" % {'vol_id': self.instance.volume_id, - 'id': self.instance.id, 'dev': CONF.device_path}) + 'id': self.instance.id, 'dev': device_path}) self.instance.nova_client.volumes.create_server_volume( - self.instance.server.id, self.instance.volume_id, CONF.device_path) + self.instance.server.id, self.instance.volume_id, device_path) def volume_in_use(): volume = self.instance.volume_client.volumes.get( @@ -1117,7 +1123,8 @@ class ResizeVolumeAction(object): LOG.debug("Resizing the filesystem for instance %(id)s" % { 'id': self.instance.id}) mount_point = self.get_mount_point() - self.instance.guest.resize_fs(device_path=CONF.device_path, + device_path = self.get_device_path() + self.instance.guest.resize_fs(device_path=device_path, mount_point=mount_point) LOG.debug("Successfully resized volume %(vol_id)s filesystem for " "instance %(id)s" % {'vol_id': self.instance.volume_id, @@ -1128,7 +1135,8 @@ class ResizeVolumeAction(object): LOG.debug("Mount the volume on instance %(id)s" % { 'id': self.instance.id}) mount_point = self.get_mount_point() - self.instance.guest.mount_volume(device_path=CONF.device_path, + device_path = self.get_device_path() + self.instance.guest.mount_volume(device_path=device_path, mount_point=mount_point) LOG.debug("Successfully mounted the volume %(vol_id)s on instance " "%(id)s" % {'vol_id': self.instance.volume_id, diff --git a/trove/tests/api/instances.py b/trove/tests/api/instances.py index cf72a237..4e241d09 100644 --- a/trove/tests/api/instances.py +++ b/trove/tests/api/instances.py @@ -238,9 +238,8 @@ class CreateInstanceQuotaTest(unittest.TestCase): self.test_info.dbaas_datastore = CONFIG.dbaas_datastore def tearDown(self): - quota_dict = {'instances': CONFIG.trove_max_instances_per_user} - if VOLUME_SUPPORT: - quota_dict['volumes'] = CONFIG.trove_max_volumes_per_user + quota_dict = {'instances': CONFIG.trove_max_instances_per_user, + 'volumes': CONFIG.trove_max_volumes_per_user} dbaas_admin.quota.update(self.test_info.user.tenant_id, quota_dict) @@ -410,6 +409,17 @@ class CreateInstanceFail(object): volume, databases) assert_equal(501, dbaas.last_http_code) + def test_create_failure_with_volume_size_and_disabled_for_datastore(self): + instance_name = "instance-failure-volume-size_and_volume_disabled" + databases = [] + datastore = 'redis' + assert_equal(CONFIG.get(datastore, 'redis')['volume_support'], False) + volume = {'size': 2} + assert_raises(exceptions.HTTPNotImplemented, dbaas.instances.create, + instance_name, instance_info.dbaas_flavor_href, + volume, databases, datastore=datastore) + 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" diff --git a/trove/tests/api/limits.py b/trove/tests/api/limits.py index 0765b8cc..3ff56a86 100644 --- a/trove/tests/api/limits.py +++ b/trove/tests/api/limits.py @@ -27,7 +27,6 @@ from trove.tests.util import create_dbaas_client from troveclient.compat import exceptions from datetime import datetime from trove.tests.util.users import Users -from trove.tests.config import CONFIG GROUP = "dbaas.api.limits" DEFAULT_RATE = 200 @@ -93,8 +92,7 @@ class Limits(object): assert_equal(abs_limits.verb, "ABSOLUTE") assert_equal(int(abs_limits.max_instances), DEFAULT_MAX_INSTANCES) assert_equal(int(abs_limits.max_backups), DEFAULT_MAX_BACKUPS) - if CONFIG.trove_volume_support: - assert_equal(int(abs_limits.max_volumes), DEFAULT_MAX_VOLUMES) + assert_equal(int(abs_limits.max_volumes), DEFAULT_MAX_VOLUMES) for k in d: assert_equal(d[k].verb, k) @@ -116,8 +114,7 @@ class Limits(object): assert_equal(int(abs_limits.max_instances), DEFAULT_MAX_INSTANCES) assert_equal(int(abs_limits.max_backups), DEFAULT_MAX_BACKUPS) - if CONFIG.trove_volume_support: - assert_equal(int(abs_limits.max_volumes), DEFAULT_MAX_VOLUMES) + 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) @@ -146,9 +143,8 @@ class Limits(object): DEFAULT_MAX_INSTANCES) assert_equal(int(abs_limits.max_backups), DEFAULT_MAX_BACKUPS) - if CONFIG.trove_volume_support: - assert_equal(int(abs_limits.max_volumes), - DEFAULT_MAX_VOLUMES) + assert_equal(int(abs_limits.max_volumes), + DEFAULT_MAX_VOLUMES) except exceptions.OverLimit: encountered = True diff --git a/trove/tests/api/mgmt/instances.py b/trove/tests/api/mgmt/instances.py index e39fa036..aac5e91b 100644 --- a/trove/tests/api/mgmt/instances.py +++ b/trove/tests/api/mgmt/instances.py @@ -81,6 +81,8 @@ def mgmt_instance_get(): # a global. id = instance_info.id api_instance = mgmt.show(id) + datastore = getattr(api_instance, 'datastore') + datastore_type = datastore.get('type') # Print out all fields for extra info if the test fails. for name in dir(api_instance): @@ -102,7 +104,8 @@ def mgmt_instance_get(): instance.has_field('tenant_id', basestring) instance.has_field('updated', basestring) # Can be None if no volume is given on this instance. - if CONFIG.trove_volume_support: + volume_support = CONFIG.get(datastore_type, 'mysql')['volume_support'] + if volume_support: instance.has_field('volume', dict, volume_check) else: instance.has_field('volume', None) @@ -126,7 +129,7 @@ def mgmt_instance_get(): server.has_element("status", basestring) server.has_element("tenant_id", basestring) - if (CONFIG.trove_volume_support and + if (volume_support and CONFIG.trove_main_instance_has_volume): with CollectionCheck("volume", api_instance.volume) as volume: volume.has_element("attachments", list) @@ -151,9 +154,11 @@ 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. - body = None - if CONFIG.trove_volume_support: - body = {'size': 13} + datastore = {'type': 'mysql', 'version': '5.5'} + body = {'datastore': datastore} + vol_support = CONFIG.get(datastore['type'], 'mysql')['volume_support'] + if vol_support: + body.update({'size': 13}) response = self.client.instances.create( 'test_SERVER_ERROR', instance_info.dbaas_flavor_href, diff --git a/trove/tests/config.py b/trove/tests/config.py index fb501d4d..c07b5e80 100644 --- a/trove/tests/config.py +++ b/trove/tests/config.py @@ -116,8 +116,10 @@ class TestConfig(object): "key_buffer_size", "connect_timeout" ] - } - } + }, + "volume_support": True, + }, + "redis": {"volume_support": False}, } self._frozen_values = FrozenDict(self._values) self._users = None diff --git a/trove/tests/unittests/instance/test_instance_views.py b/trove/tests/unittests/instance/test_instance_views.py index 371a28a7..1189c23c 100644 --- a/trove/tests/unittests/instance/test_instance_views.py +++ b/trove/tests/unittests/instance/test_instance_views.py @@ -53,6 +53,7 @@ class InstanceDetailViewTest(TestCase): self.instance.updated = 'Now' self.instance.datastore_version = Mock() self.instance.datastore_version.name = 'mysql_test_version' + self.instance.datastore_version.manager = 'mysql' self.instance.hostname = 'test.trove.com' self.ip = "1.2.3.4" self.instance.addresses = {"private": [{"addr": self.ip}]} diff --git a/trove/tests/unittests/taskmanager/test_models.py b/trove/tests/unittests/taskmanager/test_models.py index 5def6b12..41e6d7f1 100644 --- a/trove/tests/unittests/taskmanager/test_models.py +++ b/trove/tests/unittests/taskmanager/test_models.py @@ -359,6 +359,7 @@ class ResizeVolumeTest(testtools.TestCase): class FakeGroup(): def __init__(self): self.mount_point = 'var/lib/mysql' + self.device_path = '/dev/vdb' taskmanager_models.CONF.get = Mock(return_value=FakeGroup()) def tearDown(self): |