Merge "Support datastore version number for creating instance"
This commit is contained in:
commit
c19c564694
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
# 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)
|
||||
|
||||
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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue