# coding: utf-8 # # Copyright 2013 Red Hat, Inc. # 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. from oslo_utils import strutils import wsme from wsme import types as wtypes from magnum.common import exception from magnum.common import utils from magnum.i18n import _ class MacAddressType(wtypes.UserType): """A simple MAC address type.""" basetype = wtypes.text name = 'macaddress' # FIXME(lucasagomes): When used with wsexpose decorator WSME will try # to get the name of the type by accessing it's __name__ attribute. # Remove this __name__ attribute once it's fixed in WSME. # https://bugs.launchpad.net/wsme/+bug/1265590 __name__ = name @staticmethod def validate(value): return utils.validate_and_normalize_mac(value) @staticmethod def frombasetype(value): if value is None: return None return MacAddressType.validate(value) class NameType(wtypes.UserType): """A logical name type.""" basetype = wtypes.text name = 'name' # FIXME(lucasagomes): When used with wsexpose decorator WSME will try # to get the name of the type by accessing it's __name__ attribute. # Remove this __name__ attribute once it's fixed in WSME. # https://bugs.launchpad.net/wsme/+bug/1265590 __name__ = name @staticmethod def validate(value): if not utils.is_name_safe(value): raise exception.InvalidName(name=value) return value @staticmethod def frombasetype(value): if value is None: return None return NameType.validate(value) class UuidType(wtypes.UserType): """A simple UUID type.""" basetype = wtypes.text name = 'uuid' # FIXME(lucasagomes): When used with wsexpose decorator WSME will try # to get the name of the type by accessing it's __name__ attribute. # Remove this __name__ attribute once it's fixed in WSME. # https://bugs.launchpad.net/wsme/+bug/1265590 __name__ = name @staticmethod def validate(value): if not utils.is_uuid_like(value): raise exception.InvalidUUID(uuid=value) return value @staticmethod def frombasetype(value): if value is None: return None return UuidType.validate(value) class BooleanType(wtypes.UserType): """A simple boolean type.""" basetype = wtypes.text name = 'boolean' # FIXME(lucasagomes): When used with wsexpose decorator WSME will try # to get the name of the type by accessing it's __name__ attribute. # Remove this __name__ attribute once it's fixed in WSME. # https://bugs.launchpad.net/wsme/+bug/1265590 __name__ = name @staticmethod def validate(value): try: return strutils.bool_from_string(value, strict=True) except ValueError as e: # raise Invalid to return 400 (BadRequest) in the API raise exception.Invalid(e) @staticmethod def frombasetype(value): if value is None: return None return BooleanType.validate(value) class MultiType(wtypes.UserType): """A complex type that represents one or more types. Used for validating that a value is an instance of one of the types. :param types: Variable-length list of types. """ basetype = wtypes.text def __init__(self, *types): self.types = types def __str__(self): return ' | '.join(map(str, self.types)) def validate(self, value): for t in self.types: try: return wtypes.validate_value(t, value) except (exception.InvalidUUID, ValueError): pass else: raise ValueError( _("Wrong type. Expected '%(type)s', got '%(value)s'") % {'type': self.types, 'value': type(value)}) macaddress = MacAddressType() uuid = UuidType() name = NameType() uuid_or_name = MultiType(UuidType, NameType) boolean = BooleanType() class JsonPatchType(wtypes.Base): """A complex type that represents a single json-patch operation.""" path = wtypes.wsattr(wtypes.StringType(pattern='^(/[\w-]+)+$'), mandatory=True) op = wtypes.wsattr(wtypes.Enum(str, 'add', 'replace', 'remove'), mandatory=True) value = MultiType(wtypes.text, int) @staticmethod def internal_attrs(): """Returns a list of internal attributes. Internal attributes can't be added, replaced or removed. This method may be overwritten by derived class. """ return ['/created_at', '/id', '/links', '/updated_at', '/uuid'] @staticmethod def mandatory_attrs(): """Retruns a list of mandatory attributes. Mandatory attributes can't be removed from the document. This method should be overwritten by derived class. """ return [] @staticmethod def validate(patch): if patch.path in patch.internal_attrs(): msg = _("'%s' is an internal attribute and can not be updated") raise wsme.exc.ClientSideError(msg % patch.path) if patch.path in patch.mandatory_attrs() and patch.op == 'remove': msg = _("'%s' is a mandatory attribute and can not be removed") raise wsme.exc.ClientSideError(msg % patch.path) if patch.op != 'remove': if not patch.value: msg = _("'add' and 'replace' operations needs value") raise wsme.exc.ClientSideError(msg) ret = {'path': patch.path, 'op': patch.op} if patch.value: ret['value'] = patch.value return ret