acf175d335
Change-Id: I76b2e80c97670b4ae49ba7f8b293519369c4ca9a
231 lines
9.0 KiB
Python
231 lines
9.0 KiB
Python
# Copyright (c) 2016 Mirantis, Inc.
|
|
#
|
|
# 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 six
|
|
|
|
from oslo_versionedobjects import fields
|
|
|
|
from glare.common import exception as exc
|
|
from glare.objects.meta import fields as glare_fields
|
|
from glare.objects.meta import validators as val_lib
|
|
|
|
FILTERS = (
|
|
FILTER_EQ, FILTER_NEQ, FILTER_IN, FILTER_GT, FILTER_GTE, FILTER_LT,
|
|
FILTER_LTE) = ('eq', 'neq', 'in', 'gt', 'gte', 'lt', 'lte')
|
|
|
|
DEFAULT_MAX_BLOB_SIZE = 10485760
|
|
|
|
|
|
class Attribute(object):
|
|
def __init__(self, field_class, mutable=False, required_on_activate=True,
|
|
system=False, validators=None, nullable=True, default=None,
|
|
sortable=False, filter_ops=None, description=""):
|
|
"""Init and validate attribute."""
|
|
if not issubclass(field_class, fields.AutoTypedField):
|
|
raise exc.IncorrectArtifactType(
|
|
"Field class %s must be sub-class of AutoTypedField." %
|
|
field_class)
|
|
|
|
self.validators = validators or []
|
|
for v in self.validators:
|
|
v.check_type_allowed(field_class)
|
|
if isinstance(v, val_lib.MaxStrLen):
|
|
if v.size > 255 and sortable:
|
|
raise exc.IncorrectArtifactType(
|
|
"It's forbidden to make attribute %(attr)s "
|
|
"sortable if string length can be more than 255 "
|
|
"symbols. Maximal allowed length now: %(max)d" %
|
|
{"attr": str(field_class), 'max': v.size})
|
|
|
|
self.field_class = field_class
|
|
self.nullable = nullable
|
|
self.default = default
|
|
self.vo_attrs = ['nullable', 'default']
|
|
|
|
self.mutable = mutable
|
|
self.required_on_activate = required_on_activate
|
|
self.system = system
|
|
self.sortable = sortable
|
|
|
|
try:
|
|
default_ops = self.get_allowed_filter_ops(self.element_type)
|
|
except AttributeError:
|
|
default_ops = self.get_allowed_filter_ops(field_class)
|
|
|
|
if filter_ops is None:
|
|
self.filter_ops = default_ops
|
|
else:
|
|
for op in filter_ops:
|
|
if op not in default_ops:
|
|
raise exc.IncorrectArtifactType(
|
|
"Incorrect filter operator '%s'. "
|
|
"Only %s are allowed" % (op, ', '.join(default_ops)))
|
|
self.filter_ops = filter_ops
|
|
|
|
self.field_attrs = ['mutable', 'required_on_activate', 'system',
|
|
'sortable', 'filter_ops', 'description']
|
|
self.description = description
|
|
|
|
@staticmethod
|
|
def get_allowed_filter_ops(field):
|
|
if field in (fields.StringField, fields.String,
|
|
glare_fields.ArtifactStatusField):
|
|
return [FILTER_EQ, FILTER_NEQ, FILTER_IN]
|
|
elif field in (fields.IntegerField, fields.Integer, fields.FloatField,
|
|
fields.Float, glare_fields.VersionField):
|
|
return FILTERS
|
|
elif field in (fields.FlexibleBooleanField, fields.FlexibleBoolean,
|
|
glare_fields.Link, glare_fields.LinkFieldType):
|
|
return [FILTER_EQ, FILTER_NEQ]
|
|
elif field in (glare_fields.BlobField, glare_fields.BlobFieldType):
|
|
return []
|
|
elif field is fields.DateTimeField:
|
|
return [FILTER_LT, FILTER_GT]
|
|
|
|
def get_default_validators(self):
|
|
default = []
|
|
if issubclass(self.field_class, fields.StringField):
|
|
# check if fields is string
|
|
if not any(isinstance(v, val_lib.MaxStrLen)
|
|
for v in self.validators):
|
|
default.append(val_lib.MaxStrLen(255))
|
|
return default
|
|
|
|
def get_field(self):
|
|
# init the field
|
|
vo_attrs = {attr_name: getattr(self, attr_name)
|
|
for attr_name in self.vo_attrs}
|
|
field = self.field_class(**vo_attrs)
|
|
# setup custom field attrs
|
|
field_attrs = {attr_name: getattr(self, attr_name)
|
|
for attr_name in self.field_attrs}
|
|
for prop, value in six.iteritems(field_attrs):
|
|
setattr(field, prop, value)
|
|
|
|
# apply custom validators
|
|
vals = self.validators
|
|
for def_val in self.get_default_validators():
|
|
for val in self.validators:
|
|
if type(val) is type(def_val):
|
|
break
|
|
else:
|
|
vals.append(def_val)
|
|
|
|
def wrapper(coerce_func):
|
|
def coerce_wrapper(obj, attr, value):
|
|
try:
|
|
val = coerce_func(obj, attr, value)
|
|
if val is not None:
|
|
for check_func in vals:
|
|
check_func(val)
|
|
return val
|
|
except (KeyError, ValueError, TypeError) as e:
|
|
msg = "Type: %s. Field: %s. Exception: %s" % (
|
|
obj.get_type_name(), attr, str(e))
|
|
raise exc.BadRequest(message=msg)
|
|
return coerce_wrapper
|
|
|
|
field.coerce = wrapper(field.coerce)
|
|
field.validators = vals
|
|
return field
|
|
|
|
@classmethod
|
|
def init(cls, *args, **kwargs):
|
|
"""Fabric to build attributes."""
|
|
return cls(*args, **kwargs).get_field()
|
|
|
|
|
|
class CompoundAttribute(Attribute):
|
|
def __init__(self, field_class, element_type, element_validators=None,
|
|
**kwargs):
|
|
if element_type is None:
|
|
raise exc.IncorrectArtifactType("'element_type' must be set for "
|
|
"compound type.")
|
|
self.element_type = element_type
|
|
|
|
super(CompoundAttribute, self).__init__(field_class, **kwargs)
|
|
|
|
self.vo_attrs.append('element_type')
|
|
self.field_attrs.append('element_type')
|
|
|
|
self.element_validators = element_validators or []
|
|
if self.sortable:
|
|
raise exc.IncorrectArtifactType("'sortable' must be False for "
|
|
"compound type.")
|
|
|
|
def get_element_validators(self):
|
|
default_vals = []
|
|
if issubclass(self.element_type, fields.String):
|
|
# check if fields is string
|
|
if not any(isinstance(v, val_lib.MaxStrLen)
|
|
for v in self.element_validators):
|
|
default_vals.append(val_lib.MaxStrLen(255))
|
|
vals = default_vals + self.element_validators
|
|
for v in vals:
|
|
v.check_type_allowed(self.element_type)
|
|
return default_vals + self.element_validators
|
|
|
|
|
|
class ListAttribute(CompoundAttribute):
|
|
def __init__(self, element_type, max_size=255, **kwargs):
|
|
if 'default' not in kwargs:
|
|
kwargs['default'] = []
|
|
if element_type is glare_fields.BlobField:
|
|
raise exc.IncorrectArtifactType("List of blobs is not allowed "
|
|
"to be specified in artifact.")
|
|
super(ListAttribute, self).__init__(glare_fields.List, element_type,
|
|
**kwargs)
|
|
self.validators.append(val_lib.MaxListSize(max_size))
|
|
|
|
def get_default_validators(self):
|
|
default_vals = []
|
|
elem_val = val_lib.ListElementValidator(
|
|
super(ListAttribute, self).get_element_validators())
|
|
default_vals.append(elem_val)
|
|
return default_vals
|
|
|
|
|
|
class DictAttribute(CompoundAttribute):
|
|
def __init__(self, element_type, max_size=255, **kwargs):
|
|
if 'default' not in kwargs:
|
|
kwargs['default'] = {}
|
|
super(DictAttribute, self).__init__(glare_fields.Dict, element_type,
|
|
**kwargs)
|
|
self.validators.append(val_lib.MaxDictSize(max_size))
|
|
|
|
def get_default_validators(self):
|
|
default_vals = []
|
|
elem_val = val_lib.DictElementValidator(
|
|
super(DictAttribute, self).get_element_validators())
|
|
default_vals.append(elem_val)
|
|
default_vals.append(val_lib.MaxDictKeyLen(255))
|
|
default_vals.append(val_lib.MinDictKeyLen(1))
|
|
return default_vals
|
|
|
|
|
|
class BlobAttribute(Attribute):
|
|
def __init__(self, max_blob_size=DEFAULT_MAX_BLOB_SIZE, **kwargs):
|
|
super(BlobAttribute, self).__init__(
|
|
field_class=glare_fields.BlobField, **kwargs)
|
|
self.max_blob_size = int(max_blob_size)
|
|
self.field_attrs.append('max_blob_size')
|
|
|
|
|
|
class BlobDictAttribute(DictAttribute):
|
|
def __init__(self, max_blob_size=DEFAULT_MAX_BLOB_SIZE, **kwargs):
|
|
super(BlobDictAttribute, self).__init__(
|
|
element_type=glare_fields.BlobFieldType, **kwargs)
|
|
self.max_blob_size = int(max_blob_size)
|
|
self.field_attrs.append('max_blob_size')
|