summaryrefslogtreecommitdiff
path: root/trove
diff options
context:
space:
mode:
authorLingxian Kong <anlin.kong@gmail.com>2020-11-18 11:18:20 +1300
committerLingxian Kong <anlin.kong@gmail.com>2020-11-18 22:33:32 +1300
commitbf1ee5e8f29f286d2dee853db19912ea01ea4c78 (patch)
tree6c6967d32aee9ad6173ed6e1b79e7723c551beed /trove
parent051cdbe08c03280cb6808010ade88f058011f2ab (diff)
downloadtrove-bf1ee5e8f29f286d2dee853db19912ea01ea4c78.tar.gz
Support datastore version number for creating instance
* Support version number for datastore version flavor and volume type association commands. * Support 'version_number' parameter for creating instance. Story: 2008358 Task: 41264 Change-Id: I0e30194d65dcaa595b8927e2b9d5f8ec8c4300de
Diffstat (limited to 'trove')
-rw-r--r--trove/cmd/manage.py115
-rw-r--r--trove/common/apischema.py3
-rw-r--r--trove/common/exception.py25
-rw-r--r--trove/datastore/models.py251
-rw-r--r--trove/datastore/service.py4
-rw-r--r--trove/instance/models.py9
-rw-r--r--trove/instance/views.py2
-rw-r--r--trove/tests/api/datastores.py10
-rw-r--r--trove/tests/api/instances.py21
-rw-r--r--trove/tests/unittests/datastore/base.py10
-rw-r--r--trove/tests/unittests/datastore/test_datastore_version_metadata.py47
-rw-r--r--trove/tests/unittests/instance/test_service.py62
12 files changed, 311 insertions, 248 deletions
diff --git a/trove/cmd/manage.py b/trove/cmd/manage.py
index abe3c028..8bccb60d 100644
--- a/trove/cmd/manage.py
+++ b/trove/cmd/manage.py
@@ -73,7 +73,7 @@ class Commands(object):
packages, active,
version=version)
print("Datastore version '%s(%s)' updated." %
- (version_name, version))
+ (version_name, version or version_name))
except exception.DatastoreNotFound as e:
print(e)
@@ -99,63 +99,96 @@ class Commands(object):
datastore, datastore_version_name)
def datastore_version_flavor_add(self, datastore_name,
- datastore_version_name, flavor_ids):
+ datastore_version_name, flavor_ids,
+ version=None):
"""Adds flavors for a given datastore version id."""
+ dsmetadata = datastore_models.DatastoreVersionMetadata
try:
- dsmetadata = datastore_models.DatastoreVersionMetadata
+ datastore_version_id = dsmetadata.datastore_version_find(
+ datastore_name,
+ datastore_version_name,
+ version_number=version)
+
dsmetadata.add_datastore_version_flavor_association(
- datastore_name, datastore_version_name, flavor_ids.split(","))
+ datastore_version_id, flavor_ids.split(","))
print("Added flavors '%s' to the '%s' '%s'."
% (flavor_ids, datastore_name, datastore_version_name))
- except exception.DatastoreVersionNotFound as e:
+ except Exception as e:
print(e)
def datastore_version_flavor_delete(self, datastore_name,
- datastore_version_name, flavor_id):
+ datastore_version_name, flavor_id,
+ version=None):
"""Deletes a flavor's association with a given datastore."""
try:
dsmetadata = datastore_models.DatastoreVersionMetadata
+
+ datastore_version_id = dsmetadata.datastore_version_find(
+ datastore_name,
+ datastore_version_name,
+ version_number=version)
+
dsmetadata.delete_datastore_version_flavor_association(
- datastore_name, datastore_version_name, flavor_id)
+ datastore_version_id, flavor_id)
print("Deleted flavor '%s' from '%s' '%s'."
% (flavor_id, datastore_name, datastore_version_name))
- except exception.DatastoreVersionNotFound as e:
+ except Exception as e:
print(e)
def datastore_version_volume_type_add(self, datastore_name,
datastore_version_name,
- volume_type_ids):
+ volume_type_ids, version=None):
"""Adds volume type assiciation for a given datastore version id."""
try:
dsmetadata = datastore_models.DatastoreVersionMetadata
+
+ datastore_version_id = dsmetadata.datastore_version_find(
+ datastore_name,
+ datastore_version_name,
+ version_number=version)
+
dsmetadata.add_datastore_version_volume_type_association(
- datastore_name, datastore_version_name,
+ datastore_version_id,
volume_type_ids.split(","))
print("Added volume type '%s' to the '%s' '%s'."
% (volume_type_ids, datastore_name, datastore_version_name))
- except exception.DatastoreVersionNotFound as e:
+ except Exception as e:
print(e)
def datastore_version_volume_type_delete(self, datastore_name,
datastore_version_name,
- volume_type_id):
+ volume_type_id, version=None):
"""Deletes a volume type association with a given datastore."""
try:
dsmetadata = datastore_models.DatastoreVersionMetadata
+
+ datastore_version_id = dsmetadata.datastore_version_find(
+ datastore_name,
+ datastore_version_name,
+ version_number=version)
+
dsmetadata.delete_datastore_version_volume_type_association(
- datastore_name, datastore_version_name, volume_type_id)
+ datastore_version_id, volume_type_id)
print("Deleted volume type '%s' from '%s' '%s'."
% (volume_type_id, datastore_name, datastore_version_name))
- except exception.DatastoreVersionNotFound as e:
+ except Exception as e:
print(e)
def datastore_version_volume_type_list(self, datastore_name,
- datastore_version_name):
+ datastore_version_name,
+ version=None):
"""Lists volume type association with a given datastore."""
try:
dsmetadata = datastore_models.DatastoreVersionMetadata
- vtlist = dsmetadata.list_datastore_volume_type_associations(
- datastore_name, datastore_version_name)
+
+ datastore_version_id = dsmetadata.datastore_version_find(
+ datastore_name,
+ datastore_version_name,
+ version_number=version)
+
+ vtlist = dsmetadata. \
+ list_datastore_version_volume_type_associations(
+ datastore_version_id)
if vtlist.count() > 0:
for volume_type in vtlist:
print("Datastore: %s, Version: %s, Volume Type: %s" %
@@ -165,7 +198,7 @@ class Commands(object):
print("No Volume Type Associations found for Datastore: %s, "
"Version: %s." %
(datastore_name, datastore_version_name))
- except exception.DatastoreVersionNotFound as e:
+ except Exception as e:
print(e)
def params_of(self, command_name):
@@ -262,49 +295,79 @@ def main():
help='Name of the datastore version.')
parser = subparser.add_parser(
- 'datastore_version_flavor_add', help='Adds flavor association to '
- 'a given datastore and datastore version.')
+ 'datastore_version_flavor_add',
+ help='Adds flavor association to a given datastore and datastore '
+ 'version.')
parser.add_argument('datastore_name', help='Name of the datastore.')
parser.add_argument('datastore_version_name', help='Name of the '
'datastore version.')
parser.add_argument('flavor_ids', help='Comma separated list of '
'flavor ids.')
+ parser.add_argument(
+ '--version',
+ help='The version number of the datastore version, e.g. 5.7.30. '
+ 'If not specified, use <datastore_version_name> as default '
+ 'value.')
parser = subparser.add_parser(
- 'datastore_version_flavor_delete', help='Deletes a flavor '
- 'associated with a given datastore and datastore version.')
+ 'datastore_version_flavor_delete',
+ help='Deletes a flavor associated with a given datastore and '
+ 'datastore version.')
parser.add_argument('datastore_name', help='Name of the datastore.')
parser.add_argument('datastore_version_name', help='Name of the '
'datastore version.')
parser.add_argument('flavor_id', help='The flavor to be deleted for '
'a given datastore and datastore version.')
+ parser.add_argument(
+ '--version',
+ help='The version number of the datastore version, e.g. 5.7.30. '
+ 'If not specified, use <datastore_version_name> as default '
+ 'value.')
+
parser = subparser.add_parser(
- 'datastore_version_volume_type_add', help='Adds volume_type '
- 'association to a given datastore and datastore version.')
+ 'datastore_version_volume_type_add',
+ help='Adds volume_type association to a given datastore and '
+ 'datastore version.')
parser.add_argument('datastore_name', help='Name of the datastore.')
parser.add_argument('datastore_version_name', help='Name of the '
'datastore version.')
parser.add_argument('volume_type_ids', help='Comma separated list of '
'volume_type ids.')
+ parser.add_argument(
+ '--version',
+ help='The version number of the datastore version, e.g. 5.7.30. '
+ 'If not specified, use <datastore_version_name> as default '
+ 'value.')
parser = subparser.add_parser(
'datastore_version_volume_type_delete',
help='Deletes a volume_type '
- 'associated with a given datastore and datastore version.')
+ 'associated with a given datastore and datastore version.')
parser.add_argument('datastore_name', help='Name of the datastore.')
parser.add_argument('datastore_version_name', help='Name of the '
'datastore version.')
parser.add_argument('volume_type_id', help='The volume_type to be '
'deleted for a given datastore and datastore '
'version.')
+ parser.add_argument(
+ '--version',
+ help='The version number of the datastore version, e.g. 5.7.30. '
+ 'If not specified, use <datastore_version_name> as default '
+ 'value.')
parser = subparser.add_parser(
'datastore_version_volume_type_list',
help='Lists the volume_types '
- 'associated with a given datastore and datastore version.')
+ 'associated with a given datastore and datastore version.')
parser.add_argument('datastore_name', help='Name of the datastore.')
parser.add_argument('datastore_version_name', help='Name of the '
'datastore version.')
+ parser.add_argument(
+ '--version',
+ help='The version number of the datastore version, e.g. 5.7.30. '
+ 'If not specified, use <datastore_version_name> as default '
+ 'value.')
+
cfg.custom_parser('action', actions)
cfg.parse_args(sys.argv)
diff --git a/trove/common/apischema.py b/trove/common/apischema.py
index 3755dc8d..d1dec146 100644
--- a/trove/common/apischema.py
+++ b/trove/common/apischema.py
@@ -410,7 +410,8 @@ instance = {
"additionalProperties": True,
"properties": {
"type": non_empty_string,
- "version": non_empty_string
+ "version": non_empty_string,
+ "version_number": non_empty_string
}
},
"nics": nics,
diff --git a/trove/common/exception.py b/trove/common/exception.py
index f89bef1f..d4b44b0c 100644
--- a/trove/common/exception.py
+++ b/trove/common/exception.py
@@ -134,38 +134,38 @@ class DatastoresNotFound(NotFound):
class DatastoreFlavorAssociationNotFound(NotFound):
message = _("Flavor %(id)s is not supported for datastore "
- "%(datastore)s version %(datastore_version)s")
+ "version %(datastore_version_id)s")
class DatastoreFlavorAssociationAlreadyExists(TroveError):
message = _("Flavor %(id)s is already associated with "
- "datastore %(datastore)s version %(datastore_version)s")
+ "datastore version %(datastore_version_id)s")
class DatastoreVolumeTypeAssociationNotFound(NotFound):
message = _("The volume type %(id)s is not valid for datastore "
- "%(datastore)s and version %(version_id)s.")
+ "version %(datastore_version_id)s.")
class DatastoreVolumeTypeAssociationAlreadyExists(TroveError):
- message = _("Datastore '%(datastore)s' version %(datastore_version)s "
+ message = _("Datastore version %(datastore_version_id)s "
"and volume-type %(id)s mapping already exists.")
class DataStoreVersionVolumeTypeRequired(TroveError):
message = _("Only specific volume types are allowed for a "
- "datastore %(datastore)s version %(datastore_version)s. "
+ "datastore version %(datastore_version_id)s. "
"You must specify a valid volume type.")
class DatastoreVersionNoVolumeTypes(TroveError):
message = _("No valid volume types could be found for datastore "
- "%(datastore)s and version %(datastore_version)s.")
+ "version %(datastore_version_id)s.")
class DatastoreNoVersion(TroveError):
@@ -194,6 +194,13 @@ class DatastoreVersionsInUse(BadRequest):
message = _("Datastore version is in use by %(resource)s.")
+class DatastoreVersionsNoUniqueMatch(TroveError):
+
+ message = _("Multiple datastore versions found for '%(name)s', "
+ "use an UUID or specify both the name and version number to "
+ "be more specific.")
+
+
class DatastoreDefaultDatastoreNotFound(TroveError):
message = _("Please specify datastore. Default datastore "
@@ -222,12 +229,6 @@ class DatastoreOperationNotSupported(TroveError):
"the '%(datastore)s' datastore.")
-class NoUniqueMatch(TroveError):
-
- message = _("Multiple matches found for '%(name)s', "
- "use an UUID to be more specific.")
-
-
class OverLimit(TroveError):
# internal_message is used for log, stop translating.
diff --git a/trove/datastore/models.py b/trove/datastore/models.py
index bf1b8903..f63136f1 100644
--- a/trove/datastore/models.py
+++ b/trove/datastore/models.py
@@ -405,15 +405,25 @@ class DatastoreVersion(object):
return cls(DBDatastoreVersion.find_by(datastore_id=datastore.id,
id=id_or_name))
- version = version or id_or_name
- versions = DBDatastoreVersion.find_all(datastore_id=datastore.id,
- name=id_or_name,
- version=version)
- if versions.count() == 0:
- raise exception.DatastoreVersionNotFound(version=version)
- if versions.count() > 1:
- raise exception.NoUniqueMatch(name=id_or_name)
- return cls(versions.first())
+ if not version:
+ versions = DBDatastoreVersion.find_all(datastore_id=datastore.id,
+ name=id_or_name)
+ if versions.count() == 0:
+ raise exception.DatastoreVersionNotFound(version=id_or_name)
+ if versions.count() > 1:
+ raise exception.DatastoreVersionsNoUniqueMatch(name=id_or_name)
+
+ db_version = versions.first()
+ else:
+ try:
+ db_version = DBDatastoreVersion.find_by(
+ datastore_id=datastore.id,
+ name=id_or_name,
+ version=version)
+ except exception.ModelNotFoundError:
+ raise exception.DatastoreVersionNotFound(version=version)
+
+ return cls(db_version)
@classmethod
def load_by_uuid(cls, uuid):
@@ -508,7 +518,8 @@ class DatastoreVersions(object):
yield item
-def get_datastore_version(type=None, version=None, return_inactive=False):
+def get_datastore_version(type=None, version=None, return_inactive=False,
+ version_number=None):
datastore = type or CONF.default_datastore
if not datastore:
raise exception.DatastoreDefaultDatastoreNotDefined()
@@ -520,11 +531,12 @@ def get_datastore_version(type=None, version=None, return_inactive=False):
datastore=datastore)
raise
- version = version or datastore.default_version_id
- if not version:
+ version_id = version or datastore.default_version_id
+ if not version_id:
raise exception.DatastoreDefaultVersionNotFound(
datastore=datastore.name)
- datastore_version = DatastoreVersion.load(datastore, version)
+ datastore_version = DatastoreVersion.load(datastore, version_id,
+ version=version_number)
if datastore_version.datastore_id != datastore.id:
raise exception.DatastoreNoVersion(datastore=datastore.name,
version=datastore_version.name)
@@ -616,8 +628,8 @@ def update_datastore_version(datastore, name, manager, image_id, image_tags,
class DatastoreVersionMetadata(object):
@classmethod
- def _datastore_version_find(cls, datastore_name,
- datastore_version_name):
+ def datastore_version_find(cls, datastore_name,
+ datastore_version_name, version_number=None):
"""
Helper to find a datastore version id for a given
datastore and datastore version name.
@@ -626,17 +638,31 @@ class DatastoreVersionMetadata(object):
db_ds_record = DBDatastore.find_by(
name=datastore_name
)
- db_dsv_record = DBDatastoreVersion.find_by(
- datastore_id=db_ds_record.id,
- name=datastore_version_name
- )
+
+ if not version_number:
+ db_dsv_records = DBDatastoreVersion.find_all(
+ datastore_id=db_ds_record.id,
+ name=datastore_version_name,
+ )
+ if db_dsv_records.count() == 0:
+ raise exception.DatastoreVersionNotFound(
+ version=datastore_version_name)
+ if db_dsv_records.count() > 1:
+ raise exception.DatastoreVersionsNoUniqueMatch(
+ name=datastore_version_name)
+
+ db_dsv_record = db_dsv_records.first()
+ else:
+ db_dsv_record = DBDatastoreVersion.find_by(
+ datastore_id=db_ds_record.id,
+ name=datastore_version_name,
+ version=version_number
+ )
return db_dsv_record.id
@classmethod
- def _datastore_version_metadata_add(cls, datastore_name,
- datastore_version_name,
- datastore_version_id,
+ def _datastore_version_metadata_add(cls, datastore_version_id,
key, value, exception_class):
"""
Create a record of the specified key and value in the
@@ -657,8 +683,7 @@ class DatastoreVersionMetadata(object):
return
else:
raise exception_class(
- datastore=datastore_name,
- datastore_version=datastore_version_name,
+ datastore_version_id=datastore_version_id,
id=value)
except exception.NotFound:
pass
@@ -669,8 +694,7 @@ class DatastoreVersionMetadata(object):
key=key, value=value)
@classmethod
- def _datastore_version_metadata_delete(cls, datastore_name,
- datastore_version_name,
+ def _datastore_version_metadata_delete(cls, datastore_version_id,
key, value, exception_class):
"""
Delete a record of the specified key and value in the
@@ -679,11 +703,6 @@ class DatastoreVersionMetadata(object):
# if an association does not exist, raise an exception
# if a deleted association exists, raise an exception
# if an un-deleted association exists, delete it
-
- datastore_version_id = cls._datastore_version_find(
- datastore_name,
- datastore_version_name)
-
try:
db_record = DBDatastoreVersionMetadata.find_by(
datastore_version_id=datastore_version_id,
@@ -693,96 +712,69 @@ class DatastoreVersionMetadata(object):
return
else:
raise exception_class(
- datastore=datastore_name,
- datastore_version=datastore_version_name,
+ datastore_version_id=datastore_version_id,
id=value)
except exception.ModelNotFoundError:
- raise exception_class(datastore=datastore_name,
- datastore_version=datastore_version_name,
+ raise exception_class(datastore_version_id=datastore_version_id,
id=value)
@classmethod
- def add_datastore_version_flavor_association(cls, datastore_name,
- datastore_version_name,
+ def add_datastore_version_flavor_association(cls, datastore_version_id,
flavor_ids):
- datastore_version_id = cls._datastore_version_find(
- datastore_name,
- datastore_version_name)
-
for flavor_id in flavor_ids:
cls._datastore_version_metadata_add(
- datastore_name, datastore_version_name,
datastore_version_id, 'flavor', flavor_id,
exception.DatastoreFlavorAssociationAlreadyExists)
@classmethod
- def delete_datastore_version_flavor_association(cls, datastore_name,
- datastore_version_name,
+ def delete_datastore_version_flavor_association(cls, datastore_version_id,
flavor_id):
cls._datastore_version_metadata_delete(
- datastore_name, datastore_version_name, 'flavor', flavor_id,
+ datastore_version_id, 'flavor', flavor_id,
exception.DatastoreFlavorAssociationNotFound)
@classmethod
def list_datastore_version_flavor_associations(cls, context,
- datastore_type,
datastore_version_id):
- if datastore_type and datastore_version_id:
- """
- All nova flavors are permitted for a datastore_version unless
- one or more entries are found in datastore_version_metadata,
- in which case only those are permitted.
- """
- (datastore, datastore_version) = get_datastore_version(
- type=datastore_type, version=datastore_version_id)
- # If datastore_version_id and flavor key exists in the
- # metadata table return all the associated flavors for
- # that datastore version.
- nova_flavors = create_nova_client(context).flavors.list()
- bound_flavors = DBDatastoreVersionMetadata.find_all(
- datastore_version_id=datastore_version.id,
- key='flavor', deleted=False
- )
- if (bound_flavors.count() != 0):
- bound_flavors = tuple(f.value for f in bound_flavors)
- # Generate a filtered list of nova flavors
- ds_nova_flavors = (f for f in nova_flavors
- if f.id in bound_flavors)
- associated_flavors = tuple(flavor_model(flavor=item)
- for item in ds_nova_flavors)
- else:
- # Return all nova flavors if no flavor metadata found
- # for datastore_version.
- associated_flavors = tuple(flavor_model(flavor=item)
- for item in nova_flavors)
- return associated_flavors
+ """Get allowed flavors for a given datastore version.
+
+ All nova flavors are permitted for a datastore_version unless
+ one or more entries are found in datastore_version_metadata,
+ in which case only those are permitted.
+ """
+ nova_flavors = create_nova_client(context).flavors.list()
+ bound_flavors = DBDatastoreVersionMetadata.find_all(
+ datastore_version_id=datastore_version_id,
+ key='flavor', deleted=False
+ )
+ if (bound_flavors.count() != 0):
+ bound_flavors = tuple(f.value for f in bound_flavors)
+ # Generate a filtered list of nova flavors
+ ds_nova_flavors = (f for f in nova_flavors
+ if f.id in bound_flavors)
+ associated_flavors = tuple(flavor_model(flavor=item)
+ for item in ds_nova_flavors)
else:
- msg = _("Specify both the datastore and datastore_version_id.")
- raise exception.BadRequest(msg)
+ # Return all nova flavors if no flavor metadata found
+ # for datastore_version.
+ associated_flavors = tuple(flavor_model(flavor=item)
+ for item in nova_flavors)
+ return associated_flavors
@classmethod
- def add_datastore_version_volume_type_association(cls, datastore_name,
- datastore_version_name,
+ def add_datastore_version_volume_type_association(cls,
+ datastore_version_id,
volume_type_names):
- datastore_version_id = cls._datastore_version_find(
- datastore_name,
- datastore_version_name)
-
- # the database record will contain
- # datastore_version_id, 'volume_type', volume_type_name
for volume_type_name in volume_type_names:
cls._datastore_version_metadata_add(
- datastore_name, datastore_version_name,
datastore_version_id, 'volume_type', volume_type_name,
exception.DatastoreVolumeTypeAssociationAlreadyExists)
@classmethod
def delete_datastore_version_volume_type_association(
- cls, datastore_name,
- datastore_version_name,
- volume_type_name):
+ cls, datastore_version_id, volume_type_name):
cls._datastore_version_metadata_delete(
- datastore_name, datastore_version_name, 'volume_type',
+ datastore_version_id, 'volume_type',
volume_type_name,
exception.DatastoreVolumeTypeAssociationNotFound)
@@ -811,7 +803,7 @@ class DatastoreVersionMetadata(object):
List the datastore associations for a given datastore and version.
"""
if datastore_name and datastore_version_name:
- datastore_version_id = cls._datastore_version_find(
+ datastore_version_id = cls.datastore_version_find(
datastore_name, datastore_version_name)
return cls.list_datastore_version_volume_type_associations(
datastore_version_id)
@@ -820,17 +812,13 @@ class DatastoreVersionMetadata(object):
raise exception.BadRequest(msg)
@classmethod
- def datastore_volume_type_associations_exist(cls,
- datastore_name,
- datastore_version_name):
- return cls.list_datastore_volume_type_associations(
- datastore_name,
- datastore_version_name).count() > 0
+ def datastore_volume_type_associations_exist(cls, datastore_version_id):
+ return cls.list_datastore_version_volume_type_associations(
+ datastore_version_id).count() > 0
@classmethod
def allowed_datastore_version_volume_types(cls, context,
- datastore_name,
- datastore_version_name):
+ datastore_version_id):
"""
List all allowed volume types for a given datastore and
datastore version. If datastore version metadata is
@@ -838,59 +826,44 @@ class DatastoreVersionMetadata(object):
allowed. If datastore version metadata is not provided
then all volume types known to cinder are allowed.
"""
- if datastore_name and datastore_version_name:
- # first obtain the list in the dsvmetadata
- datastore_version_id = cls._datastore_version_find(
- datastore_name, datastore_version_name)
+ metadata = cls.list_datastore_version_volume_type_associations(
+ datastore_version_id)
- metadata = cls.list_datastore_version_volume_type_associations(
- datastore_version_id)
-
- # then get the list of all volume types
- all_volume_types = volume_type_models.VolumeTypes(context)
+ # then get the list of all volume types
+ all_volume_types = volume_type_models.VolumeTypes(context)
- # if there's metadata: intersect,
- # else, whatever cinder has.
- if (metadata.count() != 0):
- # the volume types from metadata first
- ds_volume_types = tuple(f.value for f in metadata)
-
- # Cinder volume type names are unique, intersect
- allowed_volume_types = tuple(
- f for f in all_volume_types
- if ((f.name in ds_volume_types) or
- (f.id in ds_volume_types)))
- else:
- allowed_volume_types = tuple(all_volume_types)
+ # if there's metadata: intersect,
+ # else, whatever cinder has.
+ if (metadata.count() != 0):
+ # the volume types from metadata first
+ ds_volume_types = tuple(f.value for f in metadata)
- return allowed_volume_types
+ # Cinder volume type names are unique, intersect
+ allowed_volume_types = tuple(
+ f for f in all_volume_types
+ if ((f.name in ds_volume_types) or
+ (f.id in ds_volume_types)))
else:
- msg = _("Specify the datastore_name and datastore_version_name.")
- raise exception.BadRequest(msg)
+ allowed_volume_types = tuple(all_volume_types)
+
+ return allowed_volume_types
@classmethod
- def validate_volume_type(cls, context, volume_type,
- datastore_name, datastore_version_name):
- if cls.datastore_volume_type_associations_exist(
- datastore_name, datastore_version_name):
+ def validate_volume_type(cls, context, volume_type, datastore_version_id):
+ if cls.datastore_volume_type_associations_exist(datastore_version_id):
allowed = cls.allowed_datastore_version_volume_types(
- context, datastore_name, datastore_version_name)
+ context, datastore_version_id)
if len(allowed) == 0:
raise exception.DatastoreVersionNoVolumeTypes(
- datastore=datastore_name,
- datastore_version=datastore_version_name)
+ datastore_version_id=datastore_version_id)
if volume_type is None:
raise exception.DataStoreVersionVolumeTypeRequired(
- datastore=datastore_name,
- datastore_version=datastore_version_name)
+ datastore_version_id=datastore_version_id)
allowed_names = tuple(f.name for f in allowed)
- for n in allowed_names:
- LOG.debug("Volume Type: %s is allowed for datastore "
- "%s, version %s." %
- (n, datastore_name, datastore_version_name))
+ LOG.debug(f"Allowed volume types: {allowed_names}")
+
if volume_type not in allowed_names:
raise exception.DatastoreVolumeTypeAssociationNotFound(
- datastore=datastore_name,
- version_id=datastore_version_name,
+ datastore_version_id=datastore_version_id,
id=volume_type)
diff --git a/trove/datastore/service.py b/trove/datastore/service.py
index b9366690..ada7b4ad 100644
--- a/trove/datastore/service.py
+++ b/trove/datastore/service.py
@@ -93,7 +93,7 @@ class DatastoreController(wsgi.Controller):
context = req.environ[wsgi.CONTEXT_KEY]
flavors = (models.DatastoreVersionMetadata.
list_datastore_version_flavor_associations(
- context, datastore, version_id))
+ context, version_id))
return wsgi.Result(flavor_views.FlavorsView(flavors, req).data(), 200)
def list_associated_volume_types(self, req, tenant_id, datastore,
@@ -106,7 +106,7 @@ class DatastoreController(wsgi.Controller):
context = req.environ[wsgi.CONTEXT_KEY]
volume_types = (models.DatastoreVersionMetadata.
allowed_datastore_version_volume_types(
- context, datastore, version_id))
+ context, version_id))
return wsgi.Result(volume_type_view.VolumeTypesView(
volume_types, req).data(), 200)
diff --git a/trove/instance/models.py b/trove/instance/models.py
index 01ae2938..80bef79d 100644
--- a/trove/instance/models.py
+++ b/trove/instance/models.py
@@ -1133,9 +1133,8 @@ class Instance(BuiltInstance):
valid_flavors = tuple(f.value for f in bound_flavors)
if flavor_id not in valid_flavors:
raise exception.DatastoreFlavorAssociationNotFound(
- datastore=datastore.name,
- datastore_version=datastore_version.name,
- flavor_id=flavor_id)
+ datastore_version_id=datastore_version.id,
+ id=flavor_id)
try:
flavor = nova_client.flavors.get(flavor_id)
except nova_exceptions.NotFound:
@@ -1166,7 +1165,7 @@ class Instance(BuiltInstance):
volume_size = volume.size
dvm.validate_volume_type(context, volume_type,
- datastore.name, datastore_version.name)
+ datastore_version.id)
validate_volume_size(volume_size)
call_args['volume_type'] = volume_type
call_args['volume_size'] = volume_size
@@ -1335,7 +1334,7 @@ class Instance(BuiltInstance):
nics, overrides, slave_of_id, cluster_config,
volume_type=volume_type, modules=module_list,
locality=locality, access=access,
- ds_version=datastore_version.name)
+ ds_version=datastore_version.version)
return SimpleInstance(context, db_info, service_status,
root_password, locality=locality)
diff --git a/trove/instance/views.py b/trove/instance/views.py
index e0413c18..96d8c796 100644
--- a/trove/instance/views.py
+++ b/trove/instance/views.py
@@ -129,6 +129,8 @@ class InstanceDetailView(InstanceView):
if self.instance.datastore_version:
result['instance']['datastore']['version'] = \
self.instance.datastore_version.name
+ result['instance']['datastore']['version_number'] = \
+ self.instance.datastore_version.version
if self.instance.fault:
result['instance']['fault'] = self._build_fault_info()
diff --git a/trove/tests/api/datastores.py b/trove/tests/api/datastores.py
index af8197e5..fe911f63 100644
--- a/trove/tests/api/datastores.py
+++ b/trove/tests/api/datastores.py
@@ -161,13 +161,9 @@ class DatastoreVersions(object):
@test
def test_datastore_version_not_found(self):
- try:
- assert_raises(exceptions.NotFound,
- self.rd_client.datastore_versions.get,
- self.datastore_active.name, NAME)
- except exceptions.BadRequest as e:
- assert_equal(e.message,
- "Datastore version '%s' cannot be found." % NAME)
+ assert_raises(exceptions.BadRequest,
+ self.rd_client.datastore_versions.get,
+ self.datastore_active.name, NAME)
@test
def test_datastore_version_list_by_uuid(self):
diff --git a/trove/tests/api/instances.py b/trove/tests/api/instances.py
index c36b692e..643e120e 100644
--- a/trove/tests/api/instances.py
+++ b/trove/tests/api/instances.py
@@ -204,7 +204,7 @@ class CheckInstance(AttrCheck):
if 'datastore' not in self.instance:
self.fail("'datastore' not found in instance.")
else:
- allowed_attrs = ['type', 'version']
+ allowed_attrs = ['type', 'version', 'version_number']
self.contains_allowed_attrs(
self.instance['datastore'], allowed_attrs,
msg="datastore")
@@ -714,18 +714,13 @@ class CreateInstanceFail(object):
users = []
datastore = CONFIG.dbaas_datastore
datastore_version = "nonexistent"
- try:
- assert_raises(exceptions.NotFound,
- dbaas.instances.create, instance_name,
- instance_info.dbaas_flavor_href,
- volume, databases, users,
- datastore=datastore,
- datastore_version=datastore_version,
- nics=instance_info.nics)
- except exceptions.BadRequest as e:
- assert_equal(e.message,
- "Datastore version '%s' cannot be found." %
- datastore_version)
+ assert_raises(exceptions.BadRequest,
+ dbaas.instances.create, instance_name,
+ instance_info.dbaas_flavor_href,
+ volume, databases, users,
+ datastore=datastore,
+ datastore_version=datastore_version,
+ nics=instance_info.nics)
@test(
diff --git a/trove/tests/unittests/datastore/base.py b/trove/tests/unittests/datastore/base.py
index bf38c09a..f543b77f 100644
--- a/trove/tests/unittests/datastore/base.py
+++ b/trove/tests/unittests/datastore/base.py
@@ -41,15 +41,15 @@ class TestDatastoreBase(trove_testtools.TestCase):
datastore_models.update_datastore_version(
cls.ds_name, cls.ds_version_name, "mysql", "", "", "", True)
- DatastoreVersionMetadata.add_datastore_version_flavor_association(
- cls.ds_name, cls.ds_version_name, [cls.flavor_id])
- DatastoreVersionMetadata.add_datastore_version_volume_type_association(
- cls.ds_name, cls.ds_version_name, [cls.volume_type])
-
cls.datastore_version = DatastoreVersion.load(cls.datastore,
cls.ds_version_name)
cls.test_id = cls.datastore_version.id
+ DatastoreVersionMetadata.add_datastore_version_flavor_association(
+ cls.datastore_version.id, [cls.flavor_id])
+ DatastoreVersionMetadata.add_datastore_version_volume_type_association(
+ cls.datastore_version.id, [cls.volume_type])
+
cls.cap1 = Capability.create(cls.capability_name,
cls.capability_desc, True)
cls.cap2 = Capability.create(
diff --git a/trove/tests/unittests/datastore/test_datastore_version_metadata.py b/trove/tests/unittests/datastore/test_datastore_version_metadata.py
index 2574ef10..964cab0d 100644
--- a/trove/tests/unittests/datastore/test_datastore_version_metadata.py
+++ b/trove/tests/unittests/datastore/test_datastore_version_metadata.py
@@ -57,48 +57,40 @@ class TestDatastoreVersionMetadata(TestDatastoreBase):
def test_add_existing_flavor_associations(self):
dsmetadata = datastore_models.DatastoreVersionMetadata
- self.assertRaisesRegex(
+ self.assertRaises(
exception.DatastoreFlavorAssociationAlreadyExists,
- "Flavor %s is already associated with datastore %s version %s"
- % (self.flavor_id, self.ds_name, self.ds_version_name),
dsmetadata.add_datastore_version_flavor_association,
- self.ds_name, self.ds_version_name, [self.flavor_id])
+ self.test_id, [self.flavor_id])
def test_add_existing_volume_type_associations(self):
dsmetadata = datastore_models.DatastoreVersionMetadata
self.assertRaises(
exception.DatastoreVolumeTypeAssociationAlreadyExists,
dsmetadata.add_datastore_version_volume_type_association,
- self.ds_name, self.ds_version_name, [self.volume_type])
+ self.test_id, [self.volume_type])
def test_delete_nonexistent_flavor_mapping(self):
dsmeta = datastore_models.DatastoreVersionMetadata
- self.assertRaisesRegex(
+ self.assertRaises(
exception.DatastoreFlavorAssociationNotFound,
- "Flavor 2 is not supported for datastore %s version %s"
- % (self.ds_name, self.ds_version_name),
dsmeta.delete_datastore_version_flavor_association,
- self.ds_name, self.ds_version_name, flavor_id=2)
+ self.test_id, flavor_id=2)
def test_delete_nonexistent_volume_type_mapping(self):
dsmeta = datastore_models.DatastoreVersionMetadata
self.assertRaises(
exception.DatastoreVolumeTypeAssociationNotFound,
dsmeta.delete_datastore_version_volume_type_association,
- self.ds_name, self.ds_version_name,
+ self.test_id,
volume_type_name='some random thing')
def test_delete_flavor_mapping(self):
flavor_id = 2
dsmetadata = datastore_models.DatastoreVersionMetadata
dsmetadata.add_datastore_version_flavor_association(
- self.ds_name,
- self.ds_version_name,
- [flavor_id])
+ self.test_id, [flavor_id])
dsmetadata.delete_datastore_version_flavor_association(
- self.ds_name,
- self.ds_version_name,
- flavor_id)
+ self.test_id, flavor_id)
datastore = datastore_models.Datastore.load(self.ds_name)
ds_version = datastore_models.DatastoreVersion.load(
datastore,
@@ -108,27 +100,22 @@ class TestDatastoreVersionMetadata(TestDatastoreBase):
self.assertTrue(mapping.deleted)
# check update
dsmetadata.add_datastore_version_flavor_association(
- self.ds_name, self.ds_version_name, [flavor_id])
+ self.test_id, [flavor_id])
mapping = datastore_models.DBDatastoreVersionMetadata.find_by(
datastore_version_id=ds_version.id, value=flavor_id, key='flavor')
self.assertFalse(mapping.deleted)
# clear the mapping
datastore_models.DatastoreVersionMetadata. \
- delete_datastore_version_flavor_association(self.ds_name,
- self.ds_version_name,
+ delete_datastore_version_flavor_association(self.test_id,
flavor_id)
def test_delete_volume_type_mapping(self):
volume_type = 'this is bogus'
dsmetadata = datastore_models.DatastoreVersionMetadata
dsmetadata.add_datastore_version_volume_type_association(
- self.ds_name,
- self.ds_version_name,
- [volume_type])
+ self.test_id, [volume_type])
dsmetadata.delete_datastore_version_volume_type_association(
- self.ds_name,
- self.ds_version_name,
- volume_type)
+ self.test_id, volume_type)
datastore = datastore_models.Datastore.load(self.ds_name)
ds_version = datastore_models.DatastoreVersion.load(
datastore,
@@ -139,19 +126,17 @@ class TestDatastoreVersionMetadata(TestDatastoreBase):
self.assertTrue(mapping.deleted)
# check update
dsmetadata.add_datastore_version_volume_type_association(
- self.ds_name, self.ds_version_name, [volume_type])
+ self.test_id, [volume_type])
mapping = datastore_models.DBDatastoreVersionMetadata.find_by(
datastore_version_id=ds_version.id, value=volume_type,
key='volume_type')
self.assertFalse(mapping.deleted)
# clear the mapping
dsmetadata.delete_datastore_version_volume_type_association(
- self.ds_name,
- self.ds_version_name,
- volume_type)
+ self.test_id, volume_type)
@mock.patch.object(datastore_models.DatastoreVersionMetadata,
- '_datastore_version_find')
+ 'datastore_version_find')
@mock.patch.object(datastore_models.DatastoreVersionMetadata,
'list_datastore_version_volume_type_associations')
@mock.patch.object(clients, 'create_cinder_client')
@@ -179,7 +164,7 @@ class TestDatastoreVersionMetadata(TestDatastoreBase):
mock_list.return_value = mock_trove_list_result
return self.dsmetadata.allowed_datastore_version_volume_types(
- None, 'ds', 'dsv')
+ None, self.random_uuid())
def _assert_equal_types(self, test_dict, output_obj):
self.assertEqual(test_dict.get('id'), output_obj.id)
diff --git a/trove/tests/unittests/instance/test_service.py b/trove/tests/unittests/instance/test_service.py
index 07444a4c..a38c62f5 100644
--- a/trove/tests/unittests/instance/test_service.py
+++ b/trove/tests/unittests/instance/test_service.py
@@ -13,7 +13,9 @@
# limitations under the License.
from unittest import mock
+from trove.common import cfg
from trove.common import clients
+from trove.common import exception
from trove.datastore import models as ds_models
from trove.instance import models as ins_models
from trove.instance import service
@@ -21,6 +23,8 @@ from trove.instance import service_status as srvstatus
from trove.tests.unittests import trove_testtools
from trove.tests.unittests.util import util
+CONF = cfg.CONF
+
class TestInstanceController(trove_testtools.TestCase):
@classmethod
@@ -37,7 +41,14 @@ class TestInstanceController(trove_testtools.TestCase):
1)
ds_models.update_datastore_version(
cls.ds_name, 'test_image_tags', 'mysql', '', ['trove', 'mysql'],
- '', 1)
+ '', 1, version='test_image_tags version')
+ ds_models.update_datastore_version(
+ cls.ds_name, 'test_version', 'mysql', '', ['trove'], '', 1,
+ version='version 1')
+ ds_models.update_datastore_version(
+ cls.ds_name, 'test_version', 'mysql', '', ['trove'], '', 1,
+ version='version 2')
+
cls.ds_version_imageid = ds_models.DatastoreVersion.load(
cls.ds, 'test_image_id')
cls.ds_version_imagetags = ds_models.DatastoreVersion.load(
@@ -61,19 +72,22 @@ class TestInstanceController(trove_testtools.TestCase):
@mock.patch('trove.instance.models.Instance.create')
def test_create_by_ds_version_image_tags(self, mock_model_create,
mock_create_client):
+ image_id = self.random_uuid()
mock_glance_client = mock.MagicMock()
- mock_glance_client.images.list.return_value = [
- {'id': self.random_uuid()}]
+ mock_glance_client.images.list.return_value = [{'id': image_id}]
mock_create_client.return_value = mock_glance_client
+ name = self.random_name(name='instance',
+ prefix='TestInstanceController')
+ flavor = self.random_uuid()
body = {
'instance': {
- 'name': self.random_name(name='instance',
- prefix='TestInstanceController'),
- 'flavorRef': self.random_uuid(),
+ 'name': name,
+ 'flavorRef': flavor,
'datastore': {
'type': self.ds_name,
- 'version': self.ds_version_imagetags.name
+ 'version': self.ds_version_imagetags.name,
+ 'version_number': self.ds_version_imagetags.version
}
}
}
@@ -85,6 +99,40 @@ class TestInstanceController(trove_testtools.TestCase):
sort='created_at:desc', limit=1
)
+ mock_model_create.assert_called_once_with(
+ mock.ANY, name, flavor, image_id,
+ [], [],
+ mock.ANY, mock.ANY,
+ None, None, None, [], None, None,
+ replica_count=None, volume_type=None, modules=None, locality=None,
+ region_name=CONF.service_credentials.region_name, access=None
+ )
+ args = mock_model_create.call_args[0]
+ actual_ds_version = args[7]
+ self.assertEqual(self.ds_version_imagetags.name,
+ actual_ds_version.name)
+ self.assertEqual(self.ds_version_imagetags.version,
+ actual_ds_version.version)
+
+ def test_create_multiple_versions(self):
+ body = {
+ 'instance': {
+ 'name': self.random_name(name='instance',
+ prefix='TestInstanceController'),
+ 'flavorRef': self.random_uuid(),
+ 'datastore': {
+ 'type': self.ds_name,
+ 'version': 'test_version'
+ }
+ }
+ }
+
+ self.assertRaises(
+ exception.DatastoreVersionsNoUniqueMatch,
+ self.controller.create,
+ mock.MagicMock(), body, mock.ANY
+ )
+
@mock.patch.object(clients, 'create_nova_client',
return_value=mock.MagicMock())
@mock.patch('trove.rpc.get_client')