Files
glance/glance/api/v2/metadef_objects.py
T
Dinesh Bhor 2562da28c6 Fix 500 if user passes name with more than 80 characters
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
2017-10-05 10:24:22 +05:30

368 lines
14 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_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 webob.exc
from wsme.rest import json
from glance.api import policy
from glance.api.v2 import metadef_namespaces as namespaces
from glance.api.v2.model.metadef_object import MetadefObject
from glance.api.v2.model.metadef_object import MetadefObjects
from glance.common import exception
from glance.common import wsgi
from glance.common import wsme_utils
import glance.db
from glance.i18n import _
import glance.notifier
import glance.schema
LOG = logging.getLogger(__name__)
class MetadefObjectsController(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.obj_schema_link = '/v2/schemas/metadefs/object'
def create(self, req, metadata_object, namespace):
object_factory = self.gateway.get_metadef_object_factory(req.context)
object_repo = self.gateway.get_metadef_object_repo(req.context)
try:
new_meta_object = object_factory.new_object(
namespace=namespace,
**metadata_object.to_dict())
object_repo.add(new_meta_object)
except exception.Forbidden as e:
LOG.debug("User not permitted to create metadata object within "
"'%s' namespace", namespace)
raise webob.exc.HTTPForbidden(explanation=e.msg)
except exception.Invalid as e:
msg = (_("Couldn't create metadata object: %s")
% encodeutils.exception_to_unicode(e))
raise webob.exc.HTTPBadRequest(explanation=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 MetadefObject.to_wsme_model(
new_meta_object,
get_object_href(namespace, new_meta_object),
self.obj_schema_link)
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
object_repo = self.gateway.get_metadef_object_repo(req.context)
db_metaobject_list = object_repo.list(
marker=marker, limit=limit, sort_key=sort_key,
sort_dir=sort_dir, filters=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]
metadef_objects = MetadefObjects()
metadef_objects.objects = object_list
except exception.Forbidden as e:
LOG.debug("User not permitted to retrieve 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()
return metadef_objects
def show(self, req, namespace, object_name):
meta_object_repo = self.gateway.get_metadef_object_repo(
req.context)
try:
metadef_object = meta_object_repo.get(namespace, object_name)
return MetadefObject.to_wsme_model(
metadef_object,
get_object_href(namespace, metadef_object),
self.obj_schema_link)
except exception.Forbidden as e:
LOG.debug("User not permitted to show metadata object '%s' "
"within '%s' namespace", namespace, object_name)
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 update(self, req, metadata_object, namespace, object_name):
meta_repo = self.gateway.get_metadef_object_repo(req.context)
try:
metadef_object = meta_repo.get(namespace, object_name)
metadef_object._old_name = metadef_object.name
metadef_object.name = wsme_utils._get_value(
metadata_object.name)
metadef_object.description = wsme_utils._get_value(
metadata_object.description)
metadef_object.required = wsme_utils._get_value(
metadata_object.required)
metadef_object.properties = wsme_utils._get_value(
metadata_object.properties)
updated_metadata_obj = meta_repo.save(metadef_object)
except exception.Invalid as e:
msg = (_("Couldn't update metadata object: %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 object '%s' "
"within '%s' namespace ", object_name, 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 MetadefObject.to_wsme_model(
updated_metadata_obj,
get_object_href(namespace, updated_metadata_obj),
self.obj_schema_link)
def delete(self, req, namespace, object_name):
meta_repo = self.gateway.get_metadef_object_repo(req.context)
try:
metadef_object = meta_repo.get(namespace, object_name)
metadef_object.delete()
meta_repo.remove(metadef_object)
except exception.Forbidden as e:
LOG.debug("User not permitted to delete metadata object '%s' "
"within '%s' namespace", object_name, 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 _get_base_definitions():
return namespaces.get_schema_definitions()
def _get_base_properties():
return {
"name": {
"type": "string",
"maxLength": 80
},
"description": {
"type": "string"
},
"required": {
"$ref": "#/definitions/stringArray"
},
"properties": {
"$ref": "#/definitions/property"
},
"schema": {
'readOnly': True,
"type": "string"
},
"self": {
'readOnly': True,
"type": "string"
},
"created_at": {
"type": "string",
"readOnly": True,
"description": _("Date and time of object creation"),
"format": "date-time"
},
"updated_at": {
"type": "string",
"readOnly": True,
"description": _("Date and time of the last object modification"),
"format": "date-time"
}
}
def get_schema():
definitions = _get_base_definitions()
properties = _get_base_properties()
mandatory_attrs = MetadefObject.get_mandatory_attrs()
schema = glance.schema.Schema(
'object',
properties,
required=mandatory_attrs,
definitions=definitions,
)
return schema
def get_collection_schema():
object_schema = get_schema()
return glance.schema.CollectionSchema('objects', object_schema)
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']
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)
metadata_object = json.fromjson(MetadefObject, body)
return dict(metadata_object=metadata_object)
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)
metadata_object = json.fromjson(MetadefObject, body)
return dict(metadata_object=metadata_object)
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 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', '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
@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_object):
response.status_int = http.CREATED
self.show(response, metadata_object)
def show(self, response, metadata_object):
metadata_object_json = json.tojson(MetadefObject, metadata_object)
body = jsonutils.dumps(metadata_object_json, ensure_ascii=False)
response.unicode_body = six.text_type(body)
response.content_type = 'application/json'
def update(self, response, metadata_object):
response.status_int = http.OK
self.show(response, metadata_object)
def index(self, response, result):
result.schema = "v2/schemas/metadefs/objects"
metadata_objects_json = json.tojson(MetadefObjects, result)
body = jsonutils.dumps(metadata_objects_json, ensure_ascii=False)
response.unicode_body = six.text_type(body)
response.content_type = 'application/json'
def delete(self, response, result):
response.status_int = http.NO_CONTENT
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 create_resource():
"""Metadef objects resource factory method"""
schema = get_schema()
deserializer = RequestDeserializer(schema)
serializer = ResponseSerializer(schema)
controller = MetadefObjectsController()
return wsgi.Resource(controller, deserializer, serializer)