def from_legacy(cls, schema_dict): """Return a Property Schema object from a legacy schema dictionary.""" # Check for fully-fledged Schema objects if isinstance(schema_dict, cls): return schema_dict unknown = [k for k in schema_dict if k not in SCHEMA_KEYS] if unknown: raise exception.InvalidSchemaError(message=_('Unknown key(s) %s') % unknown) def constraints(): def get_num(key): val = schema_dict.get(key) if val is not None: val = Schema.str_to_num(val) return val if MIN_VALUE in schema_dict or MAX_VALUE in schema_dict: yield constr.Range(get_num(MIN_VALUE), get_num(MAX_VALUE)) if MIN_LENGTH in schema_dict or MAX_LENGTH in schema_dict: yield constr.Length(get_num(MIN_LENGTH), get_num(MAX_LENGTH)) if ALLOWED_VALUES in schema_dict: yield constr.AllowedValues(schema_dict[ALLOWED_VALUES]) if ALLOWED_PATTERN in schema_dict: yield constr.AllowedPattern(schema_dict[ALLOWED_PATTERN]) try: data_type = schema_dict[TYPE] except KeyError: raise exception.InvalidSchemaError(message=_('No %s specified') % TYPE) if SCHEMA in schema_dict: if data_type == Schema.LIST: ss = cls.from_legacy(schema_dict[SCHEMA]) elif data_type == Schema.MAP: schema_dicts = schema_dict[SCHEMA].items() ss = dict((n, cls.from_legacy(sd)) for n, sd in schema_dicts) else: raise exception.InvalidSchemaError( message=_('%(schema)s supplied for %(type)s %(data)s') % dict(schema=SCHEMA, type=TYPE, data=data_type)) else: ss = None return cls(data_type, description=schema_dict.get(DESCRIPTION), default=schema_dict.get(DEFAULT), schema=ss, required=schema_dict.get(REQUIRED, False), constraints=list(constraints()), implemented=schema_dict.get(IMPLEMENTED, True), update_allowed=schema_dict.get(UPDATE_ALLOWED, False), immutable=schema_dict.get(IMMUTABLE, False))
def _check_dict(schema_dict, allowed_keys, entity): if not isinstance(schema_dict, dict): raise exception.InvalidSchemaError( message=_("Invalid %s, expected a mapping") % entity) for key in schema_dict: if key not in allowed_keys: raise exception.InvalidSchemaError( message=_("Invalid key '%(key)s' for %(entity)s") % { "key": key, "entity": entity })
def __init__(self, min=None, max=None, description=None): if min is max is None: raise exception.InvalidSchemaError( message=_('A length constraint must have a min value and/or ' 'a max value specified.')) super(Length, self).__init__(min, max, description) for param in (min, max): if not isinstance(param, (six.integer_types, type(None))): msg = _('min/max length must be integral') raise exception.InvalidSchemaError(message=msg)
def __init__(self, min=None, max=None, description=None): super(Range, self).__init__(description) self.min = min self.max = max for param in (min, max): if not isinstance(param, (float, six.integer_types, type(None))): raise exception.InvalidSchemaError( message=_('min/max must be numeric')) if min is max is None: raise exception.InvalidSchemaError( message=_('A range constraint must have a min value and/or ' 'a max value specified.'))
def validate(self, validate_value=True, context=None): """Validates the parameter. This method validates if the parameter's schema is valid, and if the default value - if present - or the user-provided value for the parameter comply with the schema. """ err_msg = _("Parameter '%(name)s' is invalid: %(exp)s") try: self.schema.validate(context) if not validate_value: return if self.user_value is not None: self._validate(self.user_value, context) elif self.has_default(): self._validate(self.default(), context) else: raise exception.UserParameterMissing(key=self.name) except exception.StackValidationFailed as ex: msg = err_msg % dict(name=self.name, exp=six.text_type(ex)) raise exception.StackValidationFailed(message=msg) except exception.InvalidSchemaError as ex: msg = err_msg % dict(name=self.name, exp=six.text_type(ex)) raise exception.InvalidSchemaError(message=msg)
def __init__(self, pattern, description=None): super(AllowedPattern, self).__init__(description) if not isinstance(pattern, six.string_types): raise exception.InvalidSchemaError( message=_('AllowedPattern must be a string')) self.pattern = pattern self.match = re.compile(pattern).match
def __init__(self, allowed, description=None): super(AllowedValues, self).__init__(description) if (not isinstance(allowed, collections.Sequence) or isinstance(allowed, six.string_types)): raise exception.InvalidSchemaError( message=_('AllowedValues must be a list')) self.allowed = tuple(allowed)
def _validate_dict(cls, param_name, schema_dict): cls._check_dict(schema_dict, cls.PARAMETER_KEYS, "parameter (%s)" % param_name) if cls.TYPE not in schema_dict: raise exception.InvalidSchemaError( message=_("Missing parameter type for parameter: %s") % param_name)
def validate_with_client(self, client, resource_id): if self.resource_client_name and self.resource_getter_name: getattr(client.client_plugin(self.resource_client_name), self.resource_getter_name)(resource_id) else: raise exception.InvalidSchemaError( message=_('Client name and resource getter name must be ' 'specified.'))
def _validate_default(self, context): if self.default is not None: try: self.validate_constraints(self.default, context, [CustomConstraint]) except (ValueError, TypeError) as exc: raise exception.InvalidSchemaError( message=_('Invalid default %(default)s (%(exc)s)') % dict(default=self.default, exc=exc))
def __init__(self, data_type, description=None, default=None, schema=None, required=False, constraints=None, label=None, immutable=False): self._len = None self.label = label self.type = data_type if self.type not in self.TYPES: raise exception.InvalidSchemaError(message=_('Invalid type (%s)') % self.type) if required and default is not None: LOG.warning( _LW("Option 'required=True' should not be used with " "any 'default' value (%s)") % default) self.description = description self.required = required self.immutable = immutable if isinstance(schema, type(self)): if self.type != self.LIST: msg = _('Single schema valid only for ' '%(ltype)s, not %(utype)s') % dict(ltype=self.LIST, utype=self.type) raise exception.InvalidSchemaError(message=msg) self.schema = AnyIndexDict(schema) else: self.schema = schema if self.schema is not None and self.type not in (self.LIST, self.MAP): msg = _('Schema valid only for %(ltype)s or ' '%(mtype)s, not %(utype)s') % dict( ltype=self.LIST, mtype=self.MAP, utype=self.type) raise exception.InvalidSchemaError(message=msg) self.constraints = constraints or [] self.default = default
def _validate_default(self, context): if self.default is not None: default_value = self.default if self.type == self.LIST and not isinstance(self.default, list): try: default_value = self.default.split(',') except (KeyError, AttributeError) as err: raise exception.InvalidSchemaError( message=_('Default must be a comma-delimited list ' 'string: %s') % err) elif self.type == self.LIST and isinstance(self.default, list): default_value = [(six.text_type(x)) for x in self.default] try: self.validate_constraints(default_value, context, [constr.CustomConstraint]) except (ValueError, TypeError, exception.StackValidationFailed) as exc: raise exception.InvalidSchemaError( message=_('Invalid default %(default)s (%(exc)s)') % dict(default=self.default, exc=exc))
def constraints(): constraints = schema_dict.get(cls.CONSTRAINTS) if constraints is None: return if not isinstance(constraints, list): raise exception.InvalidSchemaError( message=_("Invalid parameter constraints for parameter " "%s, expected a list") % param_name) for constraint in constraints: cls._check_dict(constraint, PARAM_CONSTRAINTS, 'parameter constraints') desc = constraint.get(DESCRIPTION) if RANGE in constraint: cdef = constraint.get(RANGE) cls._check_dict(cdef, RANGE_KEYS, 'range constraint') yield constr.Range(parameters.Schema.get_num(MIN, cdef), parameters.Schema.get_num(MAX, cdef), desc) elif LENGTH in constraint: cdef = constraint.get(LENGTH) cls._check_dict(cdef, RANGE_KEYS, 'length constraint') yield constr.Length(parameters.Schema.get_num(MIN, cdef), parameters.Schema.get_num(MAX, cdef), desc) elif ALLOWED_VALUES in constraint: cdef = constraint.get(ALLOWED_VALUES) yield constr.AllowedValues(cdef, desc) elif ALLOWED_PATTERN in constraint: cdef = constraint.get(ALLOWED_PATTERN) yield constr.AllowedPattern(cdef, desc) elif CUSTOM_CONSTRAINT in constraint: cdef = constraint.get(CUSTOM_CONSTRAINT) yield constr.CustomConstraint(cdef, desc) else: raise exception.InvalidSchemaError( message=_("No constraint expressed"))
def validate(self, with_value=True): try: for key in self.data: if key not in self.props: msg = _("Unknown Property %s") % key raise exception.StackValidationFailed(message=msg) for (key, prop) in self.props.items(): # check that update_allowed and immutable # do not contradict each other if prop.update_allowed() and prop.immutable(): msg = _("Property %(prop)s: %(ua)s and %(im)s " "cannot both be True") % { 'prop': key, 'ua': prop.schema.UPDATE_ALLOWED, 'im': prop.schema.IMMUTABLE } raise exception.InvalidSchemaError(message=msg) if with_value: try: self._get_property_value(key, validate=True) except exception.StackValidationFailed as ex: path = [key] path.extend(ex.path) raise exception.StackValidationFailed( path=path, message=ex.error_message) except ValueError as e: if prop.required() and key not in self.data: path = [] else: path = [key] raise exception.StackValidationFailed( path=path, message=six.text_type(e)) # are there unimplemented Properties if not prop.implemented() and key in self.data: msg = _("Property %s not implemented yet") % key raise exception.StackValidationFailed(message=msg) except exception.StackValidationFailed as ex: # NOTE(prazumovsky): should reraise exception for adding specific # error name and error_prefix to path for correct error message # building. path = self.error_prefix path.extend(ex.path) raise exception.StackValidationFailed(error=ex.error or 'Property error', path=path, message=ex.error_message)
def validate(self, context=None): """Validates the schema. This method checks if the schema itself is valid, and if the default value - if present - complies to the schema's constraints. """ for c in self.constraints: if not self._is_valid_constraint(c): err_msg = _('%(name)s constraint ' 'invalid for %(utype)s') % dict( name=type(c).__name__, utype=self.type) raise exception.InvalidSchemaError(message=err_msg) self._validate_default(context) # validated nested schema(ta) if self.schema: if isinstance(self.schema, AnyIndexDict): self.schema.value.validate(context) else: for nested_schema in six.itervalues(self.schema): nested_schema.validate(context)