Adding Metadef Tag support
Adding rest api and db support for CRUD operations on the new metadef_tags table. Implements: https://blueprints.launchpad.net/glance/+spec/metadefs-tags DocImpact Change-Id: Icfa40555280ce69766381b0abe7ef399b806f6a0
This commit is contained in:
parent
81b3c04eca
commit
c7fa300cc5
@ -47,6 +47,12 @@
|
||||
"get_metadef_property":"",
|
||||
"get_metadef_properties":"",
|
||||
"modify_metadef_property":"",
|
||||
"add_metadef_property":""
|
||||
"add_metadef_property":"",
|
||||
|
||||
"get_metadef_tag":"",
|
||||
"get_metadef_tags":"",
|
||||
"modify_metadef_tag":"",
|
||||
"add_metadef_tag":"",
|
||||
"add_metadef_tags":""
|
||||
|
||||
}
|
||||
|
@ -815,3 +815,88 @@ class MetadefPropertyRepoProxy(glance.domain.proxy.MetadefPropertyRepo):
|
||||
*args, **kwargs)
|
||||
return [proxy_namespace_property(self.context, namespace_property) for
|
||||
namespace_property in namespace_properties]
|
||||
|
||||
|
||||
# Metadef Tag classes
|
||||
def is_tag_mutable(context, tag):
|
||||
"""Return True if the tag is mutable in this context."""
|
||||
if context.is_admin:
|
||||
return True
|
||||
|
||||
if context.owner is None:
|
||||
return False
|
||||
|
||||
return tag.namespace.owner == context.owner
|
||||
|
||||
|
||||
def proxy_tag(context, tag):
|
||||
if is_tag_mutable(context, tag):
|
||||
return tag
|
||||
else:
|
||||
return ImmutableMetadefTagProxy(tag)
|
||||
|
||||
|
||||
class ImmutableMetadefTagProxy(object):
|
||||
|
||||
def __init__(self, base):
|
||||
self.base = base
|
||||
self.resource_name = 'tag'
|
||||
|
||||
tag_id = _immutable_attr('base', 'tag_id')
|
||||
name = _immutable_attr('base', 'name')
|
||||
created_at = _immutable_attr('base', 'created_at')
|
||||
updated_at = _immutable_attr('base', 'updated_at')
|
||||
|
||||
def delete(self):
|
||||
message = _("You are not permitted to delete this tag.")
|
||||
raise exception.Forbidden(message)
|
||||
|
||||
def save(self):
|
||||
message = _("You are not permitted to update this tag.")
|
||||
raise exception.Forbidden(message)
|
||||
|
||||
|
||||
class MetadefTagProxy(glance.domain.proxy.MetadefTag):
|
||||
|
||||
def __init__(self, meta_tag):
|
||||
super(MetadefTagProxy, self).__init__(meta_tag)
|
||||
|
||||
|
||||
class MetadefTagFactoryProxy(glance.domain.proxy.MetadefTagFactory):
|
||||
|
||||
def __init__(self, meta_tag_factory, context):
|
||||
self.meta_tag_factory = meta_tag_factory
|
||||
self.context = context
|
||||
super(MetadefTagFactoryProxy, self).__init__(
|
||||
meta_tag_factory,
|
||||
meta_tag_proxy_class=MetadefTagProxy)
|
||||
|
||||
def new_tag(self, **kwargs):
|
||||
owner = kwargs.pop('owner', self.context.owner)
|
||||
if not self.context.is_admin:
|
||||
if owner is None:
|
||||
message = _("Owner must be specified to create a tag.")
|
||||
raise exception.Forbidden(message)
|
||||
elif owner != self.context.owner:
|
||||
message = _("You are not permitted to create a tag"
|
||||
" in the namespace owned by '%s'")
|
||||
raise exception.Forbidden(message % (owner))
|
||||
|
||||
return super(MetadefTagFactoryProxy, self).new_tag(**kwargs)
|
||||
|
||||
|
||||
class MetadefTagRepoProxy(glance.domain.proxy.MetadefTagRepo):
|
||||
|
||||
def __init__(self, tag_repo, context):
|
||||
self.tag_repo = tag_repo
|
||||
self.context = context
|
||||
super(MetadefTagRepoProxy, self).__init__(tag_repo)
|
||||
|
||||
def get(self, namespace, tag_name):
|
||||
meta_tag = self.tag_repo.get(namespace, tag_name)
|
||||
return proxy_tag(self.context, meta_tag)
|
||||
|
||||
def list(self, *args, **kwargs):
|
||||
tags = self.tag_repo.list(*args, **kwargs)
|
||||
return [proxy_tag(self.context, meta_tag) for
|
||||
meta_tag in tags]
|
||||
|
@ -594,3 +594,58 @@ class MetadefPropertyFactoryProxy(glance.domain.proxy.MetadefPropertyFactory):
|
||||
namespace_property_factory,
|
||||
property_proxy_class=MetadefPropertyProxy,
|
||||
property_proxy_kwargs=proxy_kwargs)
|
||||
|
||||
|
||||
# Metadef Tag classes
|
||||
class MetadefTagProxy(glance.domain.proxy.MetadefTag):
|
||||
|
||||
def __init__(self, meta_tag, context, policy):
|
||||
self.context = context
|
||||
self.policy = policy
|
||||
super(MetadefTagProxy, self).__init__(meta_tag)
|
||||
|
||||
|
||||
class MetadefTagRepoProxy(glance.domain.proxy.MetadefTagRepo):
|
||||
|
||||
def __init__(self, tag_repo, context, tag_policy):
|
||||
self.context = context
|
||||
self.policy = tag_policy
|
||||
self.tag_repo = tag_repo
|
||||
proxy_kwargs = {'context': self.context, 'policy': self.policy}
|
||||
super(MetadefTagRepoProxy,
|
||||
self).__init__(tag_repo,
|
||||
tag_proxy_class=MetadefTagProxy,
|
||||
tag_proxy_kwargs=proxy_kwargs)
|
||||
|
||||
def get(self, namespace, tag_name):
|
||||
self.policy.enforce(self.context, 'get_metadef_tag', {})
|
||||
return super(MetadefTagRepoProxy, self).get(namespace, tag_name)
|
||||
|
||||
def list(self, *args, **kwargs):
|
||||
self.policy.enforce(self.context, 'get_metadef_tags', {})
|
||||
return super(MetadefTagRepoProxy, self).list(*args, **kwargs)
|
||||
|
||||
def save(self, meta_tag):
|
||||
self.policy.enforce(self.context, 'modify_metadef_tag', {})
|
||||
return super(MetadefTagRepoProxy, self).save(meta_tag)
|
||||
|
||||
def add(self, meta_tag):
|
||||
self.policy.enforce(self.context, 'add_metadef_tag', {})
|
||||
return super(MetadefTagRepoProxy, self).add(meta_tag)
|
||||
|
||||
def add_tags(self, meta_tags):
|
||||
self.policy.enforce(self.context, 'add_metadef_tags', {})
|
||||
return super(MetadefTagRepoProxy, self).add_tags(meta_tags)
|
||||
|
||||
|
||||
class MetadefTagFactoryProxy(glance.domain.proxy.MetadefTagFactory):
|
||||
|
||||
def __init__(self, meta_tag_factory, context, policy):
|
||||
self.meta_tag_factory = meta_tag_factory
|
||||
self.context = context
|
||||
self.policy = policy
|
||||
proxy_kwargs = {'context': self.context, 'policy': self.policy}
|
||||
super(MetadefTagFactoryProxy, self).__init__(
|
||||
meta_tag_factory,
|
||||
meta_tag_proxy_class=MetadefTagProxy,
|
||||
meta_tag_proxy_kwargs=proxy_kwargs)
|
||||
|
@ -26,6 +26,7 @@ from glance.api.v2.model.metadef_namespace import Namespaces
|
||||
from glance.api.v2.model.metadef_object import MetadefObject
|
||||
from glance.api.v2.model.metadef_property_type import PropertyType
|
||||
from glance.api.v2.model.metadef_resource_type import ResourceTypeAssociation
|
||||
from glance.api.v2.model.metadef_tag import MetadefTag
|
||||
from glance.common import exception
|
||||
from glance.common import utils
|
||||
from glance.common import wsgi
|
||||
@ -54,6 +55,7 @@ class NamespaceController(object):
|
||||
policy_enforcer=self.policy)
|
||||
self.ns_schema_link = '/v2/schemas/metadefs/namespace'
|
||||
self.obj_schema_link = '/v2/schemas/metadefs/object'
|
||||
self.tag_schema_link = '/v2/schemas/metadefs/tag'
|
||||
|
||||
def index(self, req, marker=None, limit=None, sort_key='created_at',
|
||||
sort_dir='desc', filters=None):
|
||||
@ -112,10 +114,11 @@ class NamespaceController(object):
|
||||
namespace_created = True
|
||||
|
||||
# Create Resource Types
|
||||
rs_factory = (
|
||||
self.gateway.get_metadef_resource_type_factory(req.context))
|
||||
rs_repo = self.gateway.get_metadef_resource_type_repo(req.context)
|
||||
if namespace.resource_type_associations:
|
||||
rs_factory = (self.gateway.get_metadef_resource_type_factory(
|
||||
req.context))
|
||||
rs_repo = self.gateway.get_metadef_resource_type_repo(
|
||||
req.context)
|
||||
for resource_type in namespace.resource_type_associations:
|
||||
new_resource = rs_factory.new_resource_type(
|
||||
namespace=namespace.namespace,
|
||||
@ -123,22 +126,34 @@ class NamespaceController(object):
|
||||
rs_repo.add(new_resource)
|
||||
|
||||
# Create Objects
|
||||
object_factory = self.gateway.get_metadef_object_factory(
|
||||
req.context)
|
||||
object_repo = self.gateway.get_metadef_object_repo(req.context)
|
||||
|
||||
if namespace.objects:
|
||||
object_factory = self.gateway.get_metadef_object_factory(
|
||||
req.context)
|
||||
object_repo = self.gateway.get_metadef_object_repo(
|
||||
req.context)
|
||||
for metadata_object in namespace.objects:
|
||||
new_meta_object = object_factory.new_object(
|
||||
namespace=namespace.namespace,
|
||||
**metadata_object.to_dict())
|
||||
object_repo.add(new_meta_object)
|
||||
|
||||
# Create Tags
|
||||
if namespace.tags:
|
||||
tag_factory = self.gateway.get_metadef_tag_factory(
|
||||
req.context)
|
||||
tag_repo = self.gateway.get_metadef_tag_repo(req.context)
|
||||
for metadata_tag in namespace.tags:
|
||||
new_meta_tag = tag_factory.new_tag(
|
||||
namespace=namespace.namespace,
|
||||
**metadata_tag.to_dict())
|
||||
tag_repo.add(new_meta_tag)
|
||||
|
||||
# Create Namespace Properties
|
||||
prop_factory = (
|
||||
self.gateway.get_metadef_property_factory(req.context))
|
||||
prop_repo = self.gateway.get_metadef_property_repo(req.context)
|
||||
if namespace.properties:
|
||||
prop_factory = (self.gateway.get_metadef_property_factory(
|
||||
req.context))
|
||||
prop_repo = self.gateway.get_metadef_property_repo(
|
||||
req.context)
|
||||
for (name, value) in namespace.properties.items():
|
||||
new_property_type = (
|
||||
prop_factory.new_namespace_property(
|
||||
@ -165,6 +180,7 @@ class NamespaceController(object):
|
||||
new_namespace.objects = namespace.objects
|
||||
new_namespace.resource_type_associations = (
|
||||
namespace.resource_type_associations)
|
||||
new_namespace.tags = namespace.tags
|
||||
return Namespace.to_wsme_model(new_namespace,
|
||||
get_namespace_href(new_namespace),
|
||||
self.ns_schema_link)
|
||||
@ -232,6 +248,14 @@ class NamespaceController(object):
|
||||
namespace_detail = self._prefix_property_name(
|
||||
namespace_detail, filters['resource_type'])
|
||||
|
||||
# Get tags
|
||||
tag_repo = self.gateway.get_metadef_tag_repo(req.context)
|
||||
db_metatag_list = tag_repo.list(filters=ns_filters)
|
||||
tag_list = [MetadefTag(**{'name': db_metatag.name})
|
||||
for db_metatag in db_metatag_list]
|
||||
if tag_list:
|
||||
namespace_detail.tags = tag_list
|
||||
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
@ -299,6 +323,20 @@ class NamespaceController(object):
|
||||
LOG.error(utils.exception_to_str(e))
|
||||
raise webob.exc.HTTPInternalServerError()
|
||||
|
||||
def delete_tags(self, req, namespace):
|
||||
ns_repo = self.gateway.get_metadef_namespace_repo(req.context)
|
||||
try:
|
||||
namespace_obj = ns_repo.get(namespace)
|
||||
namespace_obj.delete()
|
||||
ns_repo.remove_tags(namespace_obj)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except Exception as e:
|
||||
LOG.error(utils.exception_to_str(e))
|
||||
raise webob.exc.HTTPInternalServerError()
|
||||
|
||||
def delete_properties(self, req, namespace):
|
||||
ns_repo = self.gateway.get_metadef_namespace_repo(req.context)
|
||||
try:
|
||||
@ -700,7 +738,18 @@ def _get_base_properties():
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -733,6 +782,12 @@ def get_object_href(namespace_name, metadef_object):
|
||||
return base_href
|
||||
|
||||
|
||||
def get_tag_href(namespace_name, metadef_tag):
|
||||
base_href = ('/v2/metadefs/namespaces/%s/tags/%s' %
|
||||
(namespace_name, metadef_tag.name))
|
||||
return base_href
|
||||
|
||||
|
||||
def create_resource():
|
||||
"""Namespaces resource factory method"""
|
||||
schema = get_schema()
|
||||
|
391
glance/api/v2/metadef_tags.py
Normal file
391
glance/api/v2/metadef_tags.py
Normal file
@ -0,0 +1,391 @@
|
||||
# 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.config import cfg
|
||||
from oslo.serialization import jsonutils
|
||||
import six
|
||||
import webob.exc
|
||||
from wsme.rest import json
|
||||
|
||||
from glance.api import policy
|
||||
from glance.api.v2.model.metadef_tag import MetadefTag
|
||||
from glance.api.v2.model.metadef_tag import MetadefTags
|
||||
from glance.common import exception
|
||||
from glance.common import utils
|
||||
from glance.common import wsgi
|
||||
from glance.common import wsme_utils
|
||||
import glance.db
|
||||
from glance import i18n
|
||||
import glance.notifier
|
||||
import glance.openstack.common.log as logging
|
||||
import glance.schema
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
_ = i18n._
|
||||
_LE = i18n._LE
|
||||
_LI = i18n._LI
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TagsController(object):
|
||||
def __init__(self, db_api=None, policy_enforcer=None):
|
||||
self.db_api = db_api or glance.db.get_api()
|
||||
self.policy = policy_enforcer or policy.Enforcer()
|
||||
self.gateway = glance.gateway.Gateway(db_api=self.db_api,
|
||||
policy_enforcer=self.policy)
|
||||
self.tag_schema_link = '/v2/schemas/metadefs/tag'
|
||||
|
||||
def create(self, req, metadata_tag, namespace):
|
||||
tag_factory = self.gateway.get_metadef_tag_factory(req.context)
|
||||
tag_repo = self.gateway.get_metadef_tag_repo(req.context)
|
||||
try:
|
||||
new_meta_tag = tag_factory.new_tag(
|
||||
namespace=namespace,
|
||||
**metadata_tag.to_dict())
|
||||
tag_repo.add(new_meta_tag)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Duplicate as e:
|
||||
raise webob.exc.HTTPConflict(explanation=e.msg)
|
||||
except Exception as e:
|
||||
LOG.error(utils.exception_to_str(e))
|
||||
raise webob.exc.HTTPInternalServerError()
|
||||
|
||||
return MetadefTag.to_wsme_model(new_meta_tag)
|
||||
|
||||
def create_tags(self, req, metadata_tags, namespace):
|
||||
tag_factory = self.gateway.get_metadef_tag_factory(req.context)
|
||||
tag_repo = self.gateway.get_metadef_tag_repo(req.context)
|
||||
try:
|
||||
tag_list = []
|
||||
for metadata_tag in metadata_tags.tags:
|
||||
tag_list.append(tag_factory.new_tag(
|
||||
namespace=namespace, **metadata_tag.to_dict()))
|
||||
tag_repo.add_tags(tag_list)
|
||||
tag_list_out = [MetadefTag(**{'name': db_metatag.name})
|
||||
for db_metatag in tag_list]
|
||||
metadef_tags = MetadefTags()
|
||||
metadef_tags.tags = tag_list_out
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Duplicate as e:
|
||||
raise webob.exc.HTTPConflict(explanation=e.msg)
|
||||
except Exception as e:
|
||||
LOG.error(utils.exception_to_str(e))
|
||||
raise webob.exc.HTTPInternalServerError()
|
||||
|
||||
return metadef_tags
|
||||
|
||||
def index(self, req, namespace, marker=None, limit=None,
|
||||
sort_key='created_at', sort_dir='desc', filters=None):
|
||||
try:
|
||||
filters = filters or dict()
|
||||
filters['namespace'] = namespace
|
||||
|
||||
tag_repo = self.gateway.get_metadef_tag_repo(req.context)
|
||||
if marker:
|
||||
metadef_tag = tag_repo.get(namespace, marker)
|
||||
marker = metadef_tag.tag_id
|
||||
|
||||
db_metatag_list = tag_repo.list(
|
||||
marker=marker, limit=limit, sort_key=sort_key,
|
||||
sort_dir=sort_dir, filters=filters)
|
||||
|
||||
tag_list = [MetadefTag(**{'name': db_metatag.name})
|
||||
for db_metatag in db_metatag_list]
|
||||
|
||||
metadef_tags = MetadefTags()
|
||||
metadef_tags.tags = tag_list
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except Exception as e:
|
||||
LOG.error(utils.exception_to_str(e))
|
||||
raise webob.exc.HTTPInternalServerError()
|
||||
|
||||
return metadef_tags
|
||||
|
||||
def show(self, req, namespace, tag_name):
|
||||
meta_tag_repo = self.gateway.get_metadef_tag_repo(req.context)
|
||||
try:
|
||||
metadef_tag = meta_tag_repo.get(namespace, tag_name)
|
||||
return MetadefTag.to_wsme_model(metadef_tag)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except Exception as e:
|
||||
LOG.error(utils.exception_to_str(e))
|
||||
raise webob.exc.HTTPInternalServerError()
|
||||
|
||||
def update(self, req, metadata_tag, namespace, tag_name):
|
||||
meta_repo = self.gateway.get_metadef_tag_repo(req.context)
|
||||
try:
|
||||
metadef_tag = meta_repo.get(namespace, tag_name)
|
||||
metadef_tag.name = wsme_utils._get_value(
|
||||
metadata_tag.name)
|
||||
updated_metadata_tag = meta_repo.save(metadef_tag)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Duplicate as e:
|
||||
raise webob.exc.HTTPConflict(explanation=e.msg)
|
||||
except Exception as e:
|
||||
LOG.error(utils.exception_to_str(e))
|
||||
raise webob.exc.HTTPInternalServerError()
|
||||
|
||||
return MetadefTag.to_wsme_model(updated_metadata_tag)
|
||||
|
||||
def delete(self, req, namespace, tag_name):
|
||||
meta_repo = self.gateway.get_metadef_tag_repo(req.context)
|
||||
try:
|
||||
metadef_tag = meta_repo.get(namespace, tag_name)
|
||||
metadef_tag.delete()
|
||||
meta_repo.remove(metadef_tag)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except Exception as e:
|
||||
LOG.error(utils.exception_to_str(e))
|
||||
raise webob.exc.HTTPInternalServerError()
|
||||
|
||||
|
||||
def _get_base_definitions():
|
||||
return None
|
||||
|
||||
|
||||
def _get_base_properties():
|
||||
return {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"description": _("Date and time of tag creation"
|
||||
" (READ-ONLY)"),
|
||||
"format": "date-time"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"description": _("Date and time of the last tag modification"
|
||||
" (READ-ONLY)"),
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def _get_base_properties_for_list():
|
||||
return {
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
'required': ['name'],
|
||||
"additionalProperties": False
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def get_schema():
|
||||
definitions = _get_base_definitions()
|
||||
properties = _get_base_properties()
|
||||
mandatory_attrs = MetadefTag.get_mandatory_attrs()
|
||||
schema = glance.schema.Schema(
|
||||
'tag',
|
||||
properties,
|
||||
required=mandatory_attrs,
|
||||
definitions=definitions,
|
||||
)
|
||||
return schema
|
||||
|
||||
|
||||
def get_schema_for_list():
|
||||
definitions = _get_base_definitions()
|
||||
properties = _get_base_properties_for_list()
|
||||
schema = glance.schema.Schema(
|
||||
'tags',
|
||||
properties,
|
||||
required=None,
|
||||
definitions=definitions,
|
||||
)
|
||||
return schema
|
||||
|
||||
|
||||
def get_collection_schema():
|
||||
tag_schema = get_schema()
|
||||
return glance.schema.CollectionSchema('tags', tag_schema)
|
||||
|
||||
|
||||
class RequestDeserializer(wsgi.JSONRequestDeserializer):
|
||||
_disallowed_properties = ['created_at', 'updated_at']
|
||||
|
||||
def __init__(self, schema=None):
|
||||
super(RequestDeserializer, self).__init__()
|
||||
self.schema = schema or get_schema()
|
||||
self.schema_for_list = get_schema_for_list()
|
||||
|
||||
def _get_request_body(self, request):
|
||||
output = super(RequestDeserializer, self).default(request)
|
||||
if 'body' not in output:
|
||||
msg = _('Body expected in request.')
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
return output['body']
|
||||
|
||||
def _validate_sort_dir(self, sort_dir):
|
||||
if sort_dir not in ['asc', 'desc']:
|
||||
msg = _('Invalid sort direction: %s') % sort_dir
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
return sort_dir
|
||||
|
||||
def _get_filters(self, filters):
|
||||
visibility = filters.get('visibility')
|
||||
if visibility:
|
||||
if visibility not in ['public', 'private', 'shared']:
|
||||
msg = _('Invalid visibility value: %s') % visibility
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
return filters
|
||||
|
||||
def _validate_limit(self, limit):
|
||||
try:
|
||||
limit = int(limit)
|
||||
except ValueError:
|
||||
msg = _("limit param must be an integer")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
if limit < 0:
|
||||
msg = _("limit param must be positive")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
return limit
|
||||
|
||||
def _create_or_update(self, request):
|
||||
body = self._get_request_body(request)
|
||||
self._check_allowed(body)
|
||||
try:
|
||||
self.schema.validate(body)
|
||||
except exception.InvalidObject as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||
metadata_tag = json.fromjson(MetadefTag, body)
|
||||
return dict(metadata_tag=metadata_tag)
|
||||
|
||||
def index(self, request):
|
||||
params = request.params.copy()
|
||||
limit = params.pop('limit', None)
|
||||
marker = params.pop('marker', None)
|
||||
sort_dir = params.pop('sort_dir', 'desc')
|
||||
|
||||
query_params = {
|
||||
'sort_key': params.pop('sort_key', 'created_at'),
|
||||
'sort_dir': self._validate_sort_dir(sort_dir),
|
||||
'filters': self._get_filters(params)
|
||||
}
|
||||
|
||||
if marker:
|
||||
query_params['marker'] = marker
|
||||
|
||||
if limit:
|
||||
query_params['limit'] = self._validate_limit(limit)
|
||||
|
||||
return query_params
|
||||
|
||||
def create(self, request):
|
||||
return self._create_or_update(request)
|
||||
|
||||
def create_tags(self, request):
|
||||
body = self._get_request_body(request)
|
||||
self._check_allowed(body)
|
||||
try:
|
||||
self.schema_for_list.validate(body)
|
||||
except exception.InvalidObject as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||
metadata_tags = json.fromjson(MetadefTags, body)
|
||||
return dict(metadata_tags=metadata_tags)
|
||||
|
||||
def update(self, request):
|
||||
return self._create_or_update(request)
|
||||
|
||||
@classmethod
|
||||
def _check_allowed(cls, image):
|
||||
for key in cls._disallowed_properties:
|
||||
if key in image:
|
||||
msg = _("Attribute '%s' is read-only.") % key
|
||||
raise webob.exc.HTTPForbidden(explanation=msg)
|
||||
|
||||
|
||||
class ResponseSerializer(wsgi.JSONResponseSerializer):
|
||||
def __init__(self, schema=None):
|
||||
super(ResponseSerializer, self).__init__()
|
||||
self.schema = schema or get_schema()
|
||||
|
||||
def create(self, response, metadata_tag):
|
||||
response.status_int = 201
|
||||
self.show(response, metadata_tag)
|
||||
|
||||
def create_tags(self, response, result):
|
||||
response.status_int = 201
|
||||
metadata_tags_json = json.tojson(MetadefTags, result)
|
||||
body = jsonutils.dumps(metadata_tags_json, ensure_ascii=False)
|
||||
response.unicode_body = six.text_type(body)
|
||||
response.content_type = 'application/json'
|
||||
|
||||
def show(self, response, metadata_tag):
|
||||
metadata_tag_json = json.tojson(MetadefTag, metadata_tag)
|
||||
body = jsonutils.dumps(metadata_tag_json, ensure_ascii=False)
|
||||
response.unicode_body = six.text_type(body)
|
||||
response.content_type = 'application/json'
|
||||
|
||||
def update(self, response, metadata_tag):
|
||||
response.status_int = 200
|
||||
self.show(response, metadata_tag)
|
||||
|
||||
def index(self, response, result):
|
||||
metadata_tags_json = json.tojson(MetadefTags, result)
|
||||
body = jsonutils.dumps(metadata_tags_json, ensure_ascii=False)
|
||||
response.unicode_body = six.text_type(body)
|
||||
response.content_type = 'application/json'
|
||||
|
||||
def delete(self, response, result):
|
||||
response.status_int = 204
|
||||
|
||||
|
||||
def get_tag_href(namespace_name, metadef_tag):
|
||||
base_href = ('/v2/metadefs/namespaces/%s/tags/%s' %
|
||||
(namespace_name, metadef_tag.name))
|
||||
return base_href
|
||||
|
||||
|
||||
def create_resource():
|
||||
"""Metadef tags resource factory method"""
|
||||
schema = get_schema()
|
||||
deserializer = RequestDeserializer(schema)
|
||||
serializer = ResponseSerializer(schema)
|
||||
controller = TagsController()
|
||||
return wsgi.Resource(controller, deserializer, serializer)
|
@ -20,6 +20,7 @@ from wsme import types
|
||||
from glance.api.v2.model.metadef_object import MetadefObject
|
||||
from glance.api.v2.model.metadef_property_type import PropertyType
|
||||
from glance.api.v2.model.metadef_resource_type import ResourceTypeAssociation
|
||||
from glance.api.v2.model.metadef_tag import MetadefTag
|
||||
from glance.common.wsme_utils import WSMEModelTransformer
|
||||
|
||||
|
||||
@ -43,6 +44,7 @@ class Namespace(types.Base, WSMEModelTransformer):
|
||||
mandatory=False)
|
||||
properties = wsme.wsattr({types.text: PropertyType}, mandatory=False)
|
||||
objects = wsme.wsattr([MetadefObject], mandatory=False)
|
||||
tags = wsme.wsattr([MetadefTag], mandatory=False)
|
||||
|
||||
# Generated fields
|
||||
self = wsme.wsattr(types.text, mandatory=False)
|
||||
|
34
glance/api/v2/model/metadef_tag.py
Normal file
34
glance/api/v2/model/metadef_tag.py
Normal file
@ -0,0 +1,34 @@
|
||||
# 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 wsme
|
||||
from wsme import types
|
||||
|
||||
from glance.common import wsme_utils
|
||||
|
||||
|
||||
class MetadefTag(types.Base, wsme_utils.WSMEModelTransformer):
|
||||
|
||||
name = wsme.wsattr(types.text, mandatory=True)
|
||||
|
||||
# Not using datetime since time format has to be
|
||||
# in oslo.utils.timeutils.isotime() format
|
||||
created_at = wsme.wsattr(types.text, mandatory=False)
|
||||
updated_at = wsme.wsattr(types.text, mandatory=False)
|
||||
|
||||
|
||||
class MetadefTags(types.Base, wsme_utils.WSMEModelTransformer):
|
||||
|
||||
tags = wsme.wsattr([MetadefTag], mandatory=False)
|
@ -21,6 +21,7 @@ from glance.api.v2 import metadef_namespaces
|
||||
from glance.api.v2 import metadef_objects
|
||||
from glance.api.v2 import metadef_properties
|
||||
from glance.api.v2 import metadef_resource_types
|
||||
from glance.api.v2 import metadef_tags
|
||||
from glance.api.v2 import schemas
|
||||
from glance.api.v2 import tasks
|
||||
from glance.common import wsgi
|
||||
@ -186,6 +187,28 @@ class API(wsgi.Router):
|
||||
conditions={'method': ['POST', 'PUT', 'DELETE',
|
||||
'PATCH', 'HEAD']})
|
||||
|
||||
mapper.connect('/schemas/metadefs/tag',
|
||||
controller=schemas_resource,
|
||||
action='metadef_tag',
|
||||
conditions={'method': ['GET']})
|
||||
mapper.connect('/schemas/metadefs/tag',
|
||||
controller=reject_method_resource,
|
||||
action='reject',
|
||||
allowed_methods='GET',
|
||||
conditions={'method': ['POST', 'PUT', 'DELETE',
|
||||
'PATCH', 'HEAD']})
|
||||
|
||||
mapper.connect('/schemas/metadefs/tags',
|
||||
controller=schemas_resource,
|
||||
action='metadef_tags',
|
||||
conditions={'method': ['GET']})
|
||||
mapper.connect('/schemas/metadefs/tags',
|
||||
controller=reject_method_resource,
|
||||
action='reject',
|
||||
allowed_methods='GET',
|
||||
conditions={'method': ['POST', 'PUT', 'DELETE',
|
||||
'PATCH', 'HEAD']})
|
||||
|
||||
# Metadef resource types
|
||||
metadef_resource_types_resource = (
|
||||
metadef_resource_types.create_resource())
|
||||
@ -348,6 +371,48 @@ class API(wsgi.Router):
|
||||
allowed_methods='GET, PUT, DELETE',
|
||||
conditions={'method': ['POST', 'PATCH', 'HEAD']})
|
||||
|
||||
# Metadef tags
|
||||
metadef_tags_resource = metadef_tags.create_resource()
|
||||
mapper.connect('/metadefs/namespaces/{namespace}/tags',
|
||||
controller=metadef_tags_resource,
|
||||
action='index',
|
||||
conditions={'method': ['GET']})
|
||||
mapper.connect('/metadefs/namespaces/{namespace}/tags',
|
||||
controller=metadef_tags_resource,
|
||||
action='create',
|
||||
conditions={'method': ['POST']})
|
||||
mapper.connect('/metadefs/namespaces/{namespace}/tags',
|
||||
controller=metadef_tags_resource,
|
||||
action='create_tags',
|
||||
conditions={'method': ['PUT']})
|
||||
mapper.connect('/metadefs/namespaces/{namespace}/tags',
|
||||
controller=metadef_namespace_resource,
|
||||
action='delete_tags',
|
||||
conditions={'method': ['DELETE']})
|
||||
mapper.connect('/metadefs/namespaces/{namespace}/tags',
|
||||
controller=reject_method_resource,
|
||||
action='reject',
|
||||
allowed_methods='GET, POST, PUT, DELETE',
|
||||
conditions={'method': ['PATCH', 'HEAD']})
|
||||
|
||||
mapper.connect('/metadefs/namespaces/{namespace}/tags/{tag_name}',
|
||||
controller=metadef_tags_resource,
|
||||
action='show',
|
||||
conditions={'method': ['GET']})
|
||||
mapper.connect('/metadefs/namespaces/{namespace}/tags/{tag_name}',
|
||||
controller=metadef_tags_resource,
|
||||
action='update',
|
||||
conditions={'method': ['PUT']})
|
||||
mapper.connect('/metadefs/namespaces/{namespace}/tags/{tag_name}',
|
||||
controller=metadef_tags_resource,
|
||||
action='delete',
|
||||
conditions={'method': ['DELETE']})
|
||||
mapper.connect('/metadefs/namespaces/{namespace}/tags/{tag_name}',
|
||||
controller=reject_method_resource,
|
||||
action='reject',
|
||||
allowed_methods='GET, PUT, DELETE',
|
||||
conditions={'method': ['POST', 'PATCH', 'HEAD']})
|
||||
|
||||
images_resource = images.create_resource(custom_image_properties)
|
||||
mapper.connect('/images',
|
||||
controller=images_resource,
|
||||
|
@ -19,6 +19,7 @@ from glance.api.v2 import metadef_namespaces
|
||||
from glance.api.v2 import metadef_objects
|
||||
from glance.api.v2 import metadef_properties
|
||||
from glance.api.v2 import metadef_resource_types
|
||||
from glance.api.v2 import metadef_tags
|
||||
from glance.api.v2 import tasks
|
||||
from glance.common import wsgi
|
||||
|
||||
@ -50,6 +51,10 @@ class Controller(object):
|
||||
self.metadef_object_collection_schema = \
|
||||
metadef_objects.get_collection_schema()
|
||||
|
||||
self.metadef_tag_schema = metadef_tags.get_schema()
|
||||
self.metadef_tag_collection_schema = (
|
||||
metadef_tags.get_collection_schema())
|
||||
|
||||
def image(self, req):
|
||||
return self.image_schema.raw()
|
||||
|
||||
@ -92,6 +97,12 @@ class Controller(object):
|
||||
def metadef_objects(self, req):
|
||||
return self.metadef_object_collection_schema.raw()
|
||||
|
||||
def metadef_tag(self, req):
|
||||
return self.metadef_tag_schema.raw()
|
||||
|
||||
def metadef_tags(self, req):
|
||||
return self.metadef_tag_collection_schema.raw()
|
||||
|
||||
|
||||
def create_resource(custom_image_properties=None):
|
||||
controller = Controller(custom_image_properties)
|
||||
|
@ -151,6 +151,11 @@ class ProtectedMetadefResourceTypeSystemDelete(Forbidden):
|
||||
" a seeded-system type and cannot be deleted.")
|
||||
|
||||
|
||||
class ProtectedMetadefTagDelete(Forbidden):
|
||||
message = _("Metadata definition tag %(tag_name)s is protected"
|
||||
" and cannot be deleted.")
|
||||
|
||||
|
||||
class Invalid(GlanceException):
|
||||
message = _("Data supplied was not valid.")
|
||||
|
||||
@ -381,6 +386,11 @@ class MetadefDuplicateResourceTypeAssociation(Duplicate):
|
||||
" already exists.")
|
||||
|
||||
|
||||
class MetadefDuplicateTag(Duplicate):
|
||||
message = _("A metadata tag with name=%(name)s"
|
||||
" already exists in namespace=%(namespace_name)s.")
|
||||
|
||||
|
||||
class MetadefForbidden(Forbidden):
|
||||
message = _("You are not authorized to complete this action.")
|
||||
|
||||
@ -418,3 +428,9 @@ class MetadefResourceTypeAssociationNotFound(NotFound):
|
||||
" resource-type=%(resource_type_name)s to"
|
||||
" namespace=%(namespace_name)s,"
|
||||
" was not found.")
|
||||
|
||||
|
||||
class MetadefTagNotFound(NotFound):
|
||||
message = _("The metadata definition tag with"
|
||||
" name=%(name)s was not found in"
|
||||
" namespace=%(namespace_name)s.")
|
||||
|
@ -472,6 +472,16 @@ class MetadefNamespaceRepo(object):
|
||||
msg = _("The specified namespace %s could not be found")
|
||||
raise exception.NotFound(msg % namespace.namespace)
|
||||
|
||||
def remove_tags(self, namespace):
|
||||
try:
|
||||
self.db_api.metadef_tag_delete_namespace_content(
|
||||
self.context,
|
||||
namespace.namespace
|
||||
)
|
||||
except (exception.NotFound, exception.Forbidden):
|
||||
msg = _("The specified namespace %s could not be found")
|
||||
raise exception.NotFound(msg % namespace.namespace)
|
||||
|
||||
def object_count(self, namespace_name):
|
||||
return self.db_api.metadef_object_count(
|
||||
self.context,
|
||||
@ -758,3 +768,92 @@ class MetadefPropertyRepo(object):
|
||||
except exception.NotFound as e:
|
||||
raise exception.NotFound(explanation=e.msg)
|
||||
return property
|
||||
|
||||
|
||||
class MetadefTagRepo(object):
|
||||
|
||||
def __init__(self, context, db_api):
|
||||
self.context = context
|
||||
self.db_api = db_api
|
||||
self.meta_namespace_repo = MetadefNamespaceRepo(context, db_api)
|
||||
|
||||
def _format_metadef_tag_from_db(self, metadata_tag,
|
||||
namespace_entity):
|
||||
return glance.domain.MetadefTag(
|
||||
namespace=namespace_entity,
|
||||
tag_id=metadata_tag['id'],
|
||||
name=metadata_tag['name'],
|
||||
created_at=metadata_tag['created_at'],
|
||||
updated_at=metadata_tag['updated_at']
|
||||
)
|
||||
|
||||
def _format_metadef_tag_to_db(self, metadata_tag):
|
||||
db_metadata_tag = {
|
||||
'name': metadata_tag.name
|
||||
}
|
||||
return db_metadata_tag
|
||||
|
||||
def add(self, metadata_tag):
|
||||
self.db_api.metadef_tag_create(
|
||||
self.context,
|
||||
metadata_tag.namespace,
|
||||
self._format_metadef_tag_to_db(metadata_tag)
|
||||
)
|
||||
|
||||
def add_tags(self, metadata_tags):
|
||||
tag_list = []
|
||||
namespace = None
|
||||
for metadata_tag in metadata_tags:
|
||||
tag_list.append(self._format_metadef_tag_to_db(metadata_tag))
|
||||
if namespace is None:
|
||||
namespace = metadata_tag.namespace
|
||||
|
||||
self.db_api.metadef_tag_create_tags(
|
||||
self.context, namespace, tag_list)
|
||||
|
||||
def get(self, namespace, name):
|
||||
try:
|
||||
namespace_entity = self.meta_namespace_repo.get(namespace)
|
||||
db_metadata_tag = self.db_api.metadef_tag_get(
|
||||
self.context,
|
||||
namespace,
|
||||
name)
|
||||
except (exception.NotFound, exception.Forbidden):
|
||||
msg = _('Could not find metadata tag %s') % name
|
||||
raise exception.NotFound(msg)
|
||||
return self._format_metadef_tag_from_db(db_metadata_tag,
|
||||
namespace_entity)
|
||||
|
||||
def list(self, marker=None, limit=None, sort_key='created_at',
|
||||
sort_dir='desc', filters=None):
|
||||
namespace = filters['namespace']
|
||||
namespace_entity = self.meta_namespace_repo.get(namespace)
|
||||
|
||||
db_metadata_tag = self.db_api.metadef_tag_get_all(
|
||||
self.context, namespace, filters, marker, limit, sort_key,
|
||||
sort_dir)
|
||||
|
||||
return [self._format_metadef_tag_from_db(metadata_tag,
|
||||
namespace_entity)
|
||||
for metadata_tag in db_metadata_tag]
|
||||
|
||||
def remove(self, metadata_tag):
|
||||
try:
|
||||
self.db_api.metadef_tag_delete(
|
||||
self.context,
|
||||
metadata_tag.namespace.namespace,
|
||||
metadata_tag.name
|
||||
)
|
||||
except (exception.NotFound, exception.Forbidden):
|
||||
msg = _("The specified metadata tag %s could not be found")
|
||||
raise exception.NotFound(msg % metadata_tag.name)
|
||||
|
||||
def save(self, metadata_tag):
|
||||
try:
|
||||
self.db_api.metadef_tag_update(
|
||||
self.context, metadata_tag.namespace.namespace,
|
||||
metadata_tag.tag_id,
|
||||
self._format_metadef_tag_to_db(metadata_tag))
|
||||
except exception.NotFound as e:
|
||||
raise exception.NotFound(explanation=e.msg)
|
||||
return metadata_tag
|
||||
|
@ -499,3 +499,58 @@ def metadef_resource_type_association_get_all_by_namespace(
|
||||
namespace_name, session=None):
|
||||
return client.metadef_resource_type_association_get_all_by_namespace(
|
||||
namespace_name=namespace_name)
|
||||
|
||||
|
||||
@_get_client
|
||||
def metadef_tag_get_all(client, namespace_name, filters=None, marker=None,
|
||||
limit=None, sort_key='created_at', sort_dir=None,
|
||||
session=None):
|
||||
return client.metadef_tag_get_all(
|
||||
namespace_name=namespace_name, filters=filters, marker=marker,
|
||||
limit=limit, sort_key=sort_key, sort_dir=sort_dir, session=session)
|
||||
|
||||
|
||||
@_get_client
|
||||
def metadef_tag_get(client, namespace_name, name, session=None):
|
||||
return client.metadef_tag_get(
|
||||
namespace_name=namespace_name, name=name)
|
||||
|
||||
|
||||
@_get_client
|
||||
def metadef_tag_create(
|
||||
client, namespace_name, tag_dict, session=None):
|
||||
return client.metadef_tag_create(
|
||||
namespace_name=namespace_name, tag_dict=tag_dict)
|
||||
|
||||
|
||||
@_get_client
|
||||
def metadef_tag_create_tags(
|
||||
client, namespace_name, tag_list, session=None):
|
||||
return client.metadef_tag_create_tags(
|
||||
namespace_name=namespace_name, tag_list=tag_list)
|
||||
|
||||
|
||||
@_get_client
|
||||
def metadef_tag_update(
|
||||
client, namespace_name, id, tag_dict, session=None):
|
||||
return client.metadef_tag_update(
|
||||
namespace_name=namespace_name, id=id, tag_dict=tag_dict)
|
||||
|
||||
|
||||
@_get_client
|
||||
def metadef_tag_delete(
|
||||
client, namespace_name, name, session=None):
|
||||
return client.metadef_tag_delete(
|
||||
namespace_name=namespace_name, name=name)
|
||||
|
||||
|
||||
@_get_client
|
||||
def metadef_tag_delete_namespace_content(
|
||||
client, namespace_name, session=None):
|
||||
return client.metadef_tag_delete_namespace_content(
|
||||
namespace_name=namespace_name)
|
||||
|
||||
|
||||
@_get_client
|
||||
def metadef_tag_count(client, namespace_name, session=None):
|
||||
return client.metadef_tag_count(namespace_name=namespace_name)
|
||||
|
@ -39,6 +39,7 @@ DATA = {
|
||||
'metadef_objects': [],
|
||||
'metadef_properties': [],
|
||||
'metadef_resource_types': [],
|
||||
'metadef_tags': [],
|
||||
'tags': {},
|
||||
'locations': [],
|
||||
'tasks': {},
|
||||
@ -74,6 +75,7 @@ def reset():
|
||||
'metadef_objects': [],
|
||||
'metadef_properties': [],
|
||||
'metadef_resource_types': [],
|
||||
'metadef_tags': [],
|
||||
'tags': {},
|
||||
'locations': [],
|
||||
'tasks': {},
|
||||
@ -1668,6 +1670,200 @@ def metadef_resource_type_association_delete(context, namespace_name,
|
||||
return resource_type
|
||||
|
||||
|
||||
@log_call
|
||||
def metadef_tag_get(context, namespace_name, name):
|
||||
"""Get a metadef tag"""
|
||||
namespace = metadef_namespace_get(context, namespace_name)
|
||||
_check_namespace_visibility(context, namespace, namespace_name)
|
||||
|
||||
for tag in DATA['metadef_tags']:
|
||||
if (tag['namespace_id'] == namespace['id'] and
|
||||
tag['name'] == name):
|
||||
return tag
|
||||
else:
|
||||
msg = ("The metadata definition tag with name=%(name)s"
|
||||
" was not found in namespace=%(namespace_name)s."
|
||||
% {'name': name, 'namespace_name': namespace_name})
|
||||
LOG.debug(msg)
|
||||
raise exception.MetadefTagNotFound(name=name,
|
||||
namespace_name=namespace_name)
|
||||
|
||||
|
||||
@log_call
|
||||
def metadef_tag_get_by_id(context, namespace_name, id):
|
||||
"""Get a metadef tag"""
|
||||
namespace = metadef_namespace_get(context, namespace_name)
|
||||
_check_namespace_visibility(context, namespace, namespace_name)
|
||||
|
||||
for tag in DATA['metadef_tags']:
|
||||
if (tag['namespace_id'] == namespace['id'] and
|
||||
tag['id'] == id):
|
||||
return tag
|
||||
else:
|
||||
msg = (_("Metadata definition tag not found for id=%s") % id)
|
||||
LOG.warn(msg)
|
||||
raise exception.MetadefTagNotFound(msg)
|
||||
|
||||
|
||||
@log_call
|
||||
def metadef_tag_get_all(context, namespace_name, filters=None, marker=None,
|
||||
limit=None, sort_key='created_at', sort_dir=None,
|
||||
session=None):
|
||||
"""Get a metadef tags list"""
|
||||
|
||||
namespace = metadef_namespace_get(context, namespace_name)
|
||||
_check_namespace_visibility(context, namespace, namespace_name)
|
||||
|
||||
tags = []
|
||||
for tag in DATA['metadef_tags']:
|
||||
if tag['namespace_id'] == namespace['id']:
|
||||
tags.append(tag)
|
||||
|
||||
return tags
|
||||
|
||||
|
||||
@log_call
|
||||
def metadef_tag_create(context, namespace_name, values):
|
||||
"""Create a metadef tag"""
|
||||
global DATA
|
||||
|
||||
tag_values = copy.deepcopy(values)
|
||||
tag_name = tag_values['name']
|
||||
required_attributes = ['name']
|
||||
allowed_attributes = ['name']
|
||||
|
||||
namespace = metadef_namespace_get(context, namespace_name)
|
||||
|
||||
for tag in DATA['metadef_tags']:
|
||||
if (tag['name'] == tag_name and
|
||||
tag['namespace_id'] == namespace['id']):
|
||||
msg = ("A metadata definition tag with name=%(name)s"
|
||||
" in namespace=%(namespace_name)s already exists."
|
||||
% {'name': tag_name, 'namespace_name': namespace_name})
|
||||
LOG.debug(msg)
|
||||
raise exception.MetadefDuplicateTag(
|
||||
name=tag_name, namespace_name=namespace_name)
|
||||
|
||||
for key in required_attributes:
|
||||
if key not in tag_values:
|
||||
raise exception.Invalid('%s is a required attribute' % key)
|
||||
|
||||
incorrect_keys = set(tag_values.keys()) - set(allowed_attributes)
|
||||
if incorrect_keys:
|
||||
raise exception.Invalid(
|
||||
'The keys %s are not valid' % str(incorrect_keys))
|
||||
|
||||
tag_values['namespace_id'] = namespace['id']
|
||||
|
||||
_check_namespace_visibility(context, namespace, namespace_name)
|
||||
|
||||
tag = _format_tag(tag_values)
|
||||
DATA['metadef_tags'].append(tag)
|
||||
return tag
|
||||
|
||||
|
||||
@log_call
|
||||
def metadef_tag_create_tags(context, namespace_name, tag_list):
|
||||
"""Create a metadef tag"""
|
||||
global DATA
|
||||
|
||||
namespace = metadef_namespace_get(context, namespace_name)
|
||||
_check_namespace_visibility(context, namespace, namespace_name)
|
||||
|
||||
required_attributes = ['name']
|
||||
allowed_attributes = ['name']
|
||||
data_tag_list = []
|
||||
tag_name_list = []
|
||||
for tag_value in tag_list:
|
||||
tag_values = copy.deepcopy(tag_value)
|
||||
tag_name = tag_values['name']
|
||||
|
||||
for key in required_attributes:
|
||||
if key not in tag_values:
|
||||
raise exception.Invalid('%s is a required attribute' % key)
|
||||
|
||||
incorrect_keys = set(tag_values.keys()) - set(allowed_attributes)
|
||||
if incorrect_keys:
|
||||
raise exception.Invalid(
|
||||
'The keys %s are not valid' % str(incorrect_keys))
|
||||
|
||||
if tag_name in tag_name_list:
|
||||
msg = ("A metadata definition tag with name=%(name)s"
|
||||
" in namespace=%(namespace_name)s already exists."
|
||||
% {'name': tag_name, 'namespace_name': namespace_name})
|
||||
LOG.debug(msg)
|
||||
raise exception.MetadefDuplicateTag(
|
||||
name=tag_name, namespace_name=namespace_name)
|
||||
else:
|
||||
tag_name_list.append(tag_name)
|
||||
|
||||
tag_values['namespace_id'] = namespace['id']
|
||||
data_tag_list.append(_format_tag(tag_values))
|
||||
|
||||
DATA['metadef_tags'] = []
|
||||
for tag in data_tag_list:
|
||||
DATA['metadef_tags'].append(tag)
|
||||
|
||||
return data_tag_list
|
||||
|
||||
|
||||
@log_call
|
||||
def metadef_tag_update(context, namespace_name, id, values):
|
||||
"""Update a metadef tag"""
|
||||
global DATA
|
||||
|
||||
namespace = metadef_namespace_get(context, namespace_name)
|
||||
|
||||
_check_namespace_visibility(context, namespace, namespace_name)
|
||||
|
||||
tag = metadef_tag_get_by_id(context, namespace_name, id)
|
||||
if tag['name'] != values['name']:
|
||||
for db_tag in DATA['metadef_tags']:
|
||||
if (db_tag['name'] == values['name'] and
|
||||
db_tag['namespace_id'] == namespace['id']):
|
||||
msg = ("Invalid update. It would result in a duplicate"
|
||||
" metadata definition tag with same name=%(name)s "
|
||||
" in namespace=%(namespace_name)s."
|
||||
% {'name': tag['name'],
|
||||
'namespace_name': namespace_name})
|
||||
LOG.debug(msg)
|
||||
raise exception.MetadefDuplicateTag(
|
||||
name=tag['name'], namespace_name=namespace_name)
|
||||
|
||||
DATA['metadef_tags'].remove(tag)
|
||||
|
||||
tag.update(values)
|
||||
tag['updated_at'] = timeutils.utcnow()
|
||||
DATA['metadef_tags'].append(tag)
|
||||
return tag
|
||||
|
||||
|
||||
@log_call
|
||||
def metadef_tag_delete(context, namespace_name, name):
|
||||
"""Delete a metadef tag"""
|
||||
global DATA
|
||||
|
||||
tags = metadef_tag_get(context, namespace_name, name)
|
||||
DATA['metadef_tags'].remove(tags)
|
||||
|
||||
return tags
|
||||
|
||||
|
||||
@log_call
|
||||
def metadef_tag_count(context, namespace_name):
|
||||
"""Get metadef tag count in a namespace"""
|
||||
namespace = metadef_namespace_get(context, namespace_name)
|
||||
|
||||
_check_namespace_visibility(context, namespace, namespace_name)
|
||||
|
||||
count = 0
|
||||
for tag in DATA['metadef_tags']:
|
||||
if tag['namespace_id'] == namespace['id']:
|
||||
count = count + 1
|
||||
|
||||
return count
|
||||
|
||||
|
||||
def _format_association(namespace, resource_type, association_values):
|
||||
association = {
|
||||
'namespace_id': namespace['id'],
|
||||
@ -1739,6 +1935,19 @@ def _format_object(values):
|
||||
return object
|
||||
|
||||
|
||||
def _format_tag(values):
|
||||
dt = timeutils.utcnow()
|
||||
tag = {
|
||||
'id': _get_metadef_id(),
|
||||
'namespace_id': None,
|
||||
'name': None,
|
||||
'created_at': dt,
|
||||
'updated_at': dt
|
||||
}
|
||||
tag.update(values)
|
||||
return tag
|
||||
|
||||
|
||||
def _is_namespace_visible(context, namespace):
|
||||
"""Return true if namespace is visible in this context"""
|
||||
if context.is_admin:
|
||||
|
@ -43,6 +43,7 @@ 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
|
||||
from glance.db.sqlalchemy.metadef_api import tag as metadef_tag_api
|
||||
from glance.db.sqlalchemy import models
|
||||
from glance import i18n
|
||||
import glance.openstack.common.log as os_logging
|
||||
@ -1627,3 +1628,66 @@ def metadef_resource_type_association_get_all_by_namespace(
|
||||
session = session or get_session()
|
||||
return metadef_association_api.\
|
||||
get_all_by_namespace(context, namespace_name, session)
|
||||
|
||||
|
||||
def metadef_tag_get_all(
|
||||
context, namespace_name, filters=None, marker=None, limit=None,
|
||||
sort_key=None, sort_dir=None, session=None):
|
||||
"""Get metadata-schema tags or raise if none exist."""
|
||||
session = session or get_session()
|
||||
return metadef_tag_api.get_all(
|
||||
context, namespace_name, session,
|
||||
filters, marker, limit, sort_key, sort_dir)
|
||||
|
||||
|
||||
def metadef_tag_get(context, namespace_name, name, session=None):
|
||||
"""Get a metadata-schema tag or raise if it does not exist."""
|
||||
session = session or get_session()
|
||||
return metadef_tag_api.get(
|
||||
context, namespace_name, name, session)
|
||||
|
||||
|
||||
def metadef_tag_create(context, namespace_name, tag_dict,
|
||||
session=None):
|
||||
"""Create a metadata-schema tag or raise if it already exists."""
|
||||
session = session or get_session()
|
||||
return metadef_tag_api.create(
|
||||
context, namespace_name, tag_dict, session)
|
||||
|
||||
|
||||
def metadef_tag_create_tags(context, namespace_name, tag_list,
|
||||
session=None):
|
||||
"""Create a metadata-schema tag or raise if it already exists."""
|
||||
session = get_session()
|
||||
return metadef_tag_api.create_tags(
|
||||
context, namespace_name, tag_list, session)
|
||||
|
||||
|
||||
def metadef_tag_update(context, namespace_name, id, tag_dict,
|
||||
session=None):
|
||||
"""Update an tag or raise if it does not exist or not visible."""
|
||||
session = session or get_session()
|
||||
return metadef_tag_api.update(
|
||||
context, namespace_name, id, tag_dict, session)
|
||||
|
||||
|
||||
def metadef_tag_delete(context, namespace_name, name,
|
||||
session=None):
|
||||
"""Delete an tag or raise if namespace or tag doesn't exist."""
|
||||
session = session or get_session()
|
||||
return metadef_tag_api.delete(
|
||||
context, namespace_name, name, session)
|
||||
|
||||
|
||||
def metadef_tag_delete_namespace_content(
|
||||
context, namespace_name, session=None):
|
||||
"""Delete an tag or raise if namespace or tag doesn't exist."""
|
||||
session = session or get_session()
|
||||
return metadef_tag_api.delete_by_namespace_name(
|
||||
context, namespace_name, session)
|
||||
|
||||
|
||||
def metadef_tag_count(context, namespace_name, session=None):
|
||||
"""Get count of tags for a namespace, raise if ns doesn't exist."""
|
||||
session = session or get_session()
|
||||
return metadef_tag_api.count(context, namespace_name, session)
|
||||
|
@ -71,6 +71,10 @@ def get_metadef_objects_table(meta):
|
||||
return sqlalchemy.Table('metadef_objects', meta, autoload=True)
|
||||
|
||||
|
||||
def get_metadef_tags_table(meta):
|
||||
return sqlalchemy.Table('metadef_tags', meta, autoload=True)
|
||||
|
||||
|
||||
def _get_resource_type_id(meta, name):
|
||||
resource_types_table = get_metadef_resource_types_table(meta)
|
||||
return resource_types_table.select().\
|
||||
@ -106,6 +110,14 @@ def _get_objects(meta, namespace_id):
|
||||
execute().fetchall()
|
||||
|
||||
|
||||
def _get_tags(meta, namespace_id):
|
||||
tags_table = get_metadef_tags_table(meta)
|
||||
return (
|
||||
tags_table.select().
|
||||
where(tags_table.c.namespace_id == namespace_id).
|
||||
execute().fetchall())
|
||||
|
||||
|
||||
def _populate_metadata(meta, metadata_path=None):
|
||||
if not metadata_path:
|
||||
metadata_path = CONF.metadata_source_path
|
||||
@ -122,6 +134,7 @@ def _populate_metadata(meta, metadata_path=None):
|
||||
metadef_namespace_resource_types_tables =\
|
||||
get_metadef_namespace_resource_types_table(meta)
|
||||
metadef_objects_table = get_metadef_objects_table(meta)
|
||||
metadef_tags_table = get_metadef_tags_table(meta)
|
||||
metadef_properties_table = get_metadef_properties_table(meta)
|
||||
metadef_resource_types_table = get_metadef_resource_types_table(meta)
|
||||
|
||||
@ -208,7 +221,7 @@ def _populate_metadata(meta, metadata_path=None):
|
||||
|
||||
for object in metadata.get('objects', []):
|
||||
values = {
|
||||
'name': object.get('name', None),
|
||||
'name': object.get('name'),
|
||||
'description': object.get('description', None),
|
||||
'namespace_id': namespace_id,
|
||||
'json_schema': json.dumps(object.get('properties', None)),
|
||||
@ -216,6 +229,16 @@ def _populate_metadata(meta, metadata_path=None):
|
||||
}
|
||||
_insert_data_to_db(metadef_objects_table, values)
|
||||
|
||||
for tag in metadata.get('tags', []):
|
||||
timeutils_utcnow = timeutils.utcnow()
|
||||
values = {
|
||||
'name': tag.get('name'),
|
||||
'namespace_id': namespace_id,
|
||||
'created_at': timeutils_utcnow,
|
||||
'updated_at': timeutils_utcnow
|
||||
}
|
||||
_insert_data_to_db(metadef_tags_table, values)
|
||||
|
||||
LOG.info(_LI("File %s loaded to database."), file)
|
||||
|
||||
LOG.info(_LI("Metadata loading finished"))
|
||||
@ -224,6 +247,7 @@ def _populate_metadata(meta, metadata_path=None):
|
||||
def _clear_metadata(meta):
|
||||
metadef_tables = [get_metadef_properties_table(meta),
|
||||
get_metadef_objects_table(meta),
|
||||
get_metadef_tags_table(meta),
|
||||
get_metadef_namespace_resource_types_table(meta),
|
||||
get_metadef_namespaces_table(meta)]
|
||||
|
||||
@ -262,13 +286,15 @@ def _export_data_to_file(meta, path):
|
||||
'owner': namespace['owner'],
|
||||
'resource_type_associations': [],
|
||||
'properties': {},
|
||||
'objects': []
|
||||
'objects': [],
|
||||
'tags': []
|
||||
}
|
||||
|
||||
namespace_resource_types = _get_namespace_resource_types(meta,
|
||||
namespace_id)
|
||||
db_objects = _get_objects(meta, namespace_id)
|
||||
db_properties = _get_properties(meta, namespace_id)
|
||||
db_tags = _get_tags(meta, namespace_id)
|
||||
|
||||
resource_types = []
|
||||
for namespace_resource_type in namespace_resource_types:
|
||||
@ -303,6 +329,15 @@ def _export_data_to_file(meta, path):
|
||||
'properties': properties
|
||||
})
|
||||
|
||||
tags = []
|
||||
for tag in db_tags:
|
||||
tags.append({
|
||||
"name": tag['name']
|
||||
})
|
||||
values.update({
|
||||
'tags': tags
|
||||
})
|
||||
|
||||
try:
|
||||
file_name = ''.join([path, namespace_file_name, '.json'])
|
||||
with open(file_name, 'w') as json_file:
|
||||
|
@ -287,6 +287,8 @@ def delete_cascade(context, name, session):
|
||||
namespace_rec = _get_by_name(context, name, session)
|
||||
with session.begin():
|
||||
try:
|
||||
metadef_api.tag.delete_namespace_content(
|
||||
context, namespace_rec.id, session)
|
||||
metadef_api.object.delete_namespace_content(
|
||||
context, namespace_rec.id, session)
|
||||
metadef_api.property.delete_namespace_content(
|
||||
|
204
glance/db/sqlalchemy/metadef_api/tag.py
Normal file
204
glance/db/sqlalchemy/metadef_api/tag.py
Normal file
@ -0,0 +1,204 @@
|
||||
# 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
|
||||
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, id, session):
|
||||
try:
|
||||
query = (session.query(models.MetadefTag).filter_by(id=id))
|
||||
metadef_tag = query.one()
|
||||
except sa_orm.exc.NoResultFound:
|
||||
msg = (_LW("Metadata tag not found for id %s") % id)
|
||||
LOG.warn(msg)
|
||||
raise exc.MetadefTagNotFound(message=msg)
|
||||
return metadef_tag
|
||||
|
||||
|
||||
def _get_by_name(context, namespace_name, name, session):
|
||||
namespace = namespace_api.get(context, namespace_name, session)
|
||||
try:
|
||||
query = (session.query(models.MetadefTag).filter_by(
|
||||
name=name, namespace_id=namespace['id']))
|
||||
metadef_tag = query.one()
|
||||
except sa_orm.exc.NoResultFound:
|
||||
msg = ("The metadata tag with name=%(name)s"
|
||||
" was not found in namespace=%(namespace_name)s."
|
||||
% {'name': name, 'namespace_name': namespace_name})
|
||||
LOG.debug(msg)
|
||||
raise exc.MetadefTagNotFound(name=name,
|
||||
namespace_name=namespace_name)
|
||||
return metadef_tag
|
||||
|
||||
|
||||
def get_all(context, namespace_name, session, filters=None, marker=None,
|
||||
limit=None, sort_key='created_at', sort_dir='desc'):
|
||||
"""Get all tags that match zero or more filters.
|
||||
|
||||
:param filters: dict of filter keys and values.
|
||||
:param marker: tag 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)
|
||||
"""
|
||||
|
||||
namespace = namespace_api.get(context, namespace_name, session)
|
||||
query = (session.query(models.MetadefTag).filter_by(
|
||||
namespace_id=namespace['id']))
|
||||
|
||||
marker_tag = None
|
||||
if marker is not None:
|
||||
marker_tag = _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.MetadefTag,
|
||||
limit=limit,
|
||||
sort_keys=sort_keys,
|
||||
marker=marker_tag, sort_dir=sort_dir)
|
||||
metadef_tag = query.all()
|
||||
metadef_tag_list = []
|
||||
for tag in metadef_tag:
|
||||
metadef_tag_list.append(tag.to_dict())
|
||||
|
||||
return metadef_tag_list
|
||||
|
||||
|
||||
def create(context, namespace_name, values, session):
|
||||
namespace = namespace_api.get(context, namespace_name, session)
|
||||
values.update({'namespace_id': namespace['id']})
|
||||
|
||||
metadef_tag = models.MetadefTag()
|
||||
metadef_utils.drop_protected_attrs(models.MetadefTag, values)
|
||||
metadef_tag.update(values.copy())
|
||||
try:
|
||||
metadef_tag.save(session=session)
|
||||
except db_exc.DBDuplicateEntry:
|
||||
msg = ("A metadata tag name=%(name)s"
|
||||
" in namespace=%(namespace_name)s already exists."
|
||||
% {'name': metadef_tag.name,
|
||||
'namespace_name': namespace_name})
|
||||
LOG.debug(msg)
|
||||
raise exc.MetadefDuplicateTag(
|
||||
name=metadef_tag.name, namespace_name=namespace_name)
|
||||
|
||||
return metadef_tag.to_dict()
|
||||
|
||||
|
||||
def create_tags(context, namespace_name, tag_list, session):
|
||||
|
||||
metadef_tags_list = []
|
||||
if tag_list:
|
||||
namespace = namespace_api.get(context, namespace_name, session)
|
||||
|
||||
try:
|
||||
with session.begin():
|
||||
query = (session.query(models.MetadefTag).filter_by(
|
||||
namespace_id=namespace['id']))
|
||||
query.delete(synchronize_session='fetch')
|
||||
|
||||
for value in tag_list:
|
||||
value.update({'namespace_id': namespace['id']})
|
||||
metadef_utils.drop_protected_attrs(
|
||||
models.MetadefTag, value)
|
||||
metadef_tag = models.MetadefTag()
|
||||
metadef_tag.update(value.copy())
|
||||
metadef_tag.save(session=session)
|
||||
metadef_tags_list.append(metadef_tag.to_dict())
|
||||
except db_exc.DBDuplicateEntry:
|
||||
msg = ("A metadata tag name=%(name)s"
|
||||
" in namespace=%(namespace_name)s already exists."
|
||||
% {'name': metadef_tag.name,
|
||||
'namespace_name': namespace_name})
|
||||
LOG.debug(msg)
|
||||
raise exc.MetadefDuplicateTag(
|
||||
name=metadef_tag.name, namespace_name=namespace_name)
|
||||
|
||||
return metadef_tags_list
|
||||
|
||||
|
||||
def get(context, namespace_name, name, session):
|
||||
metadef_tag = _get_by_name(context, namespace_name, name, session)
|
||||
return metadef_tag.to_dict()
|
||||
|
||||
|
||||
def update(context, namespace_name, id, values, session):
|
||||
"""Update an tag, raise if ns not found/visible or duplicate result"""
|
||||
namespace_api.get(context, namespace_name, session)
|
||||
|
||||
metadata_tag = _get(context, id, session)
|
||||
metadef_utils.drop_protected_attrs(models.MetadefTag, values)
|
||||
# values['updated_at'] = timeutils.utcnow() - done by TS mixin
|
||||
try:
|
||||
metadata_tag.update(values.copy())
|
||||
metadata_tag.save(session=session)
|
||||
except db_exc.DBDuplicateEntry:
|
||||
msg = ("Invalid update. It would result in a duplicate"
|
||||
" metadata tag with same name=%(name)s"
|
||||
" in namespace=%(namespace_name)s."
|
||||
% {'name': values['name'],
|
||||
'namespace_name': namespace_name})
|
||||
LOG.debug(msg)
|
||||
raise exc.MetadefDuplicateTag(
|
||||
name=values['name'], namespace_name=namespace_name)
|
||||
|
||||
return metadata_tag.to_dict()
|
||||
|
||||
|
||||
def delete(context, namespace_name, name, session):
|
||||
namespace_api.get(context, namespace_name, session)
|
||||
md_tag = _get_by_name(context, namespace_name, name, session)
|
||||
|
||||
session.delete(md_tag)
|
||||
session.flush()
|
||||
|
||||
return md_tag.to_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.MetadefTag).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.MetadefTag.id)).filter_by(
|
||||
namespace_id=namespace['id']))
|
||||
return query.scalar()
|
@ -0,0 +1,57 @@
|
||||
# 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 sqlalchemy.schema import (
|
||||
Column, Index, MetaData, Table, UniqueConstraint) # noqa
|
||||
|
||||
from glance.db.sqlalchemy.migrate_repo.schema import (
|
||||
DateTime, Integer, String, create_tables, drop_tables) # noqa
|
||||
|
||||
|
||||
def define_metadef_tags_table(meta):
|
||||
_constr_kwargs = {}
|
||||
metadef_tags = Table('metadef_tags',
|
||||
meta,
|
||||
Column('id', Integer(), primary_key=True,
|
||||
nullable=False),
|
||||
Column('namespace_id', Integer(),
|
||||
nullable=False),
|
||||
Column('name', String(80), nullable=False),
|
||||
Column('created_at', DateTime(), nullable=False),
|
||||
Column('updated_at', DateTime()),
|
||||
UniqueConstraint('namespace_id', 'name',
|
||||
**_constr_kwargs),
|
||||
mysql_engine='InnoDB',
|
||||
extend_existing=False)
|
||||
|
||||
if meta.bind.name != 'ibm_db_sa':
|
||||
Index('ix_tags_namespace_id_name',
|
||||
metadef_tags.c.namespace_id,
|
||||
metadef_tags.c.name)
|
||||
|
||||
return metadef_tags
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = MetaData()
|
||||
meta.bind = migrate_engine
|
||||
tables = [define_metadef_tags_table(meta)]
|
||||
create_tables(tables)
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta = MetaData()
|
||||
meta.bind = migrate_engine
|
||||
tables = [define_metadef_tags_table(meta)]
|
||||
drop_tables(tables)
|
@ -137,9 +137,23 @@ class MetadefResourceType(BASE_DICT, GlanceMetadefBase):
|
||||
primaryjoin=id == MetadefNamespaceResourceType.resource_type_id)
|
||||
|
||||
|
||||
class MetadefTag(BASE_DICT, GlanceMetadefBase):
|
||||
"""Represents a metadata-schema tag in the data store."""
|
||||
__tablename__ = 'metadef_tags'
|
||||
__table_args__ = (Index('ix_metadef_tags_namespace_id',
|
||||
'namespace_id', 'name'),
|
||||
Index('ix_metadef_tags_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)
|
||||
|
||||
|
||||
def register_models(engine):
|
||||
"""Create database tables for all models with the given engine."""
|
||||
models = (MetadefNamespace, MetadefObject, MetadefProperty,
|
||||
MetadefTag,
|
||||
MetadefResourceType, MetadefNamespaceResourceType)
|
||||
for model in models:
|
||||
model.metadata.create_all(engine)
|
||||
@ -148,6 +162,7 @@ def register_models(engine):
|
||||
def unregister_models(engine):
|
||||
"""Drop database tables for all models with the given engine."""
|
||||
models = (MetadefObject, MetadefProperty, MetadefNamespaceResourceType,
|
||||
MetadefTag,
|
||||
MetadefNamespace, MetadefResourceType)
|
||||
for model in models:
|
||||
model.metadata.drop_all(engine)
|
||||
|
@ -606,3 +606,32 @@ class MetadefPropertyFactory(object):
|
||||
name,
|
||||
schema
|
||||
)
|
||||
|
||||
|
||||
class MetadefTag(object):
|
||||
|
||||
def __init__(self, namespace, tag_id, name, created_at, updated_at):
|
||||
self.namespace = namespace
|
||||
self.tag_id = tag_id
|
||||
self.name = name
|
||||
self.created_at = created_at
|
||||
self.updated_at = updated_at
|
||||
|
||||
def delete(self):
|
||||
if self.namespace.protected:
|
||||
raise exception.ProtectedMetadefTagDelete(tag_name=self.name)
|
||||
|
||||
|
||||
class MetadefTagFactory(object):
|
||||
|
||||
def new_tag(self, namespace, name, **kwargs):
|
||||
tag_id = str(uuid.uuid4())
|
||||
created_at = timeutils.utcnow()
|
||||
updated_at = created_at
|
||||
return MetadefTag(
|
||||
namespace,
|
||||
tag_id,
|
||||
name,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
|
@ -255,6 +255,11 @@ class MetadefNamespaceRepo(object):
|
||||
result = self.base.remove_properties(base_item)
|
||||
return self.namespace_proxy_helper.proxy(result)
|
||||
|
||||
def remove_tags(self, item):
|
||||
base_item = self.namespace_proxy_helper.unproxy(item)
|
||||
result = self.base.remove_tags(base_item)
|
||||
return self.namespace_proxy_helper.proxy(result)
|
||||
|
||||
def save(self, item):
|
||||
base_item = self.namespace_proxy_helper.unproxy(item)
|
||||
result = self.base.save(base_item)
|
||||
@ -464,3 +469,68 @@ class MetadefPropertyFactory(object):
|
||||
def new_namespace_property(self, **kwargs):
|
||||
t = self.base.new_namespace_property(**kwargs)
|
||||
return self.meta_object_helper.proxy(t)
|
||||
|
||||
|
||||
# Metadef tag classes
|
||||
class MetadefTagRepo(object):
|
||||
def __init__(self, base,
|
||||
tag_proxy_class=None, tag_proxy_kwargs=None):
|
||||
self.base = base
|
||||
self.tag_proxy_helper = Helper(tag_proxy_class,
|
||||
tag_proxy_kwargs)
|
||||
|
||||
def get(self, namespace, name):
|
||||
meta_tag = self.base.get(namespace, name)
|
||||
return self.tag_proxy_helper.proxy(meta_tag)
|
||||
|
||||
def add(self, meta_tag):
|
||||
self.base.add(self.tag_proxy_helper.unproxy(meta_tag))
|
||||
|
||||
def add_tags(self, meta_tags):
|
||||
tags_list = []
|
||||
for meta_tag in meta_tags:
|
||||
tags_list.append(self.tag_proxy_helper.unproxy(meta_tag))
|
||||
self.base.add_tags(tags_list)
|
||||
|
||||
def list(self, *args, **kwargs):
|
||||
tags = self.base.list(*args, **kwargs)
|
||||
return [self.tag_proxy_helper.proxy(meta_tag) for meta_tag
|
||||
in tags]
|
||||
|
||||
def remove(self, item):
|
||||
base_item = self.tag_proxy_helper.unproxy(item)
|
||||
result = self.base.remove(base_item)
|
||||
return self.tag_proxy_helper.proxy(result)
|
||||
|
||||
def save(self, item):
|
||||
base_item = self.tag_proxy_helper.unproxy(item)
|
||||
result = self.base.save(base_item)
|
||||
return self.tag_proxy_helper.proxy(result)
|
||||
|
||||
|
||||
class MetadefTag(object):
|
||||
def __init__(self, base):
|
||||
self.base = base
|
||||
|
||||
namespace = _proxy('base', 'namespace')
|
||||
tag_id = _proxy('base', 'tag_id')
|
||||
name = _proxy('base', 'name')
|
||||
created_at = _proxy('base', 'created_at')
|
||||
updated_at = _proxy('base', 'updated_at')
|
||||
|
||||
def delete(self):
|
||||
self.base.delete()
|
||||
|
||||
|
||||
class MetadefTagFactory(object):
|
||||
def __init__(self,
|
||||
base,
|
||||
meta_tag_proxy_class=None,
|
||||
meta_tag_proxy_kwargs=None):
|
||||
self.meta_tag_helper = Helper(meta_tag_proxy_class,
|
||||
meta_tag_proxy_kwargs)
|
||||
self.base = base
|
||||
|
||||
def new_tag(self, **kwargs):
|
||||
t = self.base.new_tag(**kwargs)
|
||||
return self.meta_tag_helper.proxy(t)
|
||||
|
@ -189,3 +189,19 @@ class Gateway(object):
|
||||
authorized_prop_repo = authorization.MetadefPropertyRepoProxy(
|
||||
policy_prop_repo, context)
|
||||
return authorized_prop_repo
|
||||
|
||||
def get_metadef_tag_factory(self, context):
|
||||
tag_factory = glance.domain.MetadefTagFactory()
|
||||
policy_tag_factory = policy.MetadefTagFactoryProxy(
|
||||
tag_factory, context, self.policy)
|
||||
authorized_tag_factory = authorization.MetadefTagFactoryProxy(
|
||||
policy_tag_factory, context)
|
||||
return authorized_tag_factory
|
||||
|
||||
def get_metadef_tag_repo(self, context):
|
||||
tag_repo = glance.db.MetadefTagRepo(context, self.db_api)
|
||||
policy_tag_repo = policy.MetadefTagRepoProxy(
|
||||
tag_repo, context, self.policy)
|
||||
authorized_tag_repo = authorization.MetadefTagRepoProxy(
|
||||
policy_tag_repo, context)
|
||||
return authorized_tag_repo
|
||||
|
@ -48,5 +48,11 @@
|
||||
"get_metadef_property":"",
|
||||
"get_metadef_properties":"",
|
||||
"modify_metadef_property":"",
|
||||
"add_metadef_property":""
|
||||
"add_metadef_property":"",
|
||||
|
||||
"get_metadef_tag":"",
|
||||
"get_metadef_tags":"",
|
||||
"modify_metadef_tag":"",
|
||||
"add_metadef_tag":"",
|
||||
"add_metadef_tags":""
|
||||
}
|
||||
|
@ -77,6 +77,23 @@ def build_property_fixture(**kwargs):
|
||||
return property
|
||||
|
||||
|
||||
def build_tag_fixture(**kwargs):
|
||||
# Full testing of required and schema done via rest api tests
|
||||
tag = {
|
||||
'namespace_id': 1,
|
||||
'name': u'test-tag-name',
|
||||
}
|
||||
tag.update(kwargs)
|
||||
return tag
|
||||
|
||||
|
||||
def build_tags_fixture(tag_name_list):
|
||||
tag_list = []
|
||||
for tag_name in tag_name_list:
|
||||
tag_list.append({'name': tag_name})
|
||||
return tag_list
|
||||
|
||||
|
||||
class TestMetadefDriver(test_utils.BaseTestCase):
|
||||
|
||||
"""Test Driver class for Metadef tests."""
|
||||
@ -469,10 +486,117 @@ class MetadefResourceTypeAssociationTests(object):
|
||||
self._assert_saved_fields(fixture, item)
|
||||
|
||||
|
||||
class MetadefTagTests(object):
|
||||
|
||||
def test_tag_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_tag = build_tag_fixture(namespace_id=created_ns['id'])
|
||||
created_tag = self.db_api.metadef_tag_create(
|
||||
self.context, created_ns['namespace'], fixture_tag)
|
||||
self._assert_saved_fields(fixture_tag, created_tag)
|
||||
|
||||
def test_tag_create_tags(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)
|
||||
|
||||
tags = build_tags_fixture(['Tag1', 'Tag2', 'Tag3'])
|
||||
created_tags = self.db_api.metadef_tag_create_tags(
|
||||
self.context, created_ns['namespace'], tags)
|
||||
actual = set([tag['name'] for tag in created_tags])
|
||||
expected = set(['Tag1', 'Tag2', 'Tag3'])
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_tag_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_tag = build_tag_fixture(namespace_id=created_ns['id'])
|
||||
created_tag = self.db_api.metadef_tag_create(
|
||||
self.context, created_ns['namespace'], fixture_tag)
|
||||
|
||||
found_tag = self.db_api.metadef_tag_get(
|
||||
self.context, created_ns['namespace'], created_tag['name'])
|
||||
self._assert_saved_fields(fixture_tag, found_tag)
|
||||
|
||||
def test_tag_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_tag_fixture(namespace_id=ns_created['id'])
|
||||
created_tag1 = self.db_api.metadef_tag_create(
|
||||
self.context, ns_created['namespace'], fixture1)
|
||||
self.assertIsNotNone(created_tag1, "Could not create tag 1.")
|
||||
|
||||
fixture2 = build_tag_fixture(namespace_id=ns_created['id'],
|
||||
name='test-tag-2')
|
||||
created_tag2 = self.db_api.metadef_tag_create(
|
||||
self.context, ns_created['namespace'], fixture2)
|
||||
self.assertIsNotNone(created_tag2, "Could not create tag 2.")
|
||||
|
||||
found = self.db_api.metadef_tag_get_all(
|
||||
self.context, ns_created['namespace'], sort_key='created_at')
|
||||
self.assertEqual(2, len(found))
|
||||
|
||||
def test_tag_update(self):
|
||||
delta = {'name': u'New-name'}
|
||||
|
||||
fixture_ns = build_namespace_fixture()
|
||||
created_ns = self.db_api.metadef_namespace_create(self.context,
|
||||
fixture_ns)
|
||||
self.assertIsNotNone(created_ns['namespace'])
|
||||
|
||||
tag_fixture = build_tag_fixture(namespace_id=created_ns['id'])
|
||||
created_tag = self.db_api.metadef_tag_create(
|
||||
self.context, created_ns['namespace'], tag_fixture)
|
||||
self.assertIsNotNone(created_tag, "Could not create a tag.")
|
||||
|
||||
delta_dict = {}
|
||||
delta_dict.update(delta.copy())
|
||||
|
||||
updated = self.db_api.metadef_tag_update(
|
||||
self.context, created_ns['namespace'],
|
||||
created_tag['id'], delta_dict)
|
||||
self.assertEqual(delta['name'], updated['name'])
|
||||
|
||||
def test_tag_delete(self):
|
||||
fixture_ns = build_namespace_fixture()
|
||||
created_ns = self.db_api.metadef_namespace_create(
|
||||
self.context, fixture_ns)
|
||||
self.assertIsNotNone(created_ns['namespace'])
|
||||
|
||||
tag_fixture = build_tag_fixture(namespace_id=created_ns['id'])
|
||||
created_tag = self.db_api.metadef_tag_create(
|
||||
self.context, created_ns['namespace'], tag_fixture)
|
||||
self.assertIsNotNone(created_tag, "Could not create a tag.")
|
||||
|
||||
self.db_api.metadef_tag_delete(
|
||||
self.context, created_ns['namespace'], created_tag['name'])
|
||||
|
||||
self.assertRaises(exception.NotFound,
|
||||
self.db_api.metadef_tag_get,
|
||||
self.context, created_ns['namespace'],
|
||||
created_tag['name'])
|
||||
|
||||
|
||||
class MetadefDriverTests(MetadefNamespaceTests,
|
||||
MetadefResourceTypeTests,
|
||||
MetadefResourceTypeAssociationTests,
|
||||
MetadefPropertyTests,
|
||||
MetadefObjectTests):
|
||||
MetadefObjectTests,
|
||||
MetadefTagTests):
|
||||
# collection class
|
||||
pass
|
||||
|
179
glance/tests/functional/v2/test_metadef_tags.py
Normal file
179
glance/tests/functional/v2/test_metadef_tags.py
Normal file
@ -0,0 +1,179 @@
|
||||
# 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 uuid
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
import requests
|
||||
|
||||
from glance.tests import functional
|
||||
|
||||
TENANT1 = str(uuid.uuid4())
|
||||
|
||||
|
||||
class TestMetadefTags(functional.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestMetadefTags, self).setUp()
|
||||
self.cleanup()
|
||||
self.api_server.deployment_flavor = 'noauth'
|
||||
self.start_servers(**self.__dict__.copy())
|
||||
|
||||
def _url(self, path):
|
||||
return 'http://127.0.0.1:%d%s' % (self.api_port, path)
|
||||
|
||||
def _headers(self, custom_headers=None):
|
||||
base_headers = {
|
||||
'X-Identity-Status': 'Confirmed',
|
||||
'X-Auth-Token': '932c5c84-02ac-4fe5-a9ba-620af0e2bb96',
|
||||
'X-User-Id': 'f9a41d13-0c13-47e9-bee2-ce4e8bfe958e',
|
||||
'X-Tenant-Id': TENANT1,
|
||||
'X-Roles': 'admin',
|
||||
}
|
||||
base_headers.update(custom_headers or {})
|
||||
return base_headers
|
||||
|
||||
def test_metadata_tags_lifecycle(self):
|
||||
# Namespace should not exist
|
||||
path = self._url('/v2/metadefs/namespaces/MyNamespace')
|
||||
response = requests.get(path, headers=self._headers())
|
||||
self.assertEqual(404, response.status_code)
|
||||
|
||||
# Create a namespace
|
||||
path = self._url('/v2/metadefs/namespaces')
|
||||
headers = self._headers({'content-type': 'application/json'})
|
||||
namespace_name = 'MyNamespace'
|
||||
data = jsonutils.dumps({
|
||||
"namespace": namespace_name,
|
||||
"display_name": "My User Friendly Namespace",
|
||||
"description": "My description",
|
||||
"visibility": "public",
|
||||
"protected": False,
|
||||
"owner": "The Test Owner"}
|
||||
)
|
||||
response = requests.post(path, headers=headers, data=data)
|
||||
self.assertEqual(201, response.status_code)
|
||||
|
||||
# Metadata tags should not exist
|
||||
path = self._url('/v2/metadefs/namespaces/MyNamespace/tags/tag1')
|
||||
response = requests.get(path, headers=self._headers())
|
||||
self.assertEqual(404, response.status_code)
|
||||
|
||||
# Create a tag
|
||||
path = self._url('/v2/metadefs/namespaces/MyNamespace/tags')
|
||||
headers = self._headers({'content-type': 'application/json'})
|
||||
metadata_tag_name = "tag1"
|
||||
data = jsonutils.dumps(
|
||||
{
|
||||
"name": metadata_tag_name
|
||||
}
|
||||
)
|
||||
response = requests.post(path, headers=headers, data=data)
|
||||
self.assertEqual(201, response.status_code)
|
||||
|
||||
# Get the metadata tag created above
|
||||
path = self._url('/v2/metadefs/namespaces/%s/tags/%s' %
|
||||
(namespace_name, metadata_tag_name))
|
||||
response = requests.get(path,
|
||||
headers=self._headers())
|
||||
self.assertEqual(200, response.status_code)
|
||||
metadata_tag = jsonutils.loads(response.text)
|
||||
self.assertEqual("tag1", metadata_tag['name'])
|
||||
|
||||
# Returned tag should match the created tag
|
||||
metadata_tag = jsonutils.loads(response.text)
|
||||
checked_keys = set([
|
||||
u'name',
|
||||
u'created_at',
|
||||
u'updated_at'
|
||||
])
|
||||
self.assertEqual(checked_keys, set(metadata_tag.keys()))
|
||||
expected_metadata_tag = {
|
||||
"name": metadata_tag_name
|
||||
}
|
||||
|
||||
# Simple key values
|
||||
checked_values = set([
|
||||
u'name'
|
||||
])
|
||||
for key, value in expected_metadata_tag.items():
|
||||
if(key in checked_values):
|
||||
self.assertEqual(metadata_tag[key], value, key)
|
||||
|
||||
# The metadata_tag should be mutable
|
||||
path = self._url('/v2/metadefs/namespaces/%s/tags/%s' %
|
||||
(namespace_name, metadata_tag_name))
|
||||
media_type = 'application/json'
|
||||
headers = self._headers({'content-type': media_type})
|
||||
metadata_tag_name = "tag1-UPDATED"
|
||||
data = jsonutils.dumps(
|
||||
{
|
||||
"name": metadata_tag_name
|
||||
}
|
||||
)
|
||||
response = requests.put(path, headers=headers, data=data)
|
||||
self.assertEqual(200, response.status_code, response.text)
|
||||
|
||||
# Returned metadata_tag should reflect the changes
|
||||
metadata_tag = jsonutils.loads(response.text)
|
||||
self.assertEqual('tag1-UPDATED', metadata_tag['name'])
|
||||
|
||||
# Updates should persist across requests
|
||||
path = self._url('/v2/metadefs/namespaces/%s/tags/%s' %
|
||||
(namespace_name, metadata_tag_name))
|
||||
response = requests.get(path, headers=self._headers())
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual('tag1-UPDATED', metadata_tag['name'])
|
||||
|
||||
# Deletion of metadata_tag_name
|
||||
path = self._url('/v2/metadefs/namespaces/%s/tags/%s' %
|
||||
(namespace_name, metadata_tag_name))
|
||||
response = requests.delete(path, headers=self._headers())
|
||||
self.assertEqual(204, response.status_code)
|
||||
|
||||
# metadata_tag_name should not exist
|
||||
path = self._url('/v2/metadefs/namespaces/%s/tags/%s' %
|
||||
(namespace_name, metadata_tag_name))
|
||||
response = requests.get(path, headers=self._headers())
|
||||
self.assertEqual(404, response.status_code)
|
||||
|
||||
# Create multiple tags.
|
||||
path = self._url('/v2/metadefs/namespaces/%s/tags' %
|
||||
(namespace_name))
|
||||
headers = self._headers({'content-type': 'application/json'})
|
||||
data = jsonutils.dumps(
|
||||
{"tags": [{"name": "tag1"}, {"name": "tag2"}, {"name": "tag3"}]}
|
||||
)
|
||||
response = requests.put(path, headers=headers, data=data)
|
||||
self.assertEqual(201, response.status_code)
|
||||
|
||||
# List out the three new tags.
|
||||
response = requests.get(path, headers=self._headers())
|
||||
self.assertEqual(200, response.status_code)
|
||||
tags = jsonutils.loads(response.text)['tags']
|
||||
self.assertEqual(3, len(tags))
|
||||
|
||||
# Attempt to create bogus duplicate tag4
|
||||
data = jsonutils.dumps(
|
||||
{"tags": [{"name": "tag4"}, {"name": "tag5"}, {"name": "tag4"}]}
|
||||
)
|
||||
response = requests.put(path, headers=headers, data=data)
|
||||
self.assertEqual(409, response.status_code)
|
||||
|
||||
# Verify the previous 3 still exist
|
||||
response = requests.get(path, headers=self._headers())
|
||||
self.assertEqual(200, response.status_code)
|
||||
tags = jsonutils.loads(response.text)['tags']
|
||||
self.assertEqual(3, len(tags))
|
@ -41,6 +41,12 @@ OBJECT1 = 'Object1'
|
||||
OBJECT2 = 'Object2'
|
||||
OBJECT3 = 'Object3'
|
||||
|
||||
TAG1 = 'Tag1'
|
||||
TAG2 = 'Tag2'
|
||||
TAG3 = 'Tag3'
|
||||
TAG4 = 'Tag4'
|
||||
TAG5 = 'Tag5'
|
||||
|
||||
RESOURCE_TYPE1 = 'ResourceType1'
|
||||
RESOURCE_TYPE2 = 'ResourceType2'
|
||||
RESOURCE_TYPE3 = 'ResourceType3'
|
||||
@ -79,6 +85,26 @@ def _db_object_fixture(name, **kwargs):
|
||||
return obj
|
||||
|
||||
|
||||
def _db_tag_fixture(name, **kwargs):
|
||||
obj = {
|
||||
'name': name
|
||||
}
|
||||
obj.update(kwargs)
|
||||
return obj
|
||||
|
||||
|
||||
def _db_tags_fixture(names=None):
|
||||
tags = []
|
||||
if names:
|
||||
tag_name_list = names
|
||||
else:
|
||||
tag_name_list = [TAG1, TAG2, TAG3]
|
||||
|
||||
for tag_name in tag_name_list:
|
||||
tags.append(_db_tag_fixture(tag_name))
|
||||
return tags
|
||||
|
||||
|
||||
def _db_resource_type_fixture(name, **kwargs):
|
||||
obj = {
|
||||
'name': name,
|
||||
@ -112,15 +138,19 @@ class TestMetadefRepo(test_utils.BaseTestCase):
|
||||
self.db)
|
||||
self.object_repo = glance.db.MetadefObjectRepo(self.context,
|
||||
self.db)
|
||||
self.tag_repo = glance.db.MetadefTagRepo(self.context,
|
||||
self.db)
|
||||
self.resource_type_repo = glance.db.\
|
||||
MetadefResourceTypeRepo(self.context, self.db)
|
||||
self.namespace_factory = glance.domain.MetadefNamespaceFactory()
|
||||
self.property_factory = glance.domain.MetadefPropertyFactory()
|
||||
self.object_factory = glance.domain.MetadefObjectFactory()
|
||||
self.tag_factory = glance.domain.MetadefTagFactory()
|
||||
self.resource_type_factory = glance.domain.MetadefResourceTypeFactory()
|
||||
self._create_namespaces()
|
||||
self._create_properties()
|
||||
self._create_objects()
|
||||
self._create_tags()
|
||||
self._create_resource_types()
|
||||
|
||||
def _create_namespaces(self):
|
||||
@ -179,6 +209,17 @@ class TestMetadefRepo(test_utils.BaseTestCase):
|
||||
[self.db.metadef_object_create(self.context, NAMESPACE4, object)
|
||||
for object in self.objects]
|
||||
|
||||
def _create_tags(self):
|
||||
self.tags = [
|
||||
_db_tag_fixture(name=TAG1),
|
||||
_db_tag_fixture(name=TAG2),
|
||||
_db_tag_fixture(name=TAG3),
|
||||
]
|
||||
[self.db.metadef_tag_create(self.context, NAMESPACE1, tag)
|
||||
for tag in self.tags]
|
||||
[self.db.metadef_tag_create(self.context, NAMESPACE4, tag)
|
||||
for tag in self.tags]
|
||||
|
||||
def _create_resource_types(self):
|
||||
self.resource_types = [
|
||||
_db_resource_type_fixture(name=RESOURCE_TYPE1,
|
||||
@ -426,3 +467,101 @@ class TestMetadefRepo(test_utils.BaseTestCase):
|
||||
resource_type = self.resource_type_repo.list(
|
||||
filters={'namespace': NAMESPACE1})
|
||||
self.assertEqual(0, len(resource_type))
|
||||
|
||||
def test_get_tag(self):
|
||||
tag = self.tag_repo.get(NAMESPACE1, TAG1)
|
||||
namespace = self.namespace_repo.get(NAMESPACE1)
|
||||
self.assertEqual(TAG1, tag.name)
|
||||
self.assertEqual(namespace.namespace, tag.namespace.namespace)
|
||||
|
||||
def test_get_tag_not_found(self):
|
||||
exc = self.assertRaises(exception.NotFound, self.tag_repo.get,
|
||||
NAMESPACE2, TAG1)
|
||||
self.assertIn(TAG1, utils.exception_to_str(exc))
|
||||
|
||||
def test_list_tag(self):
|
||||
tags = self.tag_repo.list(filters={'namespace': NAMESPACE1})
|
||||
tag_names = set([t.name for t in tags])
|
||||
self.assertEqual(set([TAG1, TAG2, TAG3]), tag_names)
|
||||
|
||||
def test_list_tag_empty_result(self):
|
||||
tags = self.tag_repo.list(filters={'namespace': NAMESPACE2})
|
||||
tag_names = set([t.name for t in tags])
|
||||
self.assertEqual(set([]), tag_names)
|
||||
|
||||
def test_list_tag_namespace_not_found(self):
|
||||
exc = self.assertRaises(exception.NotFound, self.tag_repo.list,
|
||||
filters={'namespace': 'not-a-namespace'})
|
||||
self.assertIn('not-a-namespace', utils.exception_to_str(exc))
|
||||
|
||||
def test_add_tag(self):
|
||||
# NOTE(pawel-koniszewski): Change db_tag_fixture to
|
||||
# tag_factory when tag primary key in DB
|
||||
# will be changed from Integer to UUID
|
||||
tag = _db_tag_fixture(name='added_tag')
|
||||
self.assertEqual('added_tag', tag['name'])
|
||||
self.db.metadef_tag_create(self.context, NAMESPACE1, tag)
|
||||
retrieved_tag = self.tag_repo.get(NAMESPACE1, 'added_tag')
|
||||
self.assertEqual('added_tag', retrieved_tag.name)
|
||||
|
||||
def test_add_tags(self):
|
||||
tags = self.tag_repo.list(filters={'namespace': NAMESPACE1})
|
||||
tag_names = set([t.name for t in tags])
|
||||
self.assertEqual(set([TAG1, TAG2, TAG3]), tag_names)
|
||||
|
||||
tags = _db_tags_fixture([TAG3, TAG4, TAG5])
|
||||
self.db.metadef_tag_create_tags(self.context, NAMESPACE1, tags)
|
||||
|
||||
tags = self.tag_repo.list(filters={'namespace': NAMESPACE1})
|
||||
tag_names = set([t.name for t in tags])
|
||||
self.assertEqual(set([TAG3, TAG4, TAG5]), tag_names)
|
||||
|
||||
def test_add_duplicate_tags_with_pre_existing_tags(self):
|
||||
tags = self.tag_repo.list(filters={'namespace': NAMESPACE1})
|
||||
tag_names = set([t.name for t in tags])
|
||||
self.assertEqual(set([TAG1, TAG2, TAG3]), tag_names)
|
||||
|
||||
tags = _db_tags_fixture([TAG5, TAG4, TAG5])
|
||||
self.assertRaises(exception.Duplicate,
|
||||
self.db.metadef_tag_create_tags,
|
||||
self.context, NAMESPACE1, tags)
|
||||
|
||||
tags = self.tag_repo.list(filters={'namespace': NAMESPACE1})
|
||||
tag_names = set([t.name for t in tags])
|
||||
self.assertEqual(set([TAG1, TAG2, TAG3]), tag_names)
|
||||
|
||||
def test_add_tag_namespace_forbidden(self):
|
||||
# NOTE(pawel-koniszewski): Change db_tag_fixture to
|
||||
# tag_factory when tag primary key in DB
|
||||
# will be changed from Integer to UUID
|
||||
tag = _db_tag_fixture(name='added_tag')
|
||||
self.assertEqual('added_tag', tag['name'])
|
||||
self.assertRaises(exception.Forbidden, self.db.metadef_tag_create,
|
||||
self.context, NAMESPACE3, tag)
|
||||
|
||||
def test_add_tag_namespace_not_found(self):
|
||||
# NOTE(pawel-koniszewski): Change db_tag_fixture to
|
||||
# tag_factory when tag primary key in DB
|
||||
# will be changed from Integer to UUID
|
||||
tag = _db_tag_fixture(name='added_tag')
|
||||
self.assertEqual('added_tag', tag['name'])
|
||||
self.assertRaises(exception.NotFound, self.db.metadef_tag_create,
|
||||
self.context, 'not-a-namespace', tag)
|
||||
|
||||
def test_save_tag(self):
|
||||
tag = self.tag_repo.get(NAMESPACE1, TAG1)
|
||||
self.tag_repo.save(tag)
|
||||
tag = self.tag_repo.get(NAMESPACE1, TAG1)
|
||||
self.assertEqual(TAG1, tag.name)
|
||||
|
||||
def test_remove_tag(self):
|
||||
tag = self.tag_repo.get(NAMESPACE1, TAG1)
|
||||
self.tag_repo.remove(tag)
|
||||
self.assertRaises(exception.NotFound, self.tag_repo.get,
|
||||
NAMESPACE1, TAG1)
|
||||
|
||||
def test_remove_tag_not_found(self):
|
||||
fake_name = 'fake_name'
|
||||
tag = self.tag_repo.get(NAMESPACE1, TAG1)
|
||||
tag.name = fake_name
|
||||
self.assertRaises(exception.NotFound, self.tag_repo.remove, tag)
|
||||
|
@ -1336,6 +1336,28 @@ class MigrationsMixin(test_migrations.WalkVersionsMixin):
|
||||
|
||||
self.assertIsNone(image_member['status'])
|
||||
|
||||
def _pre_upgrade_038(self, engine):
|
||||
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
|
||||
db_utils.get_table, engine, 'metadef_tags')
|
||||
|
||||
def _check_038(self, engine, data):
|
||||
meta = sqlalchemy.MetaData()
|
||||
meta.bind = engine
|
||||
|
||||
# metadef_tags
|
||||
table = sqlalchemy.Table("metadef_tags", meta, autoload=True)
|
||||
expected_cols = [u'id',
|
||||
u'namespace_id',
|
||||
u'name',
|
||||
u'created_at',
|
||||
u'updated_at']
|
||||
col_data = [col.name for col in table.columns]
|
||||
self.assertEqual(expected_cols, col_data)
|
||||
|
||||
def _post_downgrade_038(self, engine):
|
||||
self.assertRaises(sqlalchemy.exc.NoSuchTableError,
|
||||
db_utils.get_table, engine, 'metadef_tags')
|
||||
|
||||
|
||||
class TestMysqlMigrations(test_base.MySQLOpportunisticTestCase,
|
||||
MigrationsMixin):
|
||||
@ -1392,6 +1414,7 @@ class ModelsMigrationSyncMixin(object):
|
||||
# (except 'migrate_version')
|
||||
if name in ['migrate_version', 'metadef_objects', 'metadef_namespaces',
|
||||
'metadef_properties', 'metadef_resource_types',
|
||||
'metadef_tags',
|
||||
'metadef_namespace_resource_types'] and type_ == 'table':
|
||||
return False
|
||||
return True
|
||||
|
@ -21,6 +21,7 @@ from glance.api.v2 import metadef_namespaces as namespaces
|
||||
from glance.api.v2 import metadef_objects as objects
|
||||
from glance.api.v2 import metadef_properties as properties
|
||||
from glance.api.v2 import metadef_resource_types as resource_types
|
||||
from glance.api.v2 import metadef_tags as tags
|
||||
import glance.api.v2.model.metadef_namespace
|
||||
from glance.tests.unit import base
|
||||
import glance.tests.unit.utils as unit_test_utils
|
||||
@ -49,6 +50,12 @@ RESOURCE_TYPE2 = 'ResourceType2'
|
||||
RESOURCE_TYPE3 = 'ResourceType3'
|
||||
RESOURCE_TYPE4 = 'ResourceType4'
|
||||
|
||||
TAG1 = 'Tag1'
|
||||
TAG2 = 'Tag2'
|
||||
TAG3 = 'Tag3'
|
||||
TAG4 = 'Tag4'
|
||||
TAG5 = 'Tag5'
|
||||
|
||||
TENANT1 = '6838eb7b-6ded-434a-882c-b344c77fe8df'
|
||||
TENANT2 = '2c014f32-55eb-467d-8fcb-4bd706012f81'
|
||||
TENANT3 = '5a3e60e8-cfa9-4a9e-a90a-62b42cea92b8'
|
||||
@ -99,6 +106,26 @@ def _db_resource_type_fixture(name, **kwargs):
|
||||
return obj
|
||||
|
||||
|
||||
def _db_tag_fixture(name, **kwargs):
|
||||
obj = {
|
||||
'name': name
|
||||
}
|
||||
obj.update(kwargs)
|
||||
return obj
|
||||
|
||||
|
||||
def _db_tags_fixture(tag_names=None):
|
||||
tag_list = []
|
||||
if not tag_names:
|
||||
tag_names = [TAG1, TAG2, TAG3]
|
||||
|
||||
for tag_name in tag_names:
|
||||
tag = glance.api.v2.model.metadef_tag.MetadefTag()
|
||||
tag.name = tag_name
|
||||
tag_list.append(tag)
|
||||
return tag_list
|
||||
|
||||
|
||||
def _db_namespace_resource_type_fixture(name, **kwargs):
|
||||
obj = {
|
||||
'name': name,
|
||||
@ -120,6 +147,7 @@ class TestMetadefsControllers(base.IsolatedUnitTest):
|
||||
self._create_objects()
|
||||
self._create_resource_types()
|
||||
self._create_namespaces_resource_types()
|
||||
self._create_tags()
|
||||
self.namespace_controller = namespaces.NamespaceController(self.db,
|
||||
self.policy)
|
||||
self.property_controller = \
|
||||
@ -128,6 +156,7 @@ class TestMetadefsControllers(base.IsolatedUnitTest):
|
||||
self.policy)
|
||||
self.rt_controller = resource_types.ResourceTypeController(self.db,
|
||||
self.policy)
|
||||
self.tag_controller = tags.TagsController(self.db, self.policy)
|
||||
|
||||
def _create_namespaces(self):
|
||||
self.db.reset()
|
||||
@ -175,6 +204,16 @@ class TestMetadefsControllers(base.IsolatedUnitTest):
|
||||
[self.db.metadef_resource_type_create(req.context, resource_type)
|
||||
for resource_type in self.resource_types]
|
||||
|
||||
def _create_tags(self):
|
||||
req = unit_test_utils.get_fake_request()
|
||||
self.tags = [
|
||||
(NAMESPACE3, _db_tag_fixture(TAG1)),
|
||||
(NAMESPACE3, _db_tag_fixture(TAG2)),
|
||||
(NAMESPACE1, _db_tag_fixture(TAG1)),
|
||||
]
|
||||
[self.db.metadef_tag_create(req.context, namespace, tag)
|
||||
for namespace, tag in self.tags]
|
||||
|
||||
def _create_namespaces_resource_types(self):
|
||||
req = unit_test_utils.get_fake_request(is_admin=True)
|
||||
self.ns_resource_types = [
|
||||
@ -1141,3 +1180,242 @@ class TestMetadefsControllers(base.IsolatedUnitTest):
|
||||
actual = set([x.name for x in output.resource_type_associations])
|
||||
expected = set([RESOURCE_TYPE1, RESOURCE_TYPE2])
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_tag_index(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
output = self.tag_controller.index(request, NAMESPACE3)
|
||||
output = output.to_dict()
|
||||
self.assertEqual(2, len(output['tags']))
|
||||
actual = set([tag.name for tag in output['tags']])
|
||||
expected = set([TAG1, TAG2])
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_tag_index_empty(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
output = self.tag_controller.index(request, NAMESPACE5)
|
||||
output = output.to_dict()
|
||||
self.assertEqual(0, len(output['tags']))
|
||||
|
||||
def test_tag_index_non_existing_namespace(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.tag_controller.index,
|
||||
request, NAMESPACE4)
|
||||
|
||||
def test_tag_show(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
output = self.tag_controller.show(request, NAMESPACE3, TAG1)
|
||||
self.assertEqual(TAG1, output.name)
|
||||
|
||||
def test_tag_show_non_existing(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.tag_controller.show,
|
||||
request, NAMESPACE5, TAG1)
|
||||
|
||||
def test_tag_show_non_visible(self):
|
||||
request = unit_test_utils.get_fake_request(tenant=TENANT2)
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.tag_controller.show,
|
||||
request, NAMESPACE1, TAG1)
|
||||
|
||||
def test_tag_show_non_visible_admin(self):
|
||||
request = unit_test_utils.get_fake_request(tenant=TENANT2,
|
||||
is_admin=True)
|
||||
|
||||
output = self.tag_controller.show(request, NAMESPACE1, TAG1)
|
||||
self.assertEqual(TAG1, output.name)
|
||||
|
||||
def test_tag_delete(self):
|
||||
request = unit_test_utils.get_fake_request(tenant=TENANT3)
|
||||
self.tag_controller.delete(request, NAMESPACE3, TAG1)
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.tag_controller.show,
|
||||
request, NAMESPACE3, TAG1)
|
||||
|
||||
def test_tag_delete_other_owner(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
self.assertRaises(webob.exc.HTTPForbidden,
|
||||
self.tag_controller.delete, request, NAMESPACE3,
|
||||
TAG1)
|
||||
|
||||
def test_tag_delete_other_owner_admin(self):
|
||||
request = unit_test_utils.get_fake_request(is_admin=True)
|
||||
self.tag_controller.delete(request, NAMESPACE3, TAG1)
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.tag_controller.show,
|
||||
request, NAMESPACE3, TAG1)
|
||||
|
||||
def test_tag_delete_non_existing(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.tag_controller.delete, request, NAMESPACE5,
|
||||
TAG1)
|
||||
|
||||
def test_tag_delete_non_existing_namespace(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.tag_controller.delete, request, NAMESPACE4,
|
||||
TAG1)
|
||||
|
||||
def test_tag_delete_non_visible(self):
|
||||
request = unit_test_utils.get_fake_request(tenant=TENANT2)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.tag_controller.delete, request, NAMESPACE1,
|
||||
TAG1)
|
||||
|
||||
def test_tag_delete_admin_protected(self):
|
||||
request = unit_test_utils.get_fake_request(is_admin=True)
|
||||
self.assertRaises(webob.exc.HTTPForbidden,
|
||||
self.tag_controller.delete, request, NAMESPACE1,
|
||||
TAG1)
|
||||
|
||||
def test_tag_create(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
|
||||
tag = glance.api.v2.model.metadef_tag.MetadefTag()
|
||||
tag.name = TAG2
|
||||
tag = self.tag_controller.create(request, tag, NAMESPACE1)
|
||||
self.assertEqual(TAG2, tag.name)
|
||||
|
||||
tag = self.tag_controller.show(request, NAMESPACE1, TAG2)
|
||||
self.assertEqual(TAG2, tag.name)
|
||||
|
||||
def test_tag_create_tags(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
|
||||
metadef_tags = glance.api.v2.model.metadef_tag.MetadefTags()
|
||||
metadef_tags.tags = _db_tags_fixture()
|
||||
output = self.tag_controller.create_tags(
|
||||
request, metadef_tags, NAMESPACE1)
|
||||
output = output.to_dict()
|
||||
self.assertEqual(3, len(output['tags']))
|
||||
actual = set([tag.name for tag in output['tags']])
|
||||
expected = set([TAG1, TAG2, TAG3])
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_tag_create_duplicate_tags(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
|
||||
metadef_tags = glance.api.v2.model.metadef_tag.MetadefTags()
|
||||
metadef_tags.tags = _db_tags_fixture([TAG4, TAG5, TAG4])
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPConflict,
|
||||
self.tag_controller.create_tags,
|
||||
request, metadef_tags, NAMESPACE1)
|
||||
|
||||
def test_tag_create_duplicate_with_pre_existing_tags(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
|
||||
metadef_tags = glance.api.v2.model.metadef_tag.MetadefTags()
|
||||
metadef_tags.tags = _db_tags_fixture([TAG1, TAG2, TAG3])
|
||||
output = self.tag_controller.create_tags(
|
||||
request, metadef_tags, NAMESPACE1)
|
||||
output = output.to_dict()
|
||||
self.assertEqual(3, len(output['tags']))
|
||||
actual = set([tag.name for tag in output['tags']])
|
||||
expected = set([TAG1, TAG2, TAG3])
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
metadef_tags = glance.api.v2.model.metadef_tag.MetadefTags()
|
||||
metadef_tags.tags = _db_tags_fixture([TAG4, TAG5, TAG4])
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPConflict,
|
||||
self.tag_controller.create_tags,
|
||||
request, metadef_tags, NAMESPACE1)
|
||||
|
||||
output = self.tag_controller.index(request, NAMESPACE1)
|
||||
output = output.to_dict()
|
||||
self.assertEqual(3, len(output['tags']))
|
||||
actual = set([tag.name for tag in output['tags']])
|
||||
expected = set([TAG1, TAG2, TAG3])
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_tag_create_conflict(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
|
||||
tag = glance.api.v2.model.metadef_tag.MetadefTag()
|
||||
tag.name = TAG1
|
||||
|
||||
self.assertRaises(webob.exc.HTTPConflict,
|
||||
self.tag_controller.create, request, tag,
|
||||
NAMESPACE1)
|
||||
|
||||
def test_tag_create_non_existing_namespace(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
|
||||
tag = glance.api.v2.model.metadef_tag.MetadefTag()
|
||||
tag.name = TAG1
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.tag_controller.create, request, tag,
|
||||
NAMESPACE4)
|
||||
|
||||
def test_tag_create_non_visible_namespace(self):
|
||||
request = unit_test_utils.get_fake_request(tenant=TENANT2)
|
||||
|
||||
tag = glance.api.v2.model.metadef_tag.MetadefTag()
|
||||
tag.name = TAG1
|
||||
|
||||
self.assertRaises(webob.exc.HTTPForbidden,
|
||||
self.tag_controller.create, request, tag,
|
||||
NAMESPACE1)
|
||||
|
||||
def test_tag_create_non_visible_namespace_admin(self):
|
||||
request = unit_test_utils.get_fake_request(tenant=TENANT2,
|
||||
is_admin=True)
|
||||
|
||||
tag = glance.api.v2.model.metadef_tag.MetadefTag()
|
||||
tag.name = TAG2
|
||||
|
||||
tag = self.tag_controller.create(request, tag, NAMESPACE1)
|
||||
self.assertEqual(TAG2, tag.name)
|
||||
|
||||
tag = self.tag_controller.show(request, NAMESPACE1, TAG2)
|
||||
self.assertEqual(TAG2, tag.name)
|
||||
|
||||
def test_tag_update(self):
|
||||
request = unit_test_utils.get_fake_request(tenant=TENANT3)
|
||||
|
||||
tag = self.tag_controller.show(request, NAMESPACE3, TAG1)
|
||||
tag.name = TAG3
|
||||
tag = self.tag_controller.update(request, tag, NAMESPACE3, TAG1)
|
||||
self.assertEqual(TAG3, tag.name)
|
||||
|
||||
property = self.tag_controller.show(request, NAMESPACE3, TAG3)
|
||||
self.assertEqual(TAG3, property.name)
|
||||
|
||||
def test_tag_update_name(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
|
||||
tag = self.tag_controller.show(request, NAMESPACE1, TAG1)
|
||||
tag.name = TAG2
|
||||
tag = self.tag_controller.update(request, tag, NAMESPACE1, TAG1)
|
||||
self.assertEqual(TAG2, tag.name)
|
||||
|
||||
tag = self.tag_controller.show(request, NAMESPACE1, TAG2)
|
||||
self.assertEqual(TAG2, tag.name)
|
||||
|
||||
def test_tag_update_conflict(self):
|
||||
request = unit_test_utils.get_fake_request(tenant=TENANT3)
|
||||
|
||||
tag = self.tag_controller.show(request, NAMESPACE3, TAG1)
|
||||
tag.name = TAG2
|
||||
self.assertRaises(webob.exc.HTTPConflict,
|
||||
self.tag_controller.update, request, tag,
|
||||
NAMESPACE3, TAG1)
|
||||
|
||||
def test_tag_update_non_existing(self):
|
||||
request = unit_test_utils.get_fake_request(tenant=TENANT3)
|
||||
|
||||
tag = glance.api.v2.model.metadef_tag.MetadefTag()
|
||||
tag.name = TAG1
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.tag_controller.update, request, tag,
|
||||
NAMESPACE5, TAG1)
|
||||
|
||||
def test_tag_update_namespace_non_existing(self):
|
||||
request = unit_test_utils.get_fake_request(tenant=TENANT3)
|
||||
|
||||
tag = glance.api.v2.model.metadef_tag.MetadefTag()
|
||||
tag.name = TAG1
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.tag_controller.update, request, tag,
|
||||
NAMESPACE4, TAG1)
|
||||
|
Loading…
Reference in New Issue
Block a user