92b17a0d10
Prevent core image attributes with None values from being returned to v2 API clients. Most attribute schemas are of type 'string' and returning None is invalid. Fixes bug 1034790 Change-Id: I2885cabf14307e46ff711bdabfcd3ff3253e6628
108 lines
3.3 KiB
Python
108 lines
3.3 KiB
Python
# Copyright 2012 OpenStack LLC.
|
|
# All Rights Reserved.
|
|
#
|
|
# 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 jsonschema
|
|
|
|
from glance.common import exception
|
|
|
|
|
|
class Schema(object):
|
|
|
|
def __init__(self, name, properties=None, links=None):
|
|
self.name = name
|
|
if properties is None:
|
|
properties = {}
|
|
self.properties = properties
|
|
self.links = links
|
|
|
|
def validate(self, obj):
|
|
try:
|
|
jsonschema.validate(obj, self.raw())
|
|
except jsonschema.ValidationError as e:
|
|
raise exception.InvalidObject(schema=self.name, reason=str(e))
|
|
|
|
def filter(self, obj):
|
|
filtered = {}
|
|
for key, value in obj.iteritems():
|
|
if self._filter_func(self.properties, key) and value is not None:
|
|
filtered[key] = value
|
|
return filtered
|
|
|
|
@staticmethod
|
|
def _filter_func(properties, key):
|
|
return key in properties
|
|
|
|
def merge_properties(self, properties):
|
|
# Ensure custom props aren't attempting to override base props
|
|
original_keys = set(self.properties.keys())
|
|
new_keys = set(properties.keys())
|
|
intersecting_keys = original_keys.intersection(new_keys)
|
|
conflicting_keys = [k for k in intersecting_keys
|
|
if self.properties[k] != properties[k]]
|
|
if len(conflicting_keys) > 0:
|
|
props = ', '.join(conflicting_keys)
|
|
reason = _("custom properties (%(props)s) conflict "
|
|
"with base properties")
|
|
raise exception.SchemaLoadError(reason=reason % {'props': props})
|
|
|
|
self.properties.update(properties)
|
|
|
|
def raw(self):
|
|
raw = {
|
|
'name': self.name,
|
|
'properties': self.properties,
|
|
'additionalProperties': False,
|
|
}
|
|
if self.links:
|
|
raw['links'] = self.links
|
|
return raw
|
|
|
|
|
|
class PermissiveSchema(Schema):
|
|
@staticmethod
|
|
def _filter_func(properties, key):
|
|
return True
|
|
|
|
def raw(self):
|
|
raw = super(PermissiveSchema, self).raw()
|
|
raw['additionalProperties'] = {'type': 'string'}
|
|
return raw
|
|
|
|
|
|
class CollectionSchema(object):
|
|
|
|
def __init__(self, name, item_schema):
|
|
self.name = name
|
|
self.item_schema = item_schema
|
|
|
|
def raw(self):
|
|
return {
|
|
'name': self.name,
|
|
'properties': {
|
|
self.name: {
|
|
'type': 'array',
|
|
'items': self.item_schema.raw(),
|
|
},
|
|
'first': {'type': 'string'},
|
|
'next': {'type': 'string'},
|
|
'schema': {'type': 'string'},
|
|
},
|
|
'links': [
|
|
{'rel': 'first', 'href': '{first}'},
|
|
{'rel': 'next', 'href': '{next}'},
|
|
{'rel': 'describedby', 'href': '{schema}'},
|
|
],
|
|
}
|