Following apis are returning 500 error if user passes name with more than 80 characters: * md-tag-create * md-tag-update * md-object-create * md-object-update * md-property-create * md-property-update This happens because there is a discrepancy in db column length and glance-api length check in schema validation. In database the 'name' field for these API's is defined as of maximum 80 characters and in schema it is defined as of maximum 255 characters. So if user passes name with more than 80 characters and less than 255 characters database fails to store that value and shouts with following error which leads to 500 error to API user: (pymysql.err.DataError) (1406, u"Data too long for column 'name' at row 1") Fixed this issue by changing the maximum allowed length for 'name' from 255 to 80 in schema validation. APIImpact DocImpact Closes-Bug: #1719252 Change-Id: I4ae67457c3e4f5a6bfc3c1db2d305a9b7587395f
835 lines
32 KiB
Python
835 lines
32 KiB
Python
# 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_log import log as logging
|
|
from oslo_serialization import jsonutils
|
|
from oslo_utils import encodeutils
|
|
import six
|
|
from six.moves import http_client as http
|
|
import six.moves.urllib.parse as urlparse
|
|
import webob.exc
|
|
from wsme.rest import json
|
|
|
|
from glance.api import policy
|
|
from glance.api.v2.model.metadef_namespace import Namespace
|
|
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
|
|
from glance.common import wsme_utils
|
|
import glance.db
|
|
import glance.gateway
|
|
from glance.i18n import _, _LE
|
|
import glance.notifier
|
|
import glance.schema
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
CONF = cfg.CONF
|
|
|
|
|
|
class NamespaceController(object):
|
|
def __init__(self, db_api=None, policy_enforcer=None, notifier=None):
|
|
self.db_api = db_api or glance.db.get_api()
|
|
self.policy = policy_enforcer or policy.Enforcer()
|
|
self.notifier = notifier or glance.notifier.Notifier()
|
|
self.gateway = glance.gateway.Gateway(db_api=self.db_api,
|
|
notifier=self.notifier,
|
|
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):
|
|
try:
|
|
ns_repo = self.gateway.get_metadef_namespace_repo(req.context)
|
|
|
|
# Get namespace id
|
|
if marker:
|
|
namespace_obj = ns_repo.get(marker)
|
|
marker = namespace_obj.namespace_id
|
|
|
|
database_ns_list = ns_repo.list(
|
|
marker=marker, limit=limit, sort_key=sort_key,
|
|
sort_dir=sort_dir, filters=filters)
|
|
for db_namespace in database_ns_list:
|
|
# Get resource type associations
|
|
filters = dict()
|
|
filters['namespace'] = db_namespace.namespace
|
|
rs_repo = (
|
|
self.gateway.get_metadef_resource_type_repo(req.context))
|
|
repo_rs_type_list = rs_repo.list(filters=filters)
|
|
resource_type_list = [ResourceTypeAssociation.to_wsme_model(
|
|
resource_type) for resource_type in repo_rs_type_list]
|
|
if resource_type_list:
|
|
db_namespace.resource_type_associations = (
|
|
resource_type_list)
|
|
|
|
namespace_list = [Namespace.to_wsme_model(
|
|
db_namespace,
|
|
get_namespace_href(db_namespace),
|
|
self.ns_schema_link) for db_namespace in database_ns_list]
|
|
namespaces = Namespaces()
|
|
namespaces.namespaces = namespace_list
|
|
if len(namespace_list) != 0 and len(namespace_list) == limit:
|
|
namespaces.next = namespace_list[-1].namespace
|
|
|
|
except exception.Forbidden as e:
|
|
LOG.debug("User not permitted to retrieve metadata namespaces "
|
|
"index")
|
|
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(encodeutils.exception_to_unicode(e))
|
|
raise webob.exc.HTTPInternalServerError()
|
|
return namespaces
|
|
|
|
@utils.mutating
|
|
def create(self, req, namespace):
|
|
try:
|
|
namespace_created = False
|
|
# Create Namespace
|
|
ns_factory = self.gateway.get_metadef_namespace_factory(
|
|
req.context)
|
|
ns_repo = self.gateway.get_metadef_namespace_repo(req.context)
|
|
new_namespace = ns_factory.new_namespace(**namespace.to_dict())
|
|
ns_repo.add(new_namespace)
|
|
namespace_created = True
|
|
|
|
# Create Resource Types
|
|
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,
|
|
**resource_type.to_dict())
|
|
rs_repo.add(new_resource)
|
|
|
|
# Create Objects
|
|
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
|
|
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(
|
|
namespace=namespace.namespace,
|
|
**self._to_property_dict(name, value)
|
|
))
|
|
prop_repo.add(new_property_type)
|
|
except exception.Invalid as e:
|
|
msg = (_("Couldn't create metadata namespace: %s")
|
|
% encodeutils.exception_to_unicode(e))
|
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
|
except exception.Forbidden as e:
|
|
self._cleanup_namespace(ns_repo, namespace, namespace_created)
|
|
LOG.debug("User not permitted to create metadata namespace")
|
|
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
|
except exception.NotFound as e:
|
|
self._cleanup_namespace(ns_repo, namespace, namespace_created)
|
|
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
|
except exception.Duplicate as e:
|
|
self._cleanup_namespace(ns_repo, namespace, namespace_created)
|
|
raise webob.exc.HTTPConflict(explanation=e.msg)
|
|
except Exception as e:
|
|
LOG.error(encodeutils.exception_to_unicode(e))
|
|
raise webob.exc.HTTPInternalServerError()
|
|
|
|
# Return the user namespace as we don't expose the id to user
|
|
new_namespace.properties = namespace.properties
|
|
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)
|
|
|
|
def _to_property_dict(self, name, value):
|
|
# Convert the model PropertyTypes dict to a JSON string
|
|
db_property_type_dict = dict()
|
|
db_property_type_dict['schema'] = json.tojson(PropertyType, value)
|
|
db_property_type_dict['name'] = name
|
|
return db_property_type_dict
|
|
|
|
def _cleanup_namespace(self, namespace_repo, namespace, namespace_created):
|
|
if namespace_created:
|
|
try:
|
|
namespace_obj = namespace_repo.get(namespace.namespace)
|
|
namespace_obj.delete()
|
|
namespace_repo.remove(namespace_obj)
|
|
LOG.debug("Cleaned up namespace %(namespace)s ",
|
|
{'namespace': namespace.namespace})
|
|
except Exception as e:
|
|
msg = (_LE("Failed to delete namespace %(namespace)s."
|
|
"Exception: %(exception)s"),
|
|
{'namespace': namespace.namespace,
|
|
'exception': encodeutils.exception_to_unicode(e)})
|
|
LOG.error(msg)
|
|
|
|
def show(self, req, namespace, filters=None):
|
|
try:
|
|
# Get namespace
|
|
ns_repo = self.gateway.get_metadef_namespace_repo(req.context)
|
|
namespace_obj = ns_repo.get(namespace)
|
|
namespace_detail = Namespace.to_wsme_model(
|
|
namespace_obj,
|
|
get_namespace_href(namespace_obj),
|
|
self.ns_schema_link)
|
|
ns_filters = dict()
|
|
ns_filters['namespace'] = namespace
|
|
|
|
# Get objects
|
|
object_repo = self.gateway.get_metadef_object_repo(req.context)
|
|
db_metaobject_list = object_repo.list(filters=ns_filters)
|
|
object_list = [MetadefObject.to_wsme_model(
|
|
db_metaobject,
|
|
get_object_href(namespace, db_metaobject),
|
|
self.obj_schema_link) for db_metaobject in db_metaobject_list]
|
|
if object_list:
|
|
namespace_detail.objects = object_list
|
|
|
|
# Get resource type associations
|
|
rs_repo = self.gateway.get_metadef_resource_type_repo(req.context)
|
|
db_resource_type_list = rs_repo.list(filters=ns_filters)
|
|
resource_type_list = [ResourceTypeAssociation.to_wsme_model(
|
|
resource_type) for resource_type in db_resource_type_list]
|
|
if resource_type_list:
|
|
namespace_detail.resource_type_associations = (
|
|
resource_type_list)
|
|
|
|
# Get properties
|
|
prop_repo = self.gateway.get_metadef_property_repo(req.context)
|
|
db_properties = prop_repo.list(filters=ns_filters)
|
|
property_list = Namespace.to_model_properties(db_properties)
|
|
if property_list:
|
|
namespace_detail.properties = property_list
|
|
|
|
if filters and filters['resource_type']:
|
|
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:
|
|
LOG.debug("User not permitted to show metadata namespace "
|
|
"'%s'", namespace)
|
|
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(encodeutils.exception_to_unicode(e))
|
|
raise webob.exc.HTTPInternalServerError()
|
|
return namespace_detail
|
|
|
|
def update(self, req, user_ns, namespace):
|
|
namespace_repo = self.gateway.get_metadef_namespace_repo(req.context)
|
|
try:
|
|
ns_obj = namespace_repo.get(namespace)
|
|
ns_obj._old_namespace = ns_obj.namespace
|
|
ns_obj.namespace = wsme_utils._get_value(user_ns.namespace)
|
|
ns_obj.display_name = wsme_utils._get_value(user_ns.display_name)
|
|
ns_obj.description = wsme_utils._get_value(user_ns.description)
|
|
# Following optional fields will default to same values as in
|
|
# create namespace if not specified
|
|
ns_obj.visibility = (
|
|
wsme_utils._get_value(user_ns.visibility) or 'private')
|
|
ns_obj.protected = (
|
|
wsme_utils._get_value(user_ns.protected) or False)
|
|
ns_obj.owner = (
|
|
wsme_utils._get_value(user_ns.owner) or req.context.owner)
|
|
updated_namespace = namespace_repo.save(ns_obj)
|
|
except exception.Invalid as e:
|
|
msg = (_("Couldn't update metadata namespace: %s")
|
|
% encodeutils.exception_to_unicode(e))
|
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
|
except exception.Forbidden as e:
|
|
LOG.debug("User not permitted to update metadata namespace "
|
|
"'%s'", namespace)
|
|
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(encodeutils.exception_to_unicode(e))
|
|
raise webob.exc.HTTPInternalServerError()
|
|
|
|
return Namespace.to_wsme_model(updated_namespace,
|
|
get_namespace_href(updated_namespace),
|
|
self.ns_schema_link)
|
|
|
|
def delete(self, req, namespace):
|
|
namespace_repo = self.gateway.get_metadef_namespace_repo(req.context)
|
|
try:
|
|
namespace_obj = namespace_repo.get(namespace)
|
|
namespace_obj.delete()
|
|
namespace_repo.remove(namespace_obj)
|
|
except exception.Forbidden as e:
|
|
LOG.debug("User not permitted to delete metadata namespace "
|
|
"'%s'", namespace)
|
|
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(encodeutils.exception_to_unicode(e))
|
|
raise webob.exc.HTTPInternalServerError()
|
|
|
|
def delete_objects(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_objects(namespace_obj)
|
|
except exception.Forbidden as e:
|
|
LOG.debug("User not permitted to delete metadata objects "
|
|
"within '%s' namespace", namespace)
|
|
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(encodeutils.exception_to_unicode(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:
|
|
LOG.debug("User not permitted to delete metadata tags "
|
|
"within '%s' namespace", namespace)
|
|
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(encodeutils.exception_to_unicode(e))
|
|
raise webob.exc.HTTPInternalServerError()
|
|
|
|
def delete_properties(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_properties(namespace_obj)
|
|
except exception.Forbidden as e:
|
|
LOG.debug("User not permitted to delete metadata properties "
|
|
"within '%s' namespace", namespace)
|
|
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(encodeutils.exception_to_unicode(e))
|
|
raise webob.exc.HTTPInternalServerError()
|
|
|
|
def _prefix_property_name(self, namespace_detail, user_resource_type):
|
|
prefix = None
|
|
if user_resource_type and namespace_detail.resource_type_associations:
|
|
for resource_type in namespace_detail.resource_type_associations:
|
|
if resource_type.name == user_resource_type:
|
|
prefix = resource_type.prefix
|
|
break
|
|
|
|
if prefix:
|
|
if namespace_detail.properties:
|
|
new_property_dict = dict()
|
|
for (key, value) in namespace_detail.properties.items():
|
|
new_property_dict[prefix + key] = value
|
|
namespace_detail.properties = new_property_dict
|
|
|
|
if namespace_detail.objects:
|
|
for object in namespace_detail.objects:
|
|
new_object_property_dict = dict()
|
|
for (key, value) in object.properties.items():
|
|
new_object_property_dict[prefix + key] = value
|
|
object.properties = new_object_property_dict
|
|
|
|
if object.required and len(object.required) > 0:
|
|
required = [prefix + name for name in object.required]
|
|
object.required = required
|
|
|
|
return namespace_detail
|
|
|
|
|
|
class RequestDeserializer(wsgi.JSONRequestDeserializer):
|
|
_disallowed_properties = ['self', 'schema', 'created_at', 'updated_at']
|
|
|
|
def __init__(self, schema=None):
|
|
super(RequestDeserializer, self).__init__()
|
|
self.schema = schema or get_schema()
|
|
|
|
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']
|
|
|
|
@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)
|
|
|
|
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')
|
|
|
|
if limit is None:
|
|
limit = CONF.limit_param_default
|
|
limit = min(CONF.api_limit_max, int(limit))
|
|
|
|
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 is not None:
|
|
query_params['marker'] = marker
|
|
|
|
if limit is not None:
|
|
query_params['limit'] = self._validate_limit(limit)
|
|
|
|
return query_params
|
|
|
|
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']:
|
|
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 show(self, request):
|
|
params = request.params.copy()
|
|
query_params = {
|
|
'filters': self._get_filters(params)
|
|
}
|
|
return query_params
|
|
|
|
def create(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)
|
|
namespace = json.fromjson(Namespace, body)
|
|
return dict(namespace=namespace)
|
|
|
|
def 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)
|
|
namespace = json.fromjson(Namespace, body)
|
|
return dict(user_ns=namespace)
|
|
|
|
|
|
class ResponseSerializer(wsgi.JSONResponseSerializer):
|
|
def __init__(self, schema=None):
|
|
super(ResponseSerializer, self).__init__()
|
|
self.schema = schema
|
|
|
|
def create(self, response, namespace):
|
|
ns_json = json.tojson(Namespace, namespace)
|
|
response = self.__render(ns_json, response, http.CREATED)
|
|
response.location = get_namespace_href(namespace)
|
|
|
|
def show(self, response, namespace):
|
|
ns_json = json.tojson(Namespace, namespace)
|
|
response = self.__render(ns_json, response)
|
|
|
|
def index(self, response, result):
|
|
params = dict(response.request.params)
|
|
params.pop('marker', None)
|
|
query = urlparse.urlencode(params)
|
|
result.first = "/v2/metadefs/namespaces"
|
|
result.schema = "/v2/schemas/metadefs/namespaces"
|
|
if query:
|
|
result.first = '%s?%s' % (result.first, query)
|
|
if result.next:
|
|
params['marker'] = result.next
|
|
next_query = urlparse.urlencode(params)
|
|
result.next = '/v2/metadefs/namespaces?%s' % next_query
|
|
|
|
ns_json = json.tojson(Namespaces, result)
|
|
response = self.__render(ns_json, response)
|
|
|
|
def update(self, response, namespace):
|
|
ns_json = json.tojson(Namespace, namespace)
|
|
response = self.__render(ns_json, response, http.OK)
|
|
|
|
def delete(self, response, result):
|
|
response.status_int = http.NO_CONTENT
|
|
|
|
def delete_objects(self, response, result):
|
|
response.status_int = http.NO_CONTENT
|
|
|
|
def delete_properties(self, response, result):
|
|
response.status_int = http.NO_CONTENT
|
|
|
|
def delete_tags(self, response, result):
|
|
response.status_int = http.NO_CONTENT
|
|
|
|
def __render(self, json_data, response, response_status=None):
|
|
body = jsonutils.dumps(json_data, ensure_ascii=False)
|
|
response.unicode_body = six.text_type(body)
|
|
response.content_type = 'application/json'
|
|
if response_status:
|
|
response.status_int = response_status
|
|
return response
|
|
|
|
|
|
def _get_base_definitions():
|
|
return get_schema_definitions()
|
|
|
|
|
|
def get_schema_definitions():
|
|
return {
|
|
"positiveInteger": {
|
|
"type": "integer",
|
|
"minimum": 0
|
|
},
|
|
"positiveIntegerDefault0": {
|
|
"allOf": [
|
|
{"$ref": "#/definitions/positiveInteger"},
|
|
{"default": 0}
|
|
]
|
|
},
|
|
"stringArray": {
|
|
"type": "array",
|
|
"items": {"type": "string"},
|
|
# "minItems": 1,
|
|
"uniqueItems": True
|
|
},
|
|
"property": {
|
|
"type": "object",
|
|
"additionalProperties": {
|
|
"type": "object",
|
|
"required": ["title", "type"],
|
|
"properties": {
|
|
"name": {
|
|
"type": "string",
|
|
"maxLength": 80
|
|
},
|
|
"title": {
|
|
"type": "string"
|
|
},
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"operators": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"type": {
|
|
"type": "string",
|
|
"enum": [
|
|
"array",
|
|
"boolean",
|
|
"integer",
|
|
"number",
|
|
"object",
|
|
"string",
|
|
None
|
|
]
|
|
},
|
|
"required": {
|
|
"$ref": "#/definitions/stringArray"
|
|
},
|
|
"minimum": {
|
|
"type": "number"
|
|
},
|
|
"maximum": {
|
|
"type": "number"
|
|
},
|
|
"maxLength": {
|
|
"$ref": "#/definitions/positiveInteger"
|
|
},
|
|
"minLength": {
|
|
"$ref": "#/definitions/positiveIntegerDefault0"
|
|
},
|
|
"pattern": {
|
|
"type": "string",
|
|
"format": "regex"
|
|
},
|
|
"enum": {
|
|
"type": "array"
|
|
},
|
|
"readonly": {
|
|
"type": "boolean"
|
|
},
|
|
"default": {},
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"enum": [
|
|
"array",
|
|
"boolean",
|
|
"integer",
|
|
"number",
|
|
"object",
|
|
"string",
|
|
None
|
|
]
|
|
},
|
|
"enum": {
|
|
"type": "array"
|
|
}
|
|
}
|
|
},
|
|
"maxItems": {
|
|
"$ref": "#/definitions/positiveInteger"
|
|
},
|
|
"minItems": {
|
|
"$ref": "#/definitions/positiveIntegerDefault0"
|
|
},
|
|
"uniqueItems": {
|
|
"type": "boolean",
|
|
"default": False
|
|
},
|
|
"additionalItems": {
|
|
"type": "boolean"
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
def _get_base_properties():
|
|
return {
|
|
"namespace": {
|
|
"type": "string",
|
|
"description": _("The unique namespace text."),
|
|
"maxLength": 80,
|
|
},
|
|
"display_name": {
|
|
"type": "string",
|
|
"description": _("The user friendly name for the namespace. Used "
|
|
"by UI if available."),
|
|
"maxLength": 80,
|
|
},
|
|
"description": {
|
|
"type": "string",
|
|
"description": _("Provides a user friendly description of the "
|
|
"namespace."),
|
|
"maxLength": 500,
|
|
},
|
|
"visibility": {
|
|
"type": "string",
|
|
"description": _("Scope of namespace accessibility."),
|
|
"enum": ["public", "private"],
|
|
},
|
|
"protected": {
|
|
"type": "boolean",
|
|
"description": _("If true, namespace will not be deletable."),
|
|
},
|
|
"owner": {
|
|
"type": "string",
|
|
"description": _("Owner of the namespace."),
|
|
"maxLength": 255,
|
|
},
|
|
"created_at": {
|
|
"type": "string",
|
|
"readOnly": True,
|
|
"description": _("Date and time of namespace creation"),
|
|
"format": "date-time"
|
|
},
|
|
"updated_at": {
|
|
"type": "string",
|
|
"readOnly": True,
|
|
"description": _("Date and time of the last namespace"
|
|
" modification"),
|
|
"format": "date-time"
|
|
},
|
|
"schema": {
|
|
'readOnly': True,
|
|
"type": "string"
|
|
},
|
|
"self": {
|
|
'readOnly': True,
|
|
"type": "string"
|
|
},
|
|
"resource_type_associations": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"prefix": {
|
|
"type": "string"
|
|
},
|
|
"properties_target": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"properties": {
|
|
"$ref": "#/definitions/property"
|
|
},
|
|
"objects": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"required": {
|
|
"$ref": "#/definitions/stringArray"
|
|
},
|
|
"properties": {
|
|
"$ref": "#/definitions/property"
|
|
},
|
|
}
|
|
}
|
|
},
|
|
"tags": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"name": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
|
|
def get_schema():
|
|
properties = _get_base_properties()
|
|
definitions = _get_base_definitions()
|
|
mandatory_attrs = Namespace.get_mandatory_attrs()
|
|
schema = glance.schema.Schema(
|
|
'namespace',
|
|
properties,
|
|
required=mandatory_attrs,
|
|
definitions=definitions
|
|
)
|
|
return schema
|
|
|
|
|
|
def get_collection_schema():
|
|
namespace_schema = get_schema()
|
|
return glance.schema.CollectionSchema('namespaces', namespace_schema)
|
|
|
|
|
|
def get_namespace_href(namespace):
|
|
base_href = '/v2/metadefs/namespaces/%s' % namespace.namespace
|
|
return base_href
|
|
|
|
|
|
def get_object_href(namespace_name, metadef_object):
|
|
base_href = ('/v2/metadefs/namespaces/%s/objects/%s' %
|
|
(namespace_name, metadef_object.name))
|
|
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()
|
|
deserializer = RequestDeserializer(schema)
|
|
serializer = ResponseSerializer(schema)
|
|
controller = NamespaceController()
|
|
return wsgi.Resource(controller, deserializer, serializer)
|