Source code for glare.objects.meta.wrappers

# 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.

"""This file contains classes that wrap nat"""

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  # 10 Megabytes
DEFAULT_MAX_FOLDER_SIZE = 2673868800  # 2550 Megabytes


[docs]class Field(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 field. Each artifact field has several common properties: :param required_on_activate: boolean value indicating if the field value should be specified for the artifact before activation (Default:True). :param mutable: boolean value indicating if the field value may be changed after the artifact is activated. (Default: False) :param system: boolean value indicating if the field value cannot be edited by user (Default: False). :param sortable: boolean value indicating if there is a possibility to sort by this fields's values. (Default: False) Only fields of 4 primitive types may be sortable: integer, string, float and boolean. :param default: a default value for the field may be specified (Default: None). :param validators: a list of objects. When user sets a value to the field with additional validators Glare checks them before setting the value and raises ValueError if at least one of the requirements is not satisfied. :param filter_ops: a list of available filter operators for the field. There are seven available operators: 'eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'in'. """ 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 field %(field)s " "sortable if string length can be more than 255 " "symbols. Maximal allowed length now: %(max)d" % {"field": str(field_class), 'max': v.size}) self.field_class = field_class self.nullable = nullable self.default = default self.vo_props = ['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_props = ['mutable', 'required_on_activate', 'system', 'sortable', 'filter_ops', 'description'] self.description = description
[docs] @staticmethod def get_allowed_filter_ops(field): if field in (fields.StringField, fields.String): 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]
[docs] 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) and \ not any(isinstance(v, val_lib.AllowedValues) for v in self.validators): default.append(val_lib.MaxStrLen(255)) return default
[docs] def get_field(self): # init the field vo_props = {prop_name: getattr(self, prop_name) for prop_name in self.vo_props} field = self.field_class(**vo_props) # setup custom field properties field_props = {prop_name: getattr(self, prop_name) for prop_name in self.field_props} for prop, value in field_props.items(): 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, field, value): try: val = coerce_func(obj, field, 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(), field, str(e)) raise exc.BadRequest(message=msg) return coerce_wrapper field.coerce = wrapper(field.coerce) field.validators = vals return field
[docs] @classmethod def init(cls, *args, **kwargs): """Fabric to build fields.""" return cls(*args, **kwargs).get_field()
[docs]class CompoundField(Field): 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(CompoundField, self).__init__(field_class, **kwargs) self.vo_props.append('element_type') self.field_props.append('element_type') self.element_validators = element_validators or [] if self.sortable: raise exc.IncorrectArtifactType("'sortable' must be False for " "compound type.")
[docs] 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
[docs]class ListField(CompoundField): 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(ListField, self).__init__(glare_fields.List, element_type, **kwargs) self.validators.append(val_lib.MaxListSize(max_size))
[docs] def get_default_validators(self): default_vals = [] elem_val = val_lib.ListElementValidator( super(ListField, self).get_element_validators()) default_vals.append(elem_val) return default_vals
[docs]class DictField(CompoundField): def __init__(self, element_type, max_size=255, **kwargs): if 'default' not in kwargs: kwargs['default'] = {} super(DictField, self).__init__(glare_fields.Dict, element_type, **kwargs) self.validators.append(val_lib.MaxDictSize(max_size))
[docs] def get_default_validators(self): default_vals = [] elem_val = val_lib.DictElementValidator( super(DictField, 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
[docs]class BlobField(Field): def __init__(self, max_blob_size=DEFAULT_MAX_BLOB_SIZE, **kwargs): super(BlobField, self).__init__( field_class=glare_fields.BlobField, **kwargs) self.max_blob_size = int(max_blob_size) self.field_props.append('max_blob_size')
[docs]class FolderField(DictField): def __init__(self, max_blob_size=DEFAULT_MAX_BLOB_SIZE, max_folder_size=DEFAULT_MAX_FOLDER_SIZE, **kwargs): super(FolderField, self).__init__( element_type=glare_fields.BlobFieldType, **kwargs) self.max_blob_size = int(max_blob_size) self.max_folder_size = int(max_folder_size) self.field_props.append('max_blob_size') self.field_props.append('max_folder_size')
# Classes below added for backward compatibility. They shouldn't be used Attribute = Field CompoundAttribute = CompoundField ListAttribute = ListField DictAttribute = DictField BlobAttribute = BlobField BlobDictAttribute = FolderField