Glance Metadata Definitions Catalog - DB

Implements: blueprint metadata-schema-catalog

A common API hosted by the Glance service for vendors, admins,
services, and users to meaningfully define available key / value
pair and tag metadata. The intent is to enable better metadata
collaboration across artifacts, services, and projects for
OpenStack users.

This is about the definition of the available metadata that can
be used on different types of resources (images, artifacts,
volumes, flavors, aggregates, etc). A definition includes the
properties type, its key, it's description, and it's constraints.
This catalogue will not store the values for specific instance
properties.

Change-Id: I01cc63df3d3abe383c94cfb54598868b4bb729bb
DocImpact
Co-Authored-By: Lakshmi N Sampath <lakshmi.sampath@hp.com>
Co-Authored-By: Wayne Okuma <wayne.okuma@hp.com>
Co-Authored-By: Travis Tripp <travis.tripp@hp.com>
Co-Authored-By: Pawel Koniszewski <pawel.koniszewski@intel.com>
Co-Authored-By: Michal Jastrzebski <michal.jastrzebski@intel.com>
Co-Authored-By: Michal Dulko <michal.dulko@intel.com>
This commit is contained in:
Pawel Koniszewski 2014-08-28 10:45:53 -04:00
parent 640dd1ee89
commit 7454aac835
17 changed files with 3238 additions and 0 deletions

View File

@ -130,6 +130,31 @@ class ProtectedImageDelete(Forbidden):
message = _("Image %(image_id)s is protected and cannot be deleted.")
class ProtectedMetadefNamespaceDelete(Forbidden):
message = _("Metadata definition namespace %(namespace)s is protected"
" and cannot be deleted.")
class ProtectedMetadefNamespacePropDelete(Forbidden):
message = _("Metadata definition property %(property_name)s is protected"
" and cannot be deleted.")
class ProtectedMetadefObjectDelete(Forbidden):
message = _("Metadata definition object %(object_name)s is protected"
" and cannot be deleted.")
class ProtectedMetadefResourceTypeAssociationDelete(Forbidden):
message = _("Metadata definition resource-type-association"
" %(resource_type)s is protected and cannot be deleted.")
class ProtectedMetadefResourceTypeSystemDelete(Forbidden):
message = _("Metadata definition resource-type %(resource_type_name)s is"
" a seeded-system type and cannot be deleted.")
class Invalid(GlanceException):
message = _("Data supplied was not valid.")
@ -353,3 +378,74 @@ class InvalidParameterValue(Invalid):
class InvalidImageStatusTransition(Invalid):
message = _("Image status transition from %(cur_status)s to"
" %(new_status)s is not allowed")
class MetadefDuplicateNamespace(Duplicate):
message = _("The metadata definition namespace=%(namespace_name)s"
" already exists.")
class MetadefDuplicateObject(Duplicate):
message = _("A metadata definition object with name=%(object_name)s"
" already exists in namespace=%(namespace_name)s.")
class MetadefDuplicateProperty(Duplicate):
message = _("A metadata definition property with name=%(property_name)s"
" already exists in namespace=%(namespace_name)s.")
class MetadefDuplicateResourceType(Duplicate):
message = _("A metadata definition resource-type with"
" name=%(resource_type_name)s already exists.")
class MetadefDuplicateResourceTypeAssociation(Duplicate):
message = _("The metadata definition resource-type association of"
" resource-type=%(resource_type_name)s to"
" namespace=%(namespace_name)s"
" already exists.")
class MetadefForbidden(Forbidden):
message = _("You are not authorized to complete this action.")
class MetadefIntegrityError(Forbidden):
message = _("The metadata definition %(record_type)s with"
" name=%(record_name)s not deleted."
" Other records still refer to it.")
class MetadefNamespaceNotFound(NotFound):
message = _("Metadata definition namespace=%(namespace_name)s"
"was not found.")
class MetadefObjectNotFound(NotFound):
message = _("The metadata definition object with"
" name=%(object_name)s was not found in"
" namespace=%(namespace_name)s.")
class MetadefPropertyNotFound(NotFound):
message = _("The metadata definition property with"
" name=%(property_name)s was not found in"
" namespace=%(namespace_name)s.")
class MetadefResourceTypeNotFound(NotFound):
message = _("The metadata definition resource-type with"
" name=%(resource_type_name)s, was not found.")
class MetadefResourceTypeAssociationNotFound(NotFound):
message = _("The metadata definition resource-type association of"
" resource-type=%(resource_type_name)s to"
" namespace=%(namespace_name)s,"
" was not found.")
class MetadefRecordNotFound(NotFound):
message = _("Metadata definition %(record_type)s record not found"
" for id %(id)s.")

View File

@ -262,3 +262,214 @@ def task_get_all(client, filters=None, marker=None, limit=None,
def task_create(client, values, session=None):
"""Create a task object"""
return client.task_create(values=values)
# Metadef
@_get_client
def metadef_namespace_get_all(
client, marker=None, limit=None, sort_key='created_at',
sort_dir=None, filters=None, session=None):
return client.metadef_namespace_get_all(
marker=marker, limit=limit,
sort_key=sort_key, sort_dir=sort_dir, filters=filters)
@_get_client
def metadef_namespace_get(client, namespace_name, session=None):
return client.metadef_namespace_get(namespace_name=namespace_name)
@_get_client
def metadef_namespace_create(client, values, session=None):
return client.metadef_namespace_create(values=values)
@_get_client
def metadef_namespace_update(
client, namespace_id, namespace_dict,
session=None):
return client.metadef_namespace_update(
namespace_id=namespace_id, namespace_dict=namespace_dict)
@_get_client
def metadef_namespace_delete(client, namespace_name, session=None):
return client.metadef_namespace_delete(
namespace_name=namespace_name)
@_get_client
def metadef_object_get_all(client, namespace_name, session=None):
return client.metadef_object_get_all(
namespace_name=namespace_name)
@_get_client
def metadef_object_get(
client,
namespace_name, object_name, session=None):
return client.metadef_object_get(
namespace_name=namespace_name, object_name=object_name)
@_get_client
def metadef_object_create(
client,
namespace_name, object_dict, session=None):
return client.metadef_object_create(
namespace_name=namespace_name, object_dict=object_dict)
@_get_client
def metadef_object_update(
client,
namespace_name, object_id,
object_dict, session=None):
return client.metadef_object_update(
namespace_name=namespace_name, object_id=object_id,
object_dict=object_dict)
@_get_client
def metadef_object_delete(
client,
namespace_name, object_name,
session=None):
return client.metadef_object_delete(
namespace_name=namespace_name, object_name=object_name)
@_get_client
def metadef_object_delete_namespace_content(
client,
namespace_name, session=None):
return client.metadef_object_delete_namespace_content(
namespace_name=namespace_name)
@_get_client
def metadef_object_count(
client,
namespace_name, session=None):
return client.metadef_object_count(
namespace_name=namespace_name)
@_get_client
def metadef_property_get_all(
client,
namespace_name, session=None):
return client.metadef_property_get_all(
namespace_name=namespace_name)
@_get_client
def metadef_property_get(
client,
namespace_name, property_name,
session=None):
return client.metadef_property_get(
namespace_name=namespace_name, property_name=property_name)
@_get_client
def metadef_property_create(
client,
namespace_name, property_dict,
session=None):
return client.metadef_property_create(
namespace_name=namespace_name, property_dict=property_dict)
@_get_client
def metadef_property_update(
client,
namespace_name, property_id,
property_dict, session=None):
return client.metadef_property_update(
namespace_name=namespace_name, property_id=property_id,
property_dict=property_dict)
@_get_client
def metadef_property_delete(
client,
namespace_name, property_name,
session=None):
return client.metadef_property_delete(
namespace_name=namespace_name, property_name=property_name)
@_get_client
def metadef_property_delete_namespace_content(
client,
namespace_name, session=None):
return client.metadef_property_delete_namespace_content(
namespace_name=namespace_name)
@_get_client
def metadef_property_count(
client,
namespace_name, session=None):
return client.metadef_property_count(
namespace_name=namespace_name)
@_get_client
def metadef_resource_type_create(client, values, session=None):
return client.metadef_resource_type_create(values=values)
@_get_client
def metadef_resource_type_get(
client,
resource_type_name, session=None):
return client.metadef_resource_type_get(
resource_type_name=resource_type_name)
@_get_client
def metadef_resource_type_get_all(client, session=None):
return client.metadef_resource_type_get_all()
@_get_client
def metadef_resource_type_delete(
client,
resource_type_name, session=None):
return client.metadef_resource_type_delete(
resource_type_name=resource_type_name)
@_get_client
def metadef_resource_type_association_get(
client,
namespace_name, resource_type_name,
session=None):
return client.metadef_resource_type_association_get(
namespace_name=namespace_name, resource_type_name=resource_type_name)
@_get_client
def metadef_resource_type_association_create(
client,
namespace_name, values, session=None):
return client.metadef_resource_type_association_create(
namespace_name=namespace_name, values=values)
@_get_client
def metadef_resource_type_association_delete(
client,
namespace_name, resource_type_name, session=None):
return client.metadef_resource_type_association_delete(
namespace_name=namespace_name, resource_type_name=resource_type_name)
@_get_client
def metadef_resource_type_association_get_all_by_namespace(
client,
namespace_name, session=None):
return client.metadef_resource_type_association_get_all_by_namespace(
namespace_name=namespace_name)

View File

@ -30,12 +30,19 @@ LOG = logging.getLogger(__name__)
DATA = {
'images': {},
'members': {},
'metadef_namespace_resource_types': [],
'metadef_namespaces': [],
'metadef_objects': [],
'metadef_properties': [],
'metadef_resource_types': [],
'tags': {},
'locations': [],
'tasks': {},
'task_info': {}
}
INDEX = 0
def log_call(func):
@functools.wraps(func)
@ -57,6 +64,11 @@ def reset():
DATA = {
'images': {},
'members': [],
'metadef_namespace_resource_types': [],
'metadef_namespaces': [],
'metadef_objects': [],
'metadef_properties': [],
'metadef_resource_types': [],
'tags': {},
'locations': [],
'tasks': {},
@ -1017,3 +1029,737 @@ def _task_info_get(task_id):
raise exception.TaskNotFound(task_id=task_id)
return task_info
@log_call
def metadef_namespace_create(context, values):
"""Create a namespace object"""
global DATA
namespace_values = copy.deepcopy(values)
namespace_name = namespace_values.get('namespace')
required_attributes = ['namespace', 'owner']
allowed_attributes = ['namespace', 'owner', 'display_name', 'description',
'visibility', 'protected']
for namespace in DATA['metadef_namespaces']:
if namespace['namespace'] == namespace_name:
msg = ("Can not create the metadata definition namespace. "
"Namespace=%s already exists.") % namespace_name
LOG.debug(msg)
raise exception.MetadefDuplicateNamespace(
namespace_name=namespace_name)
for key in required_attributes:
if key not in namespace_values:
raise exception.Invalid('%s is a required attribute' % key)
incorrect_keys = set(namespace_values.keys()) - set(allowed_attributes)
if incorrect_keys:
raise exception.Invalid(
'The keys %s are not valid' % str(incorrect_keys))
namespace = _format_namespace(namespace_values)
DATA['metadef_namespaces'].append(namespace)
return namespace
@log_call
def metadef_namespace_update(context, namespace_id, values):
"""Update a namespace object"""
global DATA
namespace_values = copy.deepcopy(values)
namespace = metadef_namespace_get_by_id(context, namespace_id)
if namespace['namespace'] != values['namespace']:
for db_namespace in DATA['metadef_namespaces']:
if db_namespace['namespace'] == values['namespace']:
msg = ("Invalid update. It would result in a duplicate"
" metadata definition namespace with the same"
" name of %s"
% values['namespace'])
LOG.debug(msg)
emsg = (_("Invalid update. It would result in a duplicate"
" metadata definition namespace with the same"
" name of %s")
% values['namespace'])
raise exception.MetadefDuplicateNamespace(emsg)
DATA['metadef_namespaces'].remove(namespace)
namespace.update(namespace_values)
namespace['updated_at'] = timeutils.utcnow()
DATA['metadef_namespaces'].append(namespace)
return namespace
@log_call
def metadef_namespace_get_by_id(context, namespace_id):
"""Get a namespace object"""
try:
namespace = next(namespace for namespace in DATA['metadef_namespaces']
if namespace['id'] == namespace_id)
except StopIteration:
msg = "No namespace found with id %s" % namespace_id
LOG.debug(msg)
raise exception.MetadefRecordNotFound(
record_type='namespace', id=namespace_id)
if not _is_namespace_visible(context, namespace):
msg = ("Forbidding request, metadata definition namespace=%s"
" is not visible.") % namespace.namespace
LOG.debug(msg)
emsg = _("Forbidding request, metadata definition namespace=%s"
" is not visible.") % namespace.namespace
raise exception.MetadefForbidden(emsg)
return namespace
@log_call
def metadef_namespace_get(context, namespace_name):
"""Get a namespace object"""
try:
namespace = next(namespace for namespace in DATA['metadef_namespaces']
if namespace['namespace'] == namespace_name)
except StopIteration:
msg = "No namespace found with name %s" % namespace_name
LOG.debug(msg)
raise exception.MetadefNamespaceNotFound(
namespace_name=namespace_name)
_check_namespace_visibility(context, namespace, namespace_name)
return namespace
@log_call
def metadef_namespace_get_all(context,
marker=None,
limit=None,
sort_key='created_at',
sort_dir='desc',
filters=None):
"""Get a namespaces list"""
resource_types = filters.get('resource_types', []) if filters else []
visibility = filters.get('visibility', None) if filters else None
namespaces = []
for namespace in DATA['metadef_namespaces']:
if not _is_namespace_visible(context, namespace):
continue
if visibility and namespace['visibility'] != visibility:
continue
if resource_types:
for association in DATA['metadef_namespace_resource_types']:
if association['namespace_id'] == namespace['id']:
if association['name'] in resource_types:
break
else:
continue
namespaces.append(namespace)
return namespaces
@log_call
def metadef_namespace_delete(context, namespace_name):
"""Delete a namespace object"""
global DATA
namespace = metadef_namespace_get(context, namespace_name)
DATA['metadef_namespaces'].remove(namespace)
return namespace
@log_call
def metadef_namespace_delete_content(context, namespace_name):
"""Delete a namespace content"""
global DATA
namespace = metadef_namespace_get(context, namespace_name)
namespace_id = namespace['id']
objects = []
for object in DATA['metadef_objects']:
if object['namespace_id'] != namespace_id:
objects.append(object)
DATA['metadef_objects'] = objects
properties = []
for property in DATA['metadef_objects']:
if property['namespace_id'] != namespace_id:
properties.append(object)
DATA['metadef_objects'] = properties
return namespace
@log_call
def metadef_object_get(context, namespace_name, object_name):
"""Get a metadef object"""
namespace = metadef_namespace_get(context, namespace_name)
_check_namespace_visibility(context, namespace, namespace_name)
for object in DATA['metadef_objects']:
if (object['namespace_id'] == namespace['id'] and
object['name'] == object_name):
return object
else:
msg = ("The metadata definition object with name=%(name)s"
" was not found in namespace=%(namespace_name)s."
% {'name': object_name, 'namespace_name': namespace_name})
LOG.debug(msg)
raise exception.MetadefObjectNotFound(namespace_name=namespace_name,
object_name=object_name)
@log_call
def metadef_object_get_by_id(context, namespace_name, object_id):
"""Get a metadef object"""
namespace = metadef_namespace_get(context, namespace_name)
_check_namespace_visibility(context, namespace, namespace_name)
for object in DATA['metadef_objects']:
if (object['namespace_id'] == namespace['id'] and
object['id'] == object_id):
return object
else:
msg = ("No metadata definition object found with id %s"
% object_id)
LOG.debug(msg)
raise exception.MetadefRecordNotFound(record_type='object',
id=object_id)
@log_call
def metadef_object_get_all(context, namespace_name):
"""Get a metadef objects list"""
namespace = metadef_namespace_get(context, namespace_name)
objects = []
_check_namespace_visibility(context, namespace, namespace_name)
for object in DATA['metadef_objects']:
if object['namespace_id'] == namespace['id']:
objects.append(object)
return objects
@log_call
def metadef_object_create(context, namespace_name, values):
"""Create a metadef object"""
global DATA
object_values = copy.deepcopy(values)
object_name = object_values['name']
required_attributes = ['name']
allowed_attributes = ['name', 'description', 'schema', 'required']
namespace = metadef_namespace_get(context, namespace_name)
for object in DATA['metadef_objects']:
if (object['name'] == object_name and
object['namespace_id'] == namespace['id']):
msg = ("A metadata definition object with name=%(name)s"
" in namespace=%(namespace_name)s already exists."
% {'name': object_name, 'namespace_name': namespace_name})
LOG.debug(msg)
raise exception.MetadefDuplicateObject(
object_name=object_name, namespace_name=namespace_name)
for key in required_attributes:
if key not in object_values:
raise exception.Invalid('%s is a required attribute' % key)
incorrect_keys = set(object_values.keys()) - set(allowed_attributes)
if incorrect_keys:
raise exception.Invalid(
'The keys %s are not valid' % str(incorrect_keys))
object_values['namespace_id'] = namespace['id']
_check_namespace_visibility(context, namespace, namespace_name)
object = _format_object(object_values)
DATA['metadef_objects'].append(object)
return object
@log_call
def metadef_object_update(context, namespace_name, object_id, values):
"""Update a metadef object"""
global DATA
namespace = metadef_namespace_get(context, namespace_name)
_check_namespace_visibility(context, namespace, namespace_name)
object = metadef_object_get_by_id(context, namespace_name, object_id)
if object['name'] != values['name']:
for db_object in DATA['metadef_objects']:
if (db_object['name'] == values['name'] and
db_object['namespace_id'] == namespace['id']):
msg = ("Invalid update. It would result in a duplicate"
" metadata definition object with same name=%(name)s "
" in namespace=%(namespace_name)s."
% {'name': object['name'],
'namespace_name': namespace_name})
LOG.debug(msg)
emsg = (_("Invalid update. It would result in a duplicate"
" metadata definition object with the same"
" name=%(name)s "
" in namespace=%(namespace_name)s.")
% {'name': object['name'],
'namespace_name': namespace_name})
raise exception.MetadefDuplicateObject(emsg)
DATA['metadef_objects'].remove(object)
object.update(values)
object['updated_at'] = timeutils.utcnow()
DATA['metadef_objects'].append(object)
return object
@log_call
def metadef_object_delete(context, namespace_name, object_name):
"""Delete a metadef object"""
global DATA
object = metadef_object_get(context, namespace_name, object_name)
DATA['metadef_objects'].remove(object)
return object
@log_call
def metadef_object_count(context, namespace_name):
"""Get metadef object count in a namespace"""
namespace = metadef_namespace_get(context, namespace_name)
_check_namespace_visibility(context, namespace, namespace_name)
count = 0
for object in DATA['metadef_objects']:
if object['namespace_id'] == namespace['id']:
count = count + 1
return count
@log_call
def metadef_property_count(context, namespace_name):
"""Get properties count in a namespace"""
namespace = metadef_namespace_get(context, namespace_name)
_check_namespace_visibility(context, namespace, namespace_name)
count = 0
for property in DATA['metadef_properties']:
if property['namespace_id'] == namespace['id']:
count = count + 1
return count
@log_call
def metadef_property_create(context, namespace_name, values):
"""Create a metadef property"""
global DATA
property_values = copy.deepcopy(values)
property_name = property_values['name']
required_attributes = ['name']
allowed_attributes = ['name', 'description', 'schema', 'required']
namespace = metadef_namespace_get(context, namespace_name)
for property in DATA['metadef_properties']:
if (property['name'] == property_name and
property['namespace_id'] == namespace['id']):
msg = ("Can not create metadata definition property. A property"
" with name=%(name)s already exists in"
" namespace=%(namespace_name)s."
% {'name': property_name,
'namespace_name': namespace_name})
LOG.debug(msg)
raise exception.MetadefDuplicateProperty(
property_name=property_name,
namespace_name=namespace_name)
for key in required_attributes:
if key not in property_values:
raise exception.Invalid('%s is a required attribute' % key)
incorrect_keys = set(property_values.keys()) - set(allowed_attributes)
if incorrect_keys:
raise exception.Invalid(
'The keys %s are not valid' % str(incorrect_keys))
property_values['namespace_id'] = namespace['id']
_check_namespace_visibility(context, namespace, namespace_name)
property = _format_property(property_values)
DATA['metadef_properties'].append(property)
return property
@log_call
def metadef_property_update(context, namespace_name, property_id, values):
"""Update a metadef property"""
global DATA
namespace = metadef_namespace_get(context, namespace_name)
_check_namespace_visibility(context, namespace, namespace_name)
property = metadef_property_get_by_id(context, namespace_name, property_id)
if property['name'] != values['name']:
for db_property in DATA['metadef_properties']:
if (db_property['name'] == values['name'] and
db_property['namespace_id'] == namespace['id']):
msg = ("Invalid update. It would result in a duplicate"
" metadata definition property with the same"
" name=%(name)s"
" in namespace=%(namespace_name)s."
% {'name': property['name'],
'namespace_name': namespace_name})
LOG.debug(msg)
emsg = (_("Invalid update. It would result in a duplicate"
" metadata definition property with the same"
" name=%(name)s"
" in namespace=%(namespace_name)s.")
% {'name': property['name'],
'namespace_name': namespace_name})
raise exception.MetadefDuplicateProperty(emsg)
DATA['metadef_properties'].remove(property)
property.update(values)
property['updated_at'] = timeutils.utcnow()
DATA['metadef_properties'].append(property)
return property
@log_call
def metadef_property_get_all(context, namespace_name):
"""Get a metadef properties list"""
namespace = metadef_namespace_get(context, namespace_name)
properties = []
_check_namespace_visibility(context, namespace, namespace_name)
for property in DATA['metadef_properties']:
if property['namespace_id'] == namespace['id']:
properties.append(property)
return properties
@log_call
def metadef_property_get_by_id(context, namespace_name, property_id):
"""Get a metadef property"""
namespace = metadef_namespace_get(context, namespace_name)
_check_namespace_visibility(context, namespace, namespace_name)
for property in DATA['metadef_properties']:
if (property['namespace_id'] == namespace['id'] and
property['id'] == property_id):
return property
else:
msg = ("No metadata definition property found with id=%s"
% property_id)
LOG.debug(msg)
raise exception.MetadefRecordNotFound(record_type='property',
id=property_id)
@log_call
def metadef_property_get(context, namespace_name, property_name):
"""Get a metadef property"""
namespace = metadef_namespace_get(context, namespace_name)
_check_namespace_visibility(context, namespace, namespace_name)
for property in DATA['metadef_properties']:
if (property['namespace_id'] == namespace['id'] and
property['name'] == property_name):
return property
else:
msg = ("No property found with name=%(name)s in"
" namespace=%(namespace_name)s "
% {'name': property_name, 'namespace_name': namespace_name})
LOG.debug(msg)
raise exception.MetadefPropertyNotFound(namespace_name=namespace_name,
property_name=property_name)
@log_call
def metadef_property_delete(context, namespace_name, property_name):
"""Delete a metadef property"""
global DATA
property = metadef_property_get(context, namespace_name, property_name)
DATA['metadef_properties'].remove(property)
return property
@log_call
def metadef_resource_type_create(context, values):
"""Create a metadef resource type"""
global DATA
resource_type_values = copy.deepcopy(values)
resource_type_name = resource_type_values['name']
allowed_attrubites = ['name', 'protected']
for resource_type in DATA['metadef_resource_types']:
if resource_type['name'] == resource_type_name:
raise exception.Duplicate()
incorrect_keys = set(resource_type_values.keys()) - set(allowed_attrubites)
if incorrect_keys:
raise exception.Invalid(
'The keys %s are not valid' % str(incorrect_keys))
resource_type = _format_resource_type(resource_type_values)
DATA['metadef_resource_types'].append(resource_type)
return resource_type
@log_call
def metadef_resource_type_get_all(context):
"""List all resource types"""
return DATA['metadef_resource_types']
@log_call
def metadef_resource_type_get(context, resource_type_name):
"""Get a resource type"""
try:
resource_type = next(resource_type for resource_type in
DATA['metadef_resource_types']
if resource_type['name'] ==
resource_type_name)
except StopIteration:
msg = "No resource type found with name %s" % resource_type_name
LOG.debug(msg)
raise exception.MetadefResourceTypeNotFound(
resource_type_name=resource_type_name)
return resource_type
@log_call
def metadef_resource_type_association_create(context, namespace_name,
values):
global DATA
association_values = copy.deepcopy(values)
namespace = metadef_namespace_get(context, namespace_name)
resource_type_name = association_values['name']
resource_type = metadef_resource_type_get(context,
resource_type_name)
required_attributes = ['name', 'properties_target', 'prefix']
allowed_attributes = copy.deepcopy(required_attributes)
for association in DATA['metadef_namespace_resource_types']:
if (association['namespace_id'] == namespace['id'] and
association['resource_type'] == resource_type['id']):
msg = ("The metadata definition resource-type association of"
" resource_type=%(resource_type_name)s to"
" namespace=%(namespace_name)s, already exists."
% {'resource_type_name': resource_type_name,
'namespace_name': namespace_name})
LOG.debug(msg)
raise exception.MetadefDuplicateResourceTypeAssociation(
resource_type_name=resource_type_name,
namespace_name=namespace_name)
for key in required_attributes:
if key not in association_values:
raise exception.Invalid('%s is a required attribute' % key)
incorrect_keys = set(association_values.keys()) - set(allowed_attributes)
if incorrect_keys:
raise exception.Invalid(
'The keys %s are not valid' % str(incorrect_keys))
association = _format_association(namespace, resource_type,
association_values)
DATA['metadef_namespace_resource_types'].append(association)
return association
@log_call
def metadef_resource_type_association_get(context, namespace_name,
resource_type_name):
namespace = metadef_namespace_get(context, namespace_name)
resource_type = metadef_resource_type_get(context, resource_type_name)
for association in DATA['metadef_namespace_resource_types']:
if (association['namespace_id'] == namespace['id'] and
association['resource_type'] == resource_type['id']):
return association
else:
msg = ("No resource type association found associated with namespace "
"%s and resource type %s" % namespace_name, resource_type_name)
LOG.debug(msg)
raise exception.MetadefResourceTypeAssociationNotFound(
resource_type_name=resource_type_name,
namespace_name=namespace_name)
@log_call
def metadef_resource_type_association_get_all_by_namespace(context,
namespace_name):
namespace = metadef_namespace_get(context, namespace_name)
namespace_resource_types = []
for resource_type in DATA['metadef_namespace_resource_types']:
if resource_type['namespace_id'] == namespace['id']:
namespace_resource_types.append(resource_type)
return namespace_resource_types
@log_call
def metadef_resource_type_association_delete(context, namespace_name,
resource_type_name):
global DATA
resource_type = metadef_resource_type_association_get(context,
namespace_name,
resource_type_name)
DATA['metadef_namespace_resource_types'].remove(resource_type)
return resource_type
def _format_association(namespace, resource_type, association_values):
association = {
'namespace_id': namespace['id'],
'resource_type': resource_type['id'],
'properties_target': None,
'prefix': None,
'created_at': timeutils.utcnow(),
'updated_at': timeutils.utcnow()
}
association.update(association_values)
return association
def _format_resource_type(values):
dt = timeutils.utcnow()
resource_type = {
'id': _get_metadef_id(),
'name': values['name'],
'protected': True,
'created_at': dt,
'updated_at': dt
}
resource_type.update(values)
return resource_type
def _format_property(values):
property = {
'id': _get_metadef_id(),
'namespace_id': None,
'name': None,
'schema': None
}
property.update(values)
return property
def _format_namespace(values):
dt = timeutils.utcnow()
namespace = {
'id': _get_metadef_id(),
'namespace': None,
'display_name': None,
'description': None,
'visibility': 'private',
'protected': False,
'owner': None,
'created_at': dt,
'updated_at': dt
}
namespace.update(values)
return namespace
def _format_object(values):
dt = timeutils.utcnow()
object = {
'id': _get_metadef_id(),
'namespace_id': None,
'name': None,
'description': None,
'schema': None,
'required': None,
'created_at': dt,
'updated_at': dt
}
object.update(values)
return object
def _is_namespace_visible(context, namespace):
"""Return true if namespace is visible in this context"""
if context.is_admin:
return True
if namespace.get('visibility', '') == 'public':
return True
if namespace['owner'] is None:
return True
if context.owner is not None:
if context.owner == namespace['owner']:
return True
return False
def _check_namespace_visibility(context, namespace, namespace_name):
if not _is_namespace_visible(context, namespace):
msg = ("Forbidding request, metadata definition namespace=%s"
" not visible." % namespace_name)
LOG.debug(msg)
emsg = _("Forbidding request, metadata definition namespace=%s"
" not visible.") % namespace_name
raise exception.MetadefForbidden(emsg)
def _get_metadef_id():
global INDEX
INDEX += 1
return INDEX

View File

@ -39,6 +39,13 @@ from glance import i18n
import glance.openstack.common.log as os_logging
from glance.openstack.common import timeutils
from glance.db.sqlalchemy.metadef_api import namespace as metadef_namespace_api
from glance.db.sqlalchemy.metadef_api import object as metadef_object_api
from glance.db.sqlalchemy.metadef_api import property as metadef_property_api
from glance.db.sqlalchemy.metadef_api\
import resource_type as metadef_resource_type_api
from glance.db.sqlalchemy.metadef_api\
import resource_type_association as metadef_association_api
BASE = models.BASE
sa_logger = None
@ -1426,3 +1433,199 @@ def _task_format(task_ref, task_info_ref=None):
task_dict.update(task_info_dict)
return task_dict
def metadef_namespace_get_all(context, marker=None, limit=None, sort_key=None,
sort_dir=None, filters=None, session=None):
"""List all available namespaces."""
session = session or get_session()
namespaces = metadef_namespace_api.get_all(
context, session, marker, limit, sort_key, sort_dir, filters)
return namespaces
def metadef_namespace_get(context, namespace_name, session=None):
"""Get a namespace or raise if it does not exist or is not visible."""
session = session or get_session()
return metadef_namespace_api.get(
context, namespace_name, session)
def metadef_namespace_create(context, values, session=None):
"""Create a namespace or raise if it already exists."""
session = session or get_session()
return metadef_namespace_api.create(context, values, session)
def metadef_namespace_update(context, namespace_id, namespace_dict,
session=None):
"""Update a namespace or raise if it does not exist or not visible"""
session = session or get_session()
return metadef_namespace_api.\
update(context, namespace_id, namespace_dict, session)
def metadef_namespace_delete(context, namespace_name, session=None):
"""Delete the namespace and all foreign references"""
session = session or get_session()
return metadef_namespace_api.delete_cascade(
context, namespace_name, session)
def metadef_object_get_all(context, namespace_name, session=None):
"""Get a metadata-schema object or raise if it does not exist."""
session = session or get_session()
return metadef_object_api.get_all(
context, namespace_name, session)
def metadef_object_get(context, namespace_name, object_name, session=None):
"""Get a metadata-schema object or raise if it does not exist."""
session = session or get_session()
return metadef_object_api.get(
context, namespace_name, object_name, session)
def metadef_object_create(context, namespace_name, object_dict,
session=None):
"""Create a metadata-schema object or raise if it already exists."""
session = session or get_session()
return metadef_object_api.create(
context, namespace_name, object_dict, session)
def metadef_object_update(context, namespace_name, object_id, object_dict,
session=None):
"""Update an object or raise if it does not exist or not visible."""
session = session or get_session()
return metadef_object_api.update(
context, namespace_name, object_id, object_dict, session)
def metadef_object_delete(context, namespace_name, object_name,
session=None):
"""Delete an object or raise if namespace or object doesn't exist."""
session = session or get_session()
return metadef_object_api.delete(
context, namespace_name, object_name, session)
def metadef_object_delete_namespace_content(
context, namespace_name, session=None):
"""Delete an object or raise if namespace or object doesn't exist."""
session = session or get_session()
return metadef_object_api.delete_by_namespace_name(
context, namespace_name, session)
def metadef_object_count(context, namespace_name, session=None):
"""Get count of properties for a namespace, raise if ns doesn't exist."""
session = session or get_session()
return metadef_object_api.count(context, namespace_name, session)
def metadef_property_get_all(context, namespace_name, session=None):
"""Get a metadef property or raise if it does not exist."""
session = session or get_session()
return metadef_property_api.get_all(context, namespace_name, session)
def metadef_property_get(context, namespace_name,
property_name, session=None):
"""Get a metadef property or raise if it does not exist."""
session = session or get_session()
return metadef_property_api.get(
context, namespace_name, property_name, session)
def metadef_property_create(context, namespace_name, property_dict,
session=None):
"""Create a metadef property or raise if it already exists."""
session = session or get_session()
return metadef_property_api.create(
context, namespace_name, property_dict, session)
def metadef_property_update(context, namespace_name, property_id,
property_dict, session=None):
"""Update an object or raise if it does not exist or not visible."""
session = session or get_session()
return metadef_property_api.update(
context, namespace_name, property_id, property_dict, session)
def metadef_property_delete(context, namespace_name, property_name,
session=None):
"""Delete a property or raise if it or namespace doesn't exist."""
session = session or get_session()
return metadef_property_api.delete(
context, namespace_name, property_name, session)
def metadef_property_delete_namespace_content(
context, namespace_name, session=None):
"""Delete a property or raise if it or namespace doesn't exist."""
session = session or get_session()
return metadef_property_api.delete_by_namespace_name(
context, namespace_name, session)
def metadef_property_count(context, namespace_name, session=None):
"""Get count of properties for a namespace, raise if ns doesn't exist."""
session = session or get_session()
return metadef_property_api.count(context, namespace_name, session)
def metadef_resource_type_create(context, values, session=None):
"""Create a resource_type"""
session = session or get_session()
return metadef_resource_type_api.create(
context, values, session)
def metadef_resource_type_get(context, resource_type_name, session=None):
"""Get a resource_type"""
session = session or get_session()
return metadef_resource_type_api.get(
context, resource_type_name, session)
def metadef_resource_type_get_all(context, session=None):
"""list all resource_types"""
session = session or get_session()
return metadef_resource_type_api.get_all(context, session)
def metadef_resource_type_delete(context, resource_type_name, session=None):
"""Get a resource_type"""
session = session or get_session()
return metadef_resource_type_api.delete(
context, resource_type_name, session)
def metadef_resource_type_association_get(
context, namespace_name, resource_type_name, session=None):
session = session or get_session()
return metadef_association_api.get(
context, namespace_name, resource_type_name, session)
def metadef_resource_type_association_create(
context, namespace_name, values, session=None):
session = session or get_session()
return metadef_association_api.create(
context, namespace_name, values, session)
def metadef_resource_type_association_delete(
context, namespace_name, resource_type_name, session=None):
session = session or get_session()
return metadef_association_api.delete(
context, namespace_name, resource_type_name, session)
def metadef_resource_type_association_get_all_by_namespace(
context, namespace_name, session=None):
session = session or get_session()
return metadef_association_api.\
get_all_by_namespace(context, namespace_name, session)

View File

@ -0,0 +1,307 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo.db import exception as db_exc
from oslo.db.sqlalchemy.utils import paginate_query
import sqlalchemy.exc as sa_exc
from sqlalchemy import or_
import sqlalchemy.orm as sa_orm
from glance.common import exception as exc
import glance.db.sqlalchemy.metadef_api as metadef_api
from glance.db.sqlalchemy import models_metadef as models
from glance import i18n
import glance.openstack.common.log as os_logging
LOG = os_logging.getLogger(__name__)
_LW = i18n._LW
def _is_namespace_visible(context, namespace, status=None):
"""Return True if the namespace is visible in this context."""
# Is admin == visible
if context.is_admin:
return True
# No owner == visible
if namespace['owner'] is None:
return True
# Is public == visible
if 'visibility' in namespace:
if namespace['visibility'] == 'public':
return True
# context.owner has a value and is the namespace owner == visible
if context.owner is not None:
if context.owner == namespace['owner']:
return True
# Private
return False
def _select_namespaces_query(context, session):
"""Build the query to get all namespaces based on the context"""
LOG.debug("context.is_admin=%(is_admin)s; context.owner=%(owner)s" %
{'is_admin': context.is_admin, 'owner': context.owner})
# If admin, return everything.
query_ns = session.query(models.MetadefNamespace)
if context.is_admin:
return query_ns
else:
# If regular user, return only public namespaces.
# However, if context.owner has a value, return both
# public and private namespaces of the context.owner.
if context.owner is not None:
query = (
query_ns.filter(
or_(models.MetadefNamespace.owner == context.owner,
models.MetadefNamespace.visibility == 'public')))
else:
query = query_ns.filter(
models.MetadefNamespace.visibility == 'public')
return query
def _get(context, namespace_id, session):
"""Get a namespace by id, raise if not found"""
try:
query = session.query(models.MetadefNamespace)\
.filter_by(id=namespace_id)
namespace_rec = query.one()
except sa_orm.exc.NoResultFound:
LOG.warn(_LW("Metadata definition namespace not found for id=%s",
namespace_id))
raise exc.MetadefRecordNotFound(record_type='namespace',
id=namespace_id)
# Make sure they are allowed to view it.
if not _is_namespace_visible(context, namespace_rec.as_dict()):
msg = ("Forbidding request, metadata definition namespace=%s"
" is not visible.") % namespace_rec.namespace
LOG.debug(msg)
emsg = _("Forbidding request, metadata definition namespace=%s"
" is not visible.") % namespace_rec.namespace
raise exc.MetadefForbidden(emsg)
return namespace_rec
def _get_by_name(context, name, session):
"""Get a namespace by name, raise if not found"""
try:
query = session.query(models.MetadefNamespace)\
.filter_by(namespace=name)
namespace_rec = query.one()
except sa_orm.exc.NoResultFound:
msg = "Metadata definition namespace=%s was not found." % name
LOG.debug(msg)
raise exc.MetadefNamespaceNotFound(namespace_name=name)
# Make sure they are allowed to view it.
if not _is_namespace_visible(context, namespace_rec.as_dict()):
msg = ("Forbidding request, metadata definition namespace=%s"
" not visible." % name)
LOG.debug(msg)
emsg = _("Forbidding request, metadata definition namespace=%s"
" not visible.") % name
raise exc.MetadefForbidden(emsg)
return namespace_rec
def _get_all(context, session, filters=None, marker=None,
limit=None, sort_key='created_at', sort_dir='desc'):
"""Get all namespaces that match zero or more filters.
:param filters: dict of filter keys and values.
:param marker: namespace id after which to start page
:param limit: maximum number of namespaces to return
:param sort_key: namespace attribute by which results should be sorted
:param sort_dir: direction in which results should be sorted (asc, desc)
"""
filters = filters or {}
query = _select_namespaces_query(context, session)
# if visibility filter, apply it to the context based query
visibility = filters.pop('visibility', None)
if visibility is not None:
query = query.filter(models.MetadefNamespace.visibility == visibility)
# if id_list filter, apply it to the context based query
id_list = filters.pop('id_list', None)
if id_list is not None:
query = query.filter(models.MetadefNamespace.id.in_(id_list))
marker_namespace = None
if marker is not None:
marker_namespace = _get(context, marker, session)
sort_keys = ['created_at', 'id']
sort_keys.insert(0, sort_key) if sort_key not in sort_keys else sort_keys
query = paginate_query(query=query,
model=models.MetadefNamespace,
limit=limit,
sort_keys=sort_keys,
marker=marker_namespace, sort_dir=sort_dir)
return query.all()
def _get_all_by_resource_types(context, session, filters, marker=None,
limit=None, sort_key=None, sort_dir=None):
"""get all visible namespaces for the specified resource_types"""
resource_types = filters['resource_types']
resource_type_list = resource_types.split(',')
db_recs = (
session.query(models.MetadefResourceType)
.join(models.MetadefResourceType.associations)
.filter(models.MetadefResourceType.name.in_(resource_type_list))
.values(models.MetadefResourceType.name,
models.MetadefNamespaceResourceType.namespace_id)
)
namespace_id_list = []
for name, namespace_id in db_recs:
namespace_id_list.append(namespace_id)
if len(namespace_id_list) is 0:
return []
filters2 = filters
filters2.update({'id_list': namespace_id_list})
return _get_all(context, session, filters2,
marker, limit, sort_key, sort_dir)
def get_all(context, session, marker=None, limit=None,
sort_key=None, sort_dir=None, filters=None):
"""List all visible namespaces"""
namespaces = []
filters = filters or {}
if 'resource_types' in filters:
namespaces = _get_all_by_resource_types(
context, session, filters, marker, limit, sort_key, sort_dir)
else:
namespaces = _get_all(
context, session, filters, marker, limit, sort_key, sort_dir)
return map(lambda ns: ns.as_dict(), namespaces)
def get(context, name, session):
"""Get a namespace by name, raise if not found"""
namespace_rec = _get_by_name(context, name, session)
return namespace_rec.as_dict()
def create(context, values, session):
"""Create a namespace, raise if namespace already exists."""
namespace_name = values['namespace']
namespace = models.MetadefNamespace()
metadef_api.utils.drop_protected_attrs(models.MetadefNamespace, values)
namespace.update(values.copy())
try:
namespace.save(session=session)
except db_exc.DBDuplicateEntry:
msg = ("Can not create the metadata definition namespace."
" Namespace=%s already exists.") % namespace_name
LOG.debug(msg)
raise exc.MetadefDuplicateNamespace(
namespace_name=namespace_name)
return namespace.as_dict()
def update(context, namespace_id, values, session):
"""Update a namespace, raise if not found/visible or duplicate result"""
namespace_rec = _get(context, namespace_id, session)
metadef_api.utils.drop_protected_attrs(models.MetadefNamespace, values)
try:
namespace_rec.update(values.copy())
namespace_rec.save(session=session)
except db_exc.DBDuplicateEntry:
msg = ("Invalid update. It would result in a duplicate"
" metadata definition namespace with the same name of %s"
% values['namespace'])
LOG.debug(msg)
emsg = (_("Invalid update. It would result in a duplicate"
" metadata definition namespace with the same name of %s")
% values['namespace'])
raise exc.MetadefDuplicateNamespace(emsg)
return namespace_rec.as_dict()
def delete(context, name, session):
"""Raise if not found, has references or not visible"""
namespace_rec = _get_by_name(context, name, session)
try:
session.delete(namespace_rec)
session.flush()
except db_exc.DBError as e:
if isinstance(e.inner_exception, sa_exc.IntegrityError):
msg = ("Metadata definition namespace=%s not deleted."
" Other records still refer to it." % name)
LOG.debug(msg)
raise exc.MetadefIntegrityError(
record_type='namespace', record_name=name)
else:
raise e
return namespace_rec.as_dict()
def delete_cascade(context, name, session):
"""Raise if not found, has references or not visible"""
namespace_rec = _get_by_name(context, name, session)
with session.begin():
try:
metadef_api.object.delete_namespace_content(
context, namespace_rec.id, session)
metadef_api.property.delete_namespace_content(
context, namespace_rec.id, session)
metadef_api.resource_type_association.delete_namespace_content(
context, namespace_rec.id, session)
session.delete(namespace_rec)
session.flush()
except db_exc.DBError as e:
if isinstance(e.inner_exception, sa_exc.IntegrityError):
msg = ("Metadata definition namespace=%s not deleted."
" Other records still refer to it." % name)
LOG.debug(msg)
raise exc.MetadefIntegrityError(
record_type='namespace', record_name=name)
else:
raise e
return namespace_rec.as_dict()

View File

@ -0,0 +1,156 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo.db import exception as db_exc
from sqlalchemy import func
import sqlalchemy.orm as sa_orm
from glance.common import exception as exc
from glance.db.sqlalchemy.metadef_api import namespace as namespace_api
import glance.db.sqlalchemy.metadef_api.utils as metadef_utils
from glance.db.sqlalchemy import models_metadef as models
from glance import i18n
import glance.openstack.common.log as os_logging
LOG = os_logging.getLogger(__name__)
_LW = i18n._LW
def _get(context, object_id, session):
try:
query = session.query(models.MetadefObject)\
.filter_by(id=object_id)
metadef_object = query.one()
except sa_orm.exc.NoResultFound:
LOG.warn(_LW("Metadata definition object not found for id %s",
object_id))
raise exc.MetadefRecordNotFound(record_type='object', id=object_id)
return metadef_object
def _get_by_name(context, namespace_name, name, session):
namespace = namespace_api.get(context, namespace_name, session)
try:
query = session.query(models.MetadefObject)\
.filter_by(name=name, namespace_id=namespace['id'])
metadef_object = query.one()
except sa_orm.exc.NoResultFound:
msg = ("The metadata definition object with name=%(name)s"
" was not found in namespace=%(namespace_name)s."
% {'name': name, 'namespace_name': namespace_name})
LOG.debug(msg)
raise exc.MetadefObjectNotFound(object_name=name,
namespace_name=namespace_name)
return metadef_object
def get_all(context, namespace_name, session):
namespace = namespace_api.get(context, namespace_name, session)
query = session.query(models.MetadefObject)\
.filter_by(namespace_id=namespace['id'])
md_objects = query.all()
md_objects_list = []
for obj in md_objects:
md_objects_list.append(obj.as_dict())
return md_objects_list
def create(context, namespace_name, values, session):
namespace = namespace_api.get(context, namespace_name, session)
values.update({'namespace_id': namespace['id']})
md_object = models.MetadefObject()
metadef_utils.drop_protected_attrs(models.MetadefObject, values)
md_object.update(values.copy())
try:
md_object.save(session=session)
except db_exc.DBDuplicateEntry:
msg = ("A metadata definition object with name=%(name)s"
" in namespace=%(namespace_name)s already exists."
% {'name': md_object.name,
'namespace_name': namespace_name})
LOG.debug(msg)
raise exc.MetadefDuplicateObject(
object_name=md_object.name, namespace_name=namespace_name)
return md_object.as_dict()
def get(context, namespace_name, name, session):
md_object = _get_by_name(context, namespace_name, name, session)
return md_object.as_dict()
def update(context, namespace_name, object_id, values, session):
"""Update an object, raise if ns not found/visible or duplicate result"""
namespace_api.get(context, namespace_name, session)
md_object = _get(context, object_id, session)
metadef_utils.drop_protected_attrs(models.MetadefObject, values)
# values['updated_at'] = timeutils.utcnow() - done by TS mixin
try:
md_object.update(values.copy())
md_object.save(session=session)
except db_exc.DBDuplicateEntry:
msg = ("Invalid update. It would result in a duplicate"
" metadata definition object with same name=%(name)s"
" in namespace=%(namespace_name)s."
% {'name': md_object.name, 'namespace_name': namespace_name})
LOG.debug(msg)
emsg = (_("Invalid update. It would result in a duplicate"
" metadata definition object with the same name=%(name)s"
" in namespace=%(namespace_name)s.")
% {'name': md_object.name, 'namespace_name': namespace_name})
raise exc.MetadefDuplicateObject(emsg)
return md_object.as_dict()
def delete(context, namespace_name, object_name, session):
namespace_api.get(context, namespace_name, session)
md_object = _get_by_name(context, namespace_name, object_name, session)
session.delete(md_object)
session.flush()
return md_object.as_dict()
def delete_namespace_content(context, namespace_id, session):
"""Use this def only if the ns for the id has been verified as visible"""
count = 0
query = session.query(models.MetadefObject)\
.filter_by(namespace_id=namespace_id)
count = query.delete(synchronize_session='fetch')
return count
def delete_by_namespace_name(context, namespace_name, session):
namespace = namespace_api.get(context, namespace_name, session)
return delete_namespace_content(context, namespace['id'], session)
def count(context, namespace_name, session):
"""Get the count of objects for a namespace, raise if ns not found"""
namespace = namespace_api.get(context, namespace_name, session)
query = session.query(func.count(models.MetadefObject.id))\
.filter_by(namespace_id=namespace['id'])
return query.scalar()

View File

@ -0,0 +1,169 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo.db import exception as db_exc
from sqlalchemy import func
import sqlalchemy.orm as sa_orm
from glance.common import exception as exc
from glance.db.sqlalchemy.metadef_api import namespace as namespace_api
from glance.db.sqlalchemy.metadef_api import utils as metadef_utils
from glance.db.sqlalchemy import models_metadef as models
from glance import i18n
import glance.openstack.common.log as os_logging
LOG = os_logging.getLogger(__name__)
_LW = i18n._LW
def _get(context, property_id, session):
try:
query = session.query(models.MetadefProperty)\
.filter_by(id=property_id)
property_rec = query.one()
except sa_orm.exc.NoResultFound:
LOG.warn(_LW("Metadata definition property not found for id=%s",
property_id))
raise exc.MetadefRecordNotFound(
record_type='property', id=property_id)
return property_rec
def _get_by_name(context, namespace_name, name, session):
"""get a property; raise if ns not found/visible or property not found"""
namespace = namespace_api.get(context, namespace_name, session)
try:
query = session.query(models.MetadefProperty)\
.filter_by(name=name, namespace_id=namespace['id'])
property_rec = query.one()
except sa_orm.exc.NoResultFound:
msg = ("The metadata definition property with name=%(name)s"
" was not found in namespace=%(namespace_name)s."
% {'name': name, 'namespace_name': namespace_name})
LOG.debug(msg)
raise exc.MetadefPropertyNotFound(property_name=name,
namespace_name=namespace_name)
return property_rec
def get(context, namespace_name, name, session):
"""get a property; raise if ns not found/visible or property not found"""
property_rec = _get_by_name(context, namespace_name, name, session)
return property_rec.as_dict()
def get_all(context, namespace_name, session):
namespace = namespace_api.get(context, namespace_name, session)
query = session.query(models.MetadefProperty)\
.filter_by(namespace_id=namespace['id'])
properties = query.all()
properties_list = []
for prop in properties:
properties_list.append(prop.as_dict())
return properties_list
def create(context, namespace_name, values, session):
namespace = namespace_api.get(context, namespace_name, session)
values.update({'namespace_id': namespace['id']})
property_rec = models.MetadefProperty()
metadef_utils.drop_protected_attrs(models.MetadefProperty, values)
property_rec.update(values.copy())
try:
property_rec.save(session=session)
except db_exc.DBDuplicateEntry:
msg = ("Can not create metadata definition property. A property"
" with name=%(name)s already exists in"
" namespace=%(namespace_name)s."
% {'name': property_rec.name,
'namespace_name': namespace_name})
LOG.debug(msg)
raise exc.MetadefDuplicateProperty(
property_name=property_rec.name,
namespace_name=namespace_name)
return property_rec.as_dict()
def update(context, namespace_name, property_id, values, session):
"""Update a property, raise if ns not found/visible or duplicate result"""
namespace_api.get(context, namespace_name, session)
property_rec = _get(context, property_id, session)
metadef_utils.drop_protected_attrs(models.MetadefProperty, values)
# values['updated_at'] = timeutils.utcnow() - done by TS mixin
try:
property_rec.update(values.copy())
property_rec.save(session=session)
except db_exc.DBDuplicateEntry:
msg = ("Invalid update. It would result in a duplicate"
" metadata definition property with the same name=%(name)s"
" in namespace=%(namespace_name)s."
% {'name': property_rec.name,
'namespace_name': namespace_name})
LOG.debug(msg)
emsg = (_("Invalid update. It would result in a duplicate"
" metadata definition property with the same name=%(name)s"
" in namespace=%(namespace_name)s.")
% {'name': property_rec.name,
'namespace_name': namespace_name})
raise exc.MetadefDuplicateProperty(emsg)
return property_rec.as_dict()
def delete(context, namespace_name, property_name, session):
property_rec = _get_by_name(
context, namespace_name, property_name, session)
if property_rec:
session.delete(property_rec)
session.flush()
return property_rec.as_dict()
def delete_namespace_content(context, namespace_id, session):
"""Use this def only if the ns for the id has been verified as visible"""
count = 0
query = session.query(models.MetadefProperty)\
.filter_by(namespace_id=namespace_id)
count = query.delete(synchronize_session='fetch')
return count
def delete_by_namespace_name(context, namespace_name, session):
namespace = namespace_api.get(context, namespace_name, session)
return delete_namespace_content(context, namespace['id'], session)
def count(context, namespace_name, session):
"""Get the count of properties for a namespace, raise if ns not found"""
namespace = namespace_api.get(context, namespace_name, session)
query = session.query(func.count(models.MetadefProperty.id))\
.filter_by(namespace_id=namespace['id'])
return query.scalar()

View File

@ -0,0 +1,111 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo.db import exception as db_exc
import sqlalchemy.exc as sa_exc
import sqlalchemy.orm as sa_orm
from glance.common import exception as exc
import glance.db.sqlalchemy.metadef_api.utils as metadef_utils
from glance.db.sqlalchemy import models_metadef as models
import glance.openstack.common.log as os_logging
LOG = os_logging.getLogger(__name__)
def get(context, name, session):
"""Get a resource type, raise if not found"""
try:
query = session.query(models.MetadefResourceType)\
.filter_by(name=name)
resource_type = query.one()
except sa_orm.exc.NoResultFound:
msg = "No metadata definition resource-type found with name %s" % name
LOG.debug(msg)
raise exc.MetadefResourceTypeNotFound(resource_type_name=name)
return resource_type.as_dict()
def get_all(context, session):
"""Get a list of all resource types"""
query = session.query(models.MetadefResourceType)
resource_types = query.all()
resource_types_list = []
for rt in resource_types:
resource_types_list.append(rt.as_dict())
return resource_types_list
def create(context, values, session):
"""Create a resource_type, raise if it already exists."""
resource_type = models.MetadefResourceType()
metadef_utils.drop_protected_attrs(models.MetadefResourceType, values)
resource_type.update(values.copy())
try:
resource_type.save(session=session)
except db_exc.DBDuplicateEntry:
msg = ("Can not create the metadata definition resource-type."
" A resource-type with name=%s already exists."
% resource_type.name)
LOG.debug(msg)
raise exc.MetadefDuplicateResourceType(
resource_type_name=resource_type.name)
return resource_type.as_dict()
def update(context, values, session):
"""Update a resource type, raise if not found"""
name = values['name']
metadef_utils.drop_protected_attrs(models.MetadefResourceType, values)
db_rec = get(context, name, session)
db_rec.update(values.copy())
db_rec.save(session=session)
return db_rec.as_dict()
def delete(context, name, session):
"""Delete a resource type or raise if not found or is protected"""
db_rec = get(context, name, session)
if db_rec.protected is True:
msg = ("Delete forbidden. Metadata definition resource-type %s is a"
" seeded-system type and can not be deleted.") % name
LOG.debug(msg)
raise exc.ProtectedMetadefResourceTypeSystemDelete(
resource_type_name=name)
try:
session.delete(db_rec)
session.flush()
except db_exc.DBError as e:
if isinstance(e.inner_exception, sa_exc.IntegrityError):
msg = ("Could not delete Metadata definition resource-type %s"
". It still has content") % name
LOG.debug(msg)
raise exc.MetadefIntegrityError(
record_type='resource-type', record_name=name)
else:
raise e
return db_rec.as_dict()

View File

@ -0,0 +1,217 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo.db import exception as db_exc
import sqlalchemy.orm as sa_orm
from glance.common import exception as exc
from glance.db.sqlalchemy.metadef_api\
import namespace as namespace_api
from glance.db.sqlalchemy.metadef_api\
import resource_type as resource_type_api
from glance.db.sqlalchemy.metadef_api\
import utils as metadef_utils
from glance.db.sqlalchemy import models_metadef as models
import glance.openstack.common.log as os_logging
LOG = os_logging.getLogger(__name__)
def _to_db_dict(namespace_id, resource_type_id, model_dict):
"""transform a model dict to a metadef_namespace_resource_type dict"""
db_dict = {'namespace_id': namespace_id,
'resource_type_id': resource_type_id,
'properties_target': model_dict['properties_target'],
'prefix': model_dict['prefix']}
return db_dict
def _to_model_dict(resource_type_name, ns_res_type_dict):
"""transform a metadef_namespace_resource_type dict to a model dict"""
model_dict = {'name': resource_type_name,
'properties_target': ns_res_type_dict['properties_target'],
'prefix': ns_res_type_dict['prefix'],
'created_at': ns_res_type_dict['created_at'],
'updated_at': ns_res_type_dict['updated_at']}
return model_dict
def _set_model_dict(resource_type_name, properties_target, prefix,
created_at, updated_at):
"""return a model dict set with the passed in key values"""
model_dict = {'name': resource_type_name,
'properties_target': properties_target,
'prefix': prefix,
'created_at': created_at,
'updated_at': updated_at}
return model_dict
def _get(context, namespace_name, resource_type_name,
namespace_id, resource_type_id, session):
"""Get a namespace resource_type association"""
# visibility check assumed done in calling routine via namespace_get
try:
query = session.query(models.MetadefNamespaceResourceType).\
filter_by(namespace_id=namespace_id,
resource_type_id=resource_type_id)
db_rec = query.one()
except sa_orm.exc.NoResultFound:
msg = ("The metadata definition resource-type association of"
" resource_type=%(resource_type_name)s to"
" namespace_name=%(namespace_name)s was not found."
% {'resource_type_name': resource_type_name,
'namespace_name': namespace_name})
LOG.debug(msg)
raise exc.MetadefResourceTypeAssociationNotFound(
resource_type_name=resource_type_name,
namespace_name=namespace_name)
return db_rec
def _create_association(
context, namespace_name, resource_type_name, values, session):
"""Create an association, raise if it already exists."""
namespace_resource_type_rec = models.MetadefNamespaceResourceType()
metadef_utils.drop_protected_attrs(
models.MetadefNamespaceResourceType, values)
# values['updated_at'] = timeutils.utcnow() # TS mixin should do this
namespace_resource_type_rec.update(values.copy())
try:
namespace_resource_type_rec.save(session=session)
except db_exc.DBDuplicateEntry:
msg = ("The metadata definition resource-type association of"
" resource_type=%(resource_type_name)s to"
" namespace=%(namespace_name)s, already exists."
% {'resource_type_name': resource_type_name,
'namespace_name': namespace_name})
LOG.debug(msg)
raise exc.MetadefDuplicateResourceTypeAssociation(
resource_type_name=resource_type_name,
namespace_name=namespace_name)
return namespace_resource_type_rec.as_dict()
def _delete(context, namespace_name, resource_type_name,
namespace_id, resource_type_id, session):
"""Delete a resource type association or raise if not found."""
db_rec = _get(context, namespace_name, resource_type_name,
namespace_id, resource_type_id, session)
session.delete(db_rec)
session.flush()
return db_rec.as_dict()
def get(context, namespace_name, resource_type_name, session):
"""Get a resource_type associations; raise if not found"""
namespace = namespace_api.get(
context, namespace_name, session)
resource_type = resource_type_api.get(
context, resource_type_name, session)
found = _get(context, namespace_name, resource_type_name,
namespace['id'], resource_type['id'], session)
return _to_model_dict(resource_type_name, found)
def get_all_by_namespace(context, namespace_name, session):
"""List resource_type associations by namespace, raise if not found"""
# namespace get raises an exception if not visible
namespace = namespace_api.get(
context, namespace_name, session)
db_recs = (
session.query(models.MetadefResourceType)
.join(models.MetadefResourceType.associations)
.filter_by(namespace_id=namespace['id'])
.values(models.MetadefResourceType.name,
models.MetadefNamespaceResourceType.properties_target,
models.MetadefNamespaceResourceType.prefix,
models.MetadefNamespaceResourceType.created_at,
models.MetadefNamespaceResourceType.updated_at))
model_dict_list = []
for name, properties_target, prefix, created_at, updated_at in db_recs:
model_dict_list.append(
_set_model_dict
(name, properties_target, prefix, created_at, updated_at)
)
return model_dict_list
def create(context, namespace_name, values, session):
"""Create an association, raise if already exists or ns not found."""
namespace = namespace_api.get(
context, namespace_name, session)
# if the resource_type does not exist, create it
resource_type_name = values['name']
metadef_utils.drop_protected_attrs(
models.MetadefNamespaceResourceType, values)
try:
resource_type = resource_type_api.get(
context, resource_type_name, session)
except exc.NotFound:
resource_type = None
LOG.debug("Creating resource-type %s" % resource_type_name)
if resource_type is None:
resource_type_dict = {'name': resource_type_name, 'protected': 0}
resource_type = resource_type_api.create(
context, resource_type_dict, session)
# Create the association record, set the field values
ns_resource_type_dict = _to_db_dict(
namespace['id'], resource_type['id'], values)
new_rec = _create_association(context, namespace_name, resource_type_name,
ns_resource_type_dict, session)
return _to_model_dict(resource_type_name, new_rec)
def delete(context, namespace_name, resource_type_name, session):
"""Delete an association or raise if not found"""
namespace = namespace_api.get(
context, namespace_name, session)
resource_type = resource_type_api.get(
context, resource_type_name, session)
deleted = _delete(context, namespace_name, resource_type_name,
namespace['id'], resource_type['id'], session)
return _to_model_dict(resource_type_name, deleted)
def delete_namespace_content(context, namespace_id, session):
"""Use this def only if the ns for the id has been verified as visible"""
count = 0
query = session.query(models.MetadefNamespaceResourceType)\
.filter_by(namespace_id=namespace_id)
count = query.delete(synchronize_session='fetch')
return count

View File

@ -0,0 +1,23 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
def drop_protected_attrs(model_class, values):
"""
Removed protected attributes from values dictionary using the models
__protected_attributes__ field.
"""
for attr in model_class.__protected_attributes__:
if attr in values:
del values[attr]

View File

@ -0,0 +1,215 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import sqlalchemy
from sqlalchemy.schema import (
Column, ForeignKey, Index, MetaData, Table, UniqueConstraint) # noqa
from glance.db.sqlalchemy.migrate_repo.schema import (
Boolean, DateTime, Integer, String, Text, create_tables,
drop_tables) # noqa
from glance.openstack.common import timeutils
RESOURCE_TYPES = [u'OS::Glance::Image', u'OS::Cinder::Volume',
u'OS::Nova::Flavor', u'OS::Nova::Aggregate',
u'OS::Nova::Instance']
def _get_metadef_resource_types_table(meta):
return sqlalchemy.Table('metadef_resource_types', meta, autoload=True)
def _populate_resource_types(resource_types_table):
now = timeutils.utcnow()
for resource_type in RESOURCE_TYPES:
values = {
'name': resource_type,
'protected': True,
'created_at': now,
'updated_at': now
}
resource_types_table.insert(values=values).execute()
def define_metadef_namespaces_table(meta):
# NOTE: For DB2 if UniqueConstraint is used when creating a table
# an index will automatically be created. So, for DB2 specify the
# index name up front. If not DB2 then create the Index.
_constr_kwargs = {}
if meta.bind.name == 'ibm_db_sa':
_constr_kwargs['name'] = 'ix_namespaces_namespace'
namespaces = Table('metadef_namespaces',
meta,
Column('id', Integer(), primary_key=True,
nullable=False),
Column('namespace', String(80), nullable=False),
Column('display_name', String(80)),
Column('description', Text()),
Column('visibility', String(32)),
Column('protected', Boolean()),
Column('owner', String(255), nullable=False),
Column('created_at', DateTime(), nullable=False),
Column('updated_at', DateTime()),
UniqueConstraint('namespace', **_constr_kwargs),
mysql_engine='InnoDB',
extend_existing=True)
if meta.bind.name != 'ibm_db_sa':
Index('ix_namespaces_namespace', namespaces.c.namespace)
return namespaces
def define_metadef_objects_table(meta):
_constr_kwargs = {}
if meta.bind.name == 'ibm_db_sa':
_constr_kwargs['name'] = 'ix_objects_namespace_id_name'
objects = Table('metadef_objects',
meta,
Column('id', Integer(), primary_key=True, nullable=False),
Column('namespace_id', Integer(),
ForeignKey('metadef_namespaces.id'),
nullable=False),
Column('name', String(80), nullable=False),
Column('description', Text()),
Column('required', Text()),
Column('schema', Text()),
Column('created_at', DateTime(), nullable=False),
Column('updated_at', DateTime()),
UniqueConstraint('namespace_id', 'name',
**_constr_kwargs),
mysql_engine='InnoDB',
extend_existing=True)
if meta.bind.name != 'ibm_db_sa':
Index('ix_objects_namespace_id_name',
objects.c.namespace_id,
objects.c.name)
return objects
def define_metadef_properties_table(meta):
_constr_kwargs = {}
if meta.bind.name == 'ibm_db_sa':
_constr_kwargs['name'] = 'ix_metadef_properties_namespace_id_name'
metadef_properties = Table(
'metadef_properties',
meta,
Column('id', Integer(), primary_key=True, nullable=False),
Column('namespace_id', Integer(), ForeignKey('metadef_namespaces.id'),
nullable=False),
Column('name', String(80), nullable=False),
Column('schema', Text()),
Column('created_at', DateTime(), nullable=False),
Column('updated_at', DateTime()),
UniqueConstraint('namespace_id', 'name', **_constr_kwargs),
mysql_engine='InnoDB',
extend_existing=True)
if meta.bind.name != 'ibm_db_sa':
Index('ix_metadef_properties_namespace_id_name',
metadef_properties.c.namespace_id,
metadef_properties.c.name)
return metadef_properties
def define_metadef_resource_types_table(meta):
_constr_kwargs = {}
if meta.bind.name == 'ibm_db_sa':
_constr_kwargs['name'] = 'ix_metadef_resource_types_name'
metadef_res_types = Table(
'metadef_resource_types',
meta,
Column('id', Integer(), primary_key=True, nullable=False),
Column('name', String(80), nullable=False),
Column('protected', Boolean(), nullable=False, default=False),
Column('created_at', DateTime(), nullable=False),
Column('updated_at', DateTime()),
UniqueConstraint('name', **_constr_kwargs),
mysql_engine='InnoDB',
extend_existing=True)
if meta.bind.name != 'ibm_db_sa':
Index('ix_metadef_resource_types_name',
metadef_res_types.c.name)
return metadef_res_types
def define_metadef_namespace_resource_types_table(meta):
_constr_kwargs = {}
if meta.bind.name == 'ibm_db_sa':
_constr_kwargs['name'] = 'ix_metadef_ns_res_types_res_type_id_ns_id'
metadef_associations = Table(
'metadef_namespace_resource_types',
meta,
Column('resource_type_id', Integer(),
ForeignKey('metadef_resource_types.id'),
primary_key=True, nullable=False),
Column('namespace_id', Integer(),
ForeignKey('metadef_namespaces.id'),
primary_key=True, nullable=False),
Column('properties_target', String(80)),
Column('prefix', String(80)),
Column('created_at', DateTime(), nullable=False),
Column('updated_at', DateTime()),
UniqueConstraint('resource_type_id', 'namespace_id',
**_constr_kwargs),
mysql_engine='InnoDB',
extend_existing=True)
if meta.bind.name != 'ibm_db_sa':
Index('ix_metadef_ns_res_types_res_type_id_ns_id',
metadef_associations.c.resource_type_id,
metadef_associations.c.namespace_id)
return metadef_associations
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
tables = [define_metadef_namespaces_table(meta),
define_metadef_objects_table(meta),
define_metadef_properties_table(meta),
define_metadef_resource_types_table(meta),
define_metadef_namespace_resource_types_table(meta)]
create_tables(tables)
resource_types_table = _get_metadef_resource_types_table(meta)
_populate_resource_types(resource_types_table)
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
tables = [define_metadef_objects_table(meta),
define_metadef_properties_table(meta),
define_metadef_namespace_resource_types_table(meta),
define_metadef_resource_types_table(meta),
define_metadef_namespaces_table(meta)]
drop_tables(tables)

View File

@ -0,0 +1,152 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
SQLAlchemy models for glance metadata schema
"""
from oslo.db.sqlalchemy import models
from sqlalchemy import Boolean
from sqlalchemy import Column
from sqlalchemy import DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import ForeignKey
from sqlalchemy import Index
from sqlalchemy import Integer
from sqlalchemy.orm import relationship
from sqlalchemy import String
from sqlalchemy import Text
from glance.openstack.common import timeutils
class DictionaryBase(models.ModelBase):
metadata = None
def as_dict(self):
d = {}
for c in self.__table__.columns:
d[c.name] = self[c.name]
return d
BASE_DICT = declarative_base(cls=DictionaryBase)
class GlanceMetadefBase(models.TimestampMixin):
"""Base class for Glance Metadef Models."""
__table_args__ = {'mysql_engine': 'InnoDB'}
__table_initialized__ = False
__protected_attributes__ = set(["created_at", "updated_at"])
created_at = Column(DateTime, default=lambda: timeutils.utcnow(),
nullable=False)
# TODO(wko): Column `updated_at` have no default value in
# openstack common code. We should decide, is this value
# required and make changes in oslo (if required) or
# in glance (if not).
updated_at = Column(DateTime, default=lambda: timeutils.utcnow(),
nullable=False, onupdate=lambda: timeutils.utcnow())
class MetadefNamespace(BASE_DICT, GlanceMetadefBase):
"""Represents a metadata-schema namespace in the datastore."""
__tablename__ = 'metadef_namespaces'
__table_args__ = (Index('ix_metadef_namespaces_namespace', 'namespace'),
Index('ix_metadef_namespaces_owner', 'owner'))
id = Column(Integer, primary_key=True, nullable=False)
namespace = Column(String(80))
display_name = Column(String(80))
description = Column(Text())
visibility = Column(String(32))
protected = Column(Boolean)
owner = Column(String(255), nullable=False)
class MetadefObject(BASE_DICT, GlanceMetadefBase):
"""Represents a metadata-schema object in the datastore."""
__tablename__ = 'metadef_objects'
__table_args__ = (Index('ix_metadef_objects_namespace_id', 'namespace_id'),
Index('ix_metadef_objects_name', 'name'))
id = Column(Integer, primary_key=True, nullable=False)
namespace_id = Column(Integer(), ForeignKey('metadef_namespaces.id'),
nullable=False)
name = Column(String(80), nullable=False)
description = Column(Text())
required = Column(Text())
schema = Column(Text(), default={})
class MetadefProperty(BASE_DICT, GlanceMetadefBase):
"""Represents a metadata-schema namespace-property in the datastore."""
__tablename__ = 'metadef_properties'
__table_args__ = (Index('ix_metadef_properties_namespace_id',
'namespace_id'),
Index('ix_metadef_properties_name', 'name'))
id = Column(Integer, primary_key=True, nullable=False)
namespace_id = Column(Integer(), ForeignKey('metadef_namespaces.id'),
nullable=False)
name = Column(String(80), nullable=False)
schema = Column(Text(), default={})
class MetadefNamespaceResourceType(BASE_DICT, GlanceMetadefBase):
"""Represents a metadata-schema namespace-property in the datastore."""
__tablename__ = 'metadef_namespace_resource_types'
__table_args__ = (Index('ix_metadef_ns_res_types_res_type_id_ns_id',
'resource_type_id', 'namespace_id'),
Index('ix_metadef_ns_res_types_namespace_id',
'namespace_id'))
resource_type_id = Column(Integer,
ForeignKey('metadef_resource_types.id'),
primary_key=True, nullable=False)
namespace_id = Column(Integer, ForeignKey('metadef_namespaces.id'),
primary_key=True, nullable=False)
properties_target = Column(String(80))
prefix = Column(String(80))
class MetadefResourceType(BASE_DICT, GlanceMetadefBase):
"""Represents a metadata-schema resource type in the datastore."""
__tablename__ = 'metadef_resource_types'
__table_args__ = (Index('ix_metadef_resource_types_name', 'name'), )
id = Column(Integer, primary_key=True, nullable=False)
name = Column(String(80), nullable=False)
protected = Column(Boolean, nullable=False, default=False)
associations = relationship(
"MetadefNamespaceResourceType",
primaryjoin=id == MetadefNamespaceResourceType.resource_type_id)
def register_models(engine):
"""Create database tables for all models with the given engine."""
models = (MetadefNamespace, MetadefObject, MetadefProperty,
MetadefResourceType, MetadefNamespaceResourceType)
for model in models:
model.metadata.create_all(engine)
def unregister_models(engine):
"""Drop database tables for all models with the given engine."""
models = (MetadefObject, MetadefProperty, MetadefNamespaceResourceType,
MetadefNamespace, MetadefResourceType)
for model in models:
model.metadata.drop_all(engine)

View File

@ -0,0 +1,479 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
from glance import context
import glance.tests.functional.db as db_tests
from glance.common import config
from glance.common import exception
from glance.tests import utils as test_utils
def build_namespace_fixture(**kwargs):
namespace = {
'namespace': u'MyTestNamespace',
'display_name': u'test-display-name',
'description': u'test-description',
'visibility': u'public',
'protected': 0,
'owner': u'test-owner'
}
namespace.update(kwargs)
return namespace
def build_resource_type_fixture(**kwargs):
resource_type = {
'name': u'MyTestResourceType',
'protected': 0
}
resource_type.update(kwargs)
return resource_type
def build_association_fixture(**kwargs):
association = {
'name': u'MyTestResourceType',
'properties_target': 'test-properties-target',
'prefix': 'test-prefix'
}
association.update(kwargs)
return association
def build_object_fixture(**kwargs):
# Full testing of required and schema done via rest api tests
object = {
'namespace_id': 1,
'name': u'test-object-name',
'description': u'test-object-description',
'required': u'fake-required-properties-list',
'schema': u'{fake-schema}'
}
object.update(kwargs)
return object
def build_property_fixture(**kwargs):
# Full testing of required and schema done via rest api tests
property = {
'namespace_id': 1,
'name': u'test-property-name',
'schema': u'{fake-schema}'
}
property.update(kwargs)
return property
class TestMetadefDriver(test_utils.BaseTestCase):
"""Test Driver class for Metadef tests."""
def setUp(self):
"""Run before each test method to initialize test environment."""
super(TestMetadefDriver, self).setUp()
config.parse_args(args=[])
context_cls = context.RequestContext
self.adm_context = context_cls(is_admin=True,
auth_tok='user:user:admin')
self.context = context_cls(is_admin=False,
auth_tok='user:user:user')
self.db_api = db_tests.get_db(self.config)
db_tests.reset_db(self.db_api)
def _assert_saved_fields(self, expected, actual):
for k in expected.keys():
self.assertEqual(expected[k], actual[k])
class MetadefNamespaceTests(object):
def test_namespace_create(self):
fixture = build_namespace_fixture()
created = self.db_api.metadef_namespace_create(self.context, fixture)
self.assertIsNotNone(created)
self._assert_saved_fields(fixture, created)
def test_namespace_get(self):
fixture = build_namespace_fixture()
created = self.db_api.metadef_namespace_create(self.context, fixture)
self.assertIsNotNone(created)
self._assert_saved_fields(fixture, created)
found = self.db_api.metadef_namespace_get(
self.context, created['namespace'])
self.assertIsNotNone(found, "Namespace not found.")
def test_namespace_get_all_with_resource_types_filter(self):
ns_fixture = build_namespace_fixture()
ns_created = self.db_api.metadef_namespace_create(
self.context, ns_fixture)
self.assertIsNotNone(ns_created, "Could not create a namespace.")
self._assert_saved_fields(ns_fixture, ns_created)
fixture = build_association_fixture()
created = self.db_api.metadef_resource_type_association_create(
self.context, ns_created['namespace'], fixture)
self.assertIsNotNone(created, "Could not create an association.")
rt_filters = {'resource_types': fixture['name']}
found = self.db_api.\
metadef_namespace_get_all(self.context, filters=rt_filters,
sort_key='created_at')
self.assertEqual(len(found), 1)
for item in found:
self._assert_saved_fields(ns_fixture, item)
def test_namespace_update(self):
delta = {'owner': u'New Owner'}
fixture = build_namespace_fixture()
created = self.db_api.metadef_namespace_create(self.context, fixture)
self.assertIsNotNone(created['namespace'])
self.assertEqual(created['namespace'], fixture['namespace'])
delta_dict = copy.deepcopy(created)
delta_dict.update(delta.copy())
updated = self.db_api.metadef_namespace_update(
self.context, created['id'], delta_dict)
self.assertEqual(delta['owner'], updated['owner'])
def test_namespace_delete(self):
fixture = build_namespace_fixture()
created = self.db_api.metadef_namespace_create(self.context, fixture)
self.assertIsNotNone(created, "Could not create a Namespace.")
self.db_api.metadef_namespace_delete(
self.context, created['namespace'])
self.assertRaises(exception.NotFound,
self.db_api.metadef_namespace_get,
self.context, created['namespace'])
def test_namespace_delete_with_content(self):
fixture_ns = build_namespace_fixture()
created_ns = self.db_api.metadef_namespace_create(
self.context, fixture_ns)
self._assert_saved_fields(fixture_ns, created_ns)
# Create object content for the namespace
fixture_obj = build_object_fixture()
created_obj = self.db_api.metadef_object_create(
self.context, created_ns['namespace'], fixture_obj)
self.assertIsNotNone(created_obj)
# Create property content for the namespace
fixture_prop = build_property_fixture(namespace_id=created_ns['id'])
created_prop = self.db_api.metadef_property_create(
self.context, created_ns['namespace'], fixture_prop)
self.assertIsNotNone(created_prop)
# Create associations
fixture_assn = build_association_fixture()
created_assn = self.db_api.metadef_resource_type_association_create(
self.context, created_ns['namespace'], fixture_assn)
self.assertIsNotNone(created_assn)
deleted_ns = self.db_api.metadef_namespace_delete(
self.context, created_ns['namespace'])
self.assertRaises(exception.NotFound,
self.db_api.metadef_namespace_get,
self.context, deleted_ns['namespace'])
class MetadefPropertyTests(object):
def test_property_create(self):
fixture = build_namespace_fixture()
created_ns = self.db_api.metadef_namespace_create(
self.context, fixture)
self.assertIsNotNone(created_ns)
self._assert_saved_fields(fixture, created_ns)
fixture_prop = build_property_fixture(namespace_id=created_ns['id'])
created_prop = self.db_api.metadef_property_create(
self.context, created_ns['namespace'], fixture_prop)
self._assert_saved_fields(fixture_prop, created_prop)
def test_property_get(self):
fixture_ns = build_namespace_fixture()
created_ns = self.db_api.metadef_namespace_create(
self.context, fixture_ns)
self.assertIsNotNone(created_ns)
self._assert_saved_fields(fixture_ns, created_ns)
fixture_prop = build_property_fixture(namespace_id=created_ns['id'])
created_prop = self.db_api.metadef_property_create(
self.context, created_ns['namespace'], fixture_prop)
found_prop = self.db_api.metadef_property_get(
self.context, created_ns['namespace'], created_prop['name'])
self._assert_saved_fields(fixture_prop, found_prop)
def test_property_get_all(self):
ns_fixture = build_namespace_fixture()
ns_created = self.db_api.metadef_namespace_create(
self.context, ns_fixture)
self.assertIsNotNone(ns_created, "Could not create a namespace.")
self._assert_saved_fields(ns_fixture, ns_created)
fixture1 = build_property_fixture(namespace_id=ns_created['id'])
created_p1 = self.db_api.metadef_property_create(
self.context, ns_created['namespace'], fixture1)
self.assertIsNotNone(created_p1, "Could not create a property.")
fixture2 = build_property_fixture(namespace_id=ns_created['id'],
name='test-prop-2')
created_p2 = self.db_api.metadef_property_create(
self.context, ns_created['namespace'], fixture2)
self.assertIsNotNone(created_p2, "Could not create a property.")
found = self.db_api.\
metadef_property_get_all(self.context, ns_created['namespace'])
self.assertEqual(len(found), 2)
def test_property_update(self):
delta = {'name': u'New-name', 'schema': u'new-schema'}
fixture_ns = build_namespace_fixture()
created_ns = self.db_api.metadef_namespace_create(
self.context, fixture_ns)
self.assertIsNotNone(created_ns['namespace'])
prop_fixture = build_property_fixture(namespace_id=created_ns['id'])
created_prop = self.db_api.metadef_property_create(
self.context, created_ns['namespace'], prop_fixture)
self.assertIsNotNone(created_prop, "Could not create a property.")
delta_dict = copy.deepcopy(created_prop)
delta_dict.update(delta.copy())
updated = self.db_api.metadef_property_update(
self.context, created_ns['namespace'],
created_prop['id'], delta_dict)
self.assertEqual(delta['name'], updated['name'])
self.assertEqual(delta['schema'], updated['schema'])
def test_property_delete(self):
fixture_ns = build_namespace_fixture()
created_ns = self.db_api.metadef_namespace_create(
self.context, fixture_ns)
self.assertIsNotNone(created_ns['namespace'])
prop_fixture = build_property_fixture(namespace_id=created_ns['id'])
created_prop = self.db_api.metadef_property_create(
self.context, created_ns['namespace'], prop_fixture)
self.assertIsNotNone(created_prop, "Could not create a property.")
self.db_api.metadef_property_delete(
self.context, created_ns['namespace'], created_prop['name'])
self.assertRaises(exception.NotFound,
self.db_api.metadef_property_get,
self.context, created_ns['namespace'],
created_prop['name'])
def test_property_delete_namespace_content(self):
fixture_ns = build_namespace_fixture()
created_ns = self.db_api.metadef_namespace_create(
self.context, fixture_ns)
self.assertIsNotNone(created_ns['namespace'])
prop_fixture = build_property_fixture(namespace_id=created_ns['id'])
created_prop = self.db_api.metadef_property_create(
self.context, created_ns['namespace'], prop_fixture)
self.assertIsNotNone(created_prop, "Could not create a property.")
self.db_api.metadef_property_delete_namespace_content(
self.context, created_ns['namespace'])
self.assertRaises(exception.NotFound,
self.db_api.metadef_property_get,
self.context, created_ns['namespace'],
created_prop['name'])
class MetadefObjectTests(object):
def test_object_create(self):
fixture = build_namespace_fixture()
created_ns = self.db_api.metadef_namespace_create(self.context,
fixture)
self.assertIsNotNone(created_ns)
self._assert_saved_fields(fixture, created_ns)
fixture_object = build_object_fixture(namespace_id=created_ns['id'])
created_object = self.db_api.metadef_object_create(
self.context, created_ns['namespace'], fixture_object)
self._assert_saved_fields(fixture_object, created_object)
def test_object_get(self):
fixture_ns = build_namespace_fixture()
created_ns = self.db_api.metadef_namespace_create(self.context,
fixture_ns)
self.assertIsNotNone(created_ns)
self._assert_saved_fields(fixture_ns, created_ns)
fixture_object = build_object_fixture(namespace_id=created_ns['id'])
created_object = self.db_api.metadef_object_create(
self.context, created_ns['namespace'], fixture_object)
found_object = self.db_api.metadef_object_get(
self.context, created_ns['namespace'], created_object['name'])
self._assert_saved_fields(fixture_object, found_object)
def test_object_get_all(self):
ns_fixture = build_namespace_fixture()
ns_created = self.db_api.metadef_namespace_create(self.context,
ns_fixture)
self.assertIsNotNone(ns_created, "Could not create a namespace.")
self._assert_saved_fields(ns_fixture, ns_created)
fixture1 = build_object_fixture(namespace_id=ns_created['id'])
created_o1 = self.db_api.metadef_object_create(
self.context, ns_created['namespace'], fixture1)
self.assertIsNotNone(created_o1, "Could not create an object.")
fixture2 = build_object_fixture(namespace_id=ns_created['id'],
name='test-object-2')
created_o2 = self.db_api.metadef_object_create(
self.context, ns_created['namespace'], fixture2)
self.assertIsNotNone(created_o2, "Could not create an object.")
found = self.db_api.\
metadef_object_get_all(self.context, ns_created['namespace'])
self.assertEqual(len(found), 2)
def test_object_update(self):
delta = {'name': u'New-name', 'schema': u'new-schema',
'required': u'new-required'}
fixture_ns = build_namespace_fixture()
created_ns = self.db_api.metadef_namespace_create(self.context,
fixture_ns)
self.assertIsNotNone(created_ns['namespace'])
object_fixture = build_object_fixture(namespace_id=created_ns['id'])
created_object = self.db_api.metadef_object_create(
self.context, created_ns['namespace'], object_fixture)
self.assertIsNotNone(created_object, "Could not create an object.")
delta_dict = {}
delta_dict.update(delta.copy())
updated = self.db_api.metadef_object_update(
self.context, created_ns['namespace'],
created_object['id'], delta_dict)
self.assertEqual(delta['name'], updated['name'])
self.assertEqual(delta['schema'], updated['schema'])
def test_object_delete(self):
fixture_ns = build_namespace_fixture()
created_ns = self.db_api.metadef_namespace_create(
self.context, fixture_ns)
self.assertIsNotNone(created_ns['namespace'])
object_fixture = build_object_fixture(namespace_id=created_ns['id'])
created_object = self.db_api.metadef_object_create(
self.context, created_ns['namespace'], object_fixture)
self.assertIsNotNone(created_object, "Could not create an object.")
self.db_api.metadef_object_delete(
self.context, created_ns['namespace'], created_object['name'])
self.assertRaises(exception.NotFound,
self.db_api.metadef_object_get,
self.context, created_ns['namespace'],
created_object['name'])
class MetadefResourceTypeTests(object):
def test_resource_type_get_all(self):
resource_types_orig = self.db_api.metadef_resource_type_get_all(
self.context)
fixture = build_resource_type_fixture()
self.db_api.metadef_resource_type_create(self.context, fixture)
resource_types = self.db_api.metadef_resource_type_get_all(
self.context)
test_len = len(resource_types_orig) + 1
self.assertEqual(len(resource_types), test_len)
class MetadefResourceTypeAssociationTests(object):
def test_association_create(self):
ns_fixture = build_namespace_fixture()
ns_created = self.db_api.metadef_namespace_create(
self.context, ns_fixture)
self.assertIsNotNone(ns_created)
self._assert_saved_fields(ns_fixture, ns_created)
assn_fixture = build_association_fixture()
assn_created = self.db_api.metadef_resource_type_association_create(
self.context, ns_created['namespace'], assn_fixture)
self.assertIsNotNone(assn_created)
self._assert_saved_fields(assn_fixture, assn_created)
def test_association_delete(self):
ns_fixture = build_namespace_fixture()
ns_created = self.db_api.metadef_namespace_create(
self.context, ns_fixture)
self.assertIsNotNone(ns_created, "Could not create a namespace.")
self._assert_saved_fields(ns_fixture, ns_created)
fixture = build_association_fixture()
created = self.db_api.metadef_resource_type_association_create(
self.context, ns_created['namespace'], fixture)
self.assertIsNotNone(created, "Could not create an association.")
created_resource = self.db_api.metadef_resource_type_get(
self.context, fixture['name'])
self.assertIsNotNone(created_resource, "resource_type not created")
self.db_api.metadef_resource_type_association_delete(
self.context, ns_created['namespace'], created_resource['name'])
self.assertRaises(exception.NotFound,
self.db_api.metadef_resource_type_association_get,
self.context, ns_created['namespace'],
created_resource['name'])
def test_association_get_all_by_namespace(self):
ns_fixture = build_namespace_fixture()
ns_created = self.db_api.metadef_namespace_create(
self.context, ns_fixture)
self.assertIsNotNone(ns_created, "Could not create a namespace.")
self._assert_saved_fields(ns_fixture, ns_created)
fixture = build_association_fixture()
created = self.db_api.metadef_resource_type_association_create(
self.context, ns_created['namespace'], fixture)
self.assertIsNotNone(created, "Could not create an association.")
found = self.db_api.\
metadef_resource_type_association_get_all_by_namespace(
self.context, ns_created['namespace'])
self.assertEqual(len(found), 1)
for item in found:
self._assert_saved_fields(fixture, item)
class MetadefDriverTests(MetadefNamespaceTests,
MetadefResourceTypeTests,
MetadefResourceTypeAssociationTests,
MetadefPropertyTests,
MetadefObjectTests):
# collection class
pass

View File

@ -20,6 +20,7 @@ import glance.db
from glance.tests import functional
import glance.tests.functional.db as db_tests
from glance.tests.functional.db import base
from glance.tests.functional.db import base_metadef
CONF = cfg.CONF
@ -85,3 +86,17 @@ class TestRegistryQuota(base.DriverQuotaTests, FunctionalInitWrapper):
def tearDown(self):
self.registry_server.stop()
super(TestRegistryQuota, self).tearDown()
class TestRegistryMetadefDriver(base_metadef.TestMetadefDriver,
base_metadef.MetadefDriverTests,
FunctionalInitWrapper):
def setUp(self):
db_tests.load(get_db, reset_db)
super(TestRegistryMetadefDriver, self).setUp()
self.addCleanup(db_tests.reset)
def tearDown(self):
self.registry_server.stop()
super(TestRegistryMetadefDriver, self).tearDown()

View File

@ -20,8 +20,10 @@ from oslo.db import options
from glance.common import exception
import glance.db.sqlalchemy.api
from glance.db.sqlalchemy import models as db_models
from glance.db.sqlalchemy import models_metadef as metadef_models
import glance.tests.functional.db as db_tests
from glance.tests.functional.db import base
from glance.tests.functional.db import base_metadef
CONF = cfg.CONF
@ -38,6 +40,11 @@ def reset_db(db_api):
db_models.register_models(db_api.get_engine())
def reset_db_metadef(db_api):
metadef_models.unregister_models(db_api.get_engine())
metadef_models.register_models(db_api.get_engine())
class TestSqlAlchemyDriver(base.TestDriver, base.DriverTests):
def setUp(self):
@ -136,3 +143,12 @@ class TestSqlAlchemyQuota(base.DriverQuotaTests):
db_tests.load(get_db, reset_db)
super(TestSqlAlchemyQuota, self).setUp()
self.addCleanup(db_tests.reset)
class TestMetadefSqlAlchemyDriver(base_metadef.TestMetadefDriver,
base_metadef.MetadefDriverTests):
def setUp(self):
db_tests.load(get_db, reset_db_metadef)
super(TestMetadefSqlAlchemyDriver, self).setUp()
self.addCleanup(db_tests.reset)

View File

@ -1338,3 +1338,125 @@ class TestMigrations(test_utils.BaseTestCase):
def _post_downgrade_034(self, engine):
images = get_table(engine, 'images')
self.assertNotIn('virtual_size', images.c)
def _pre_upgrade_035(self, engine):
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
get_table, engine, 'metadef_namespaces')
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
get_table, engine, 'metadef_properties')
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
get_table, engine, 'metadef_objects')
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
get_table, engine, 'metadef_resource_types')
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
get_table, engine,
'metadef_namespace_resource_types')
def _check_035(self, engine, data):
meta = sqlalchemy.MetaData()
meta.bind = engine
# metadef_namespaces
table = sqlalchemy.Table("metadef_namespaces", meta, autoload=True)
index_namespace = ('ix_namespaces_namespace', ['namespace'])
index_data = [(idx.name, idx.columns.keys())
for idx in table.indexes]
self.assertIn(index_namespace, index_data)
expected_cols = [u'id',
u'namespace',
u'display_name',
u'description',
u'visibility',
u'protected',
u'owner',
u'created_at',
u'updated_at']
col_data = [col.name for col in table.columns]
self.assertEqual(expected_cols, col_data)
# metadef_objects
table = sqlalchemy.Table("metadef_objects", meta, autoload=True)
index_namespace_id_name = (
'ix_objects_namespace_id_name', ['namespace_id', 'name'])
index_data = [(idx.name, idx.columns.keys())
for idx in table.indexes]
self.assertIn(index_namespace_id_name, index_data)
expected_cols = [u'id',
u'namespace_id',
u'name',
u'description',
u'required',
u'schema',
u'created_at',
u'updated_at']
col_data = [col.name for col in table.columns]
self.assertEqual(expected_cols, col_data)
# metadef_properties
table = sqlalchemy.Table("metadef_properties", meta, autoload=True)
index_namespace_id_name = (
'ix_metadef_properties_namespace_id_name',
['namespace_id', 'name'])
index_data = [(idx.name, idx.columns.keys())
for idx in table.indexes]
self.assertIn(index_namespace_id_name, index_data)
expected_cols = [u'id',
u'namespace_id',
u'name',
u'schema',
u'created_at',
u'updated_at']
col_data = [col.name for col in table.columns]
self.assertEqual(expected_cols, col_data)
# metadef_resource_types
table = sqlalchemy.Table(
"metadef_resource_types", meta, autoload=True)
index_resource_types_name = (
'ix_metadef_resource_types_name', ['name'])
index_data = [(idx.name, idx.columns.keys())
for idx in table.indexes]
self.assertIn(index_resource_types_name, index_data)
expected_cols = [u'id',
u'name',
u'protected',
u'created_at',
u'updated_at']
col_data = [col.name for col in table.columns]
self.assertEqual(expected_cols, col_data)
# metadef_namespace_resource_types
table = sqlalchemy.Table(
"metadef_namespace_resource_types", meta, autoload=True)
index_ns_res_types_res_type_id_ns_id = (
'ix_metadef_ns_res_types_res_type_id_ns_id',
['resource_type_id', 'namespace_id'])
index_data = [(idx.name, idx.columns.keys())
for idx in table.indexes]
self.assertIn(index_ns_res_types_res_type_id_ns_id, index_data)
expected_cols = [u'resource_type_id',
u'namespace_id',
u'properties_target',
u'prefix',
u'created_at',
u'updated_at']
col_data = [col.name for col in table.columns]
self.assertEqual(expected_cols, col_data)
def _post_downgrade_035(self, engine):
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
get_table, engine, 'metadef_namespaces')
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
get_table, engine, 'metadef_properties')
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
get_table, engine, 'metadef_objects')
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
get_table, engine, 'metadef_resource_types')
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
get_table, engine,
'metadef_namespace_resource_types')