Add Nimble base object
Change-Id: I9e9f11c04ce7833adde5bf26203662c2bc45bfdf
This commit is contained in:
parent
4deba007f1
commit
d004abb331
@ -118,3 +118,7 @@ class NotAcceptable(NimbleException):
|
||||
|
||||
class ConfigInvalid(NimbleException):
|
||||
_msg_fmt = _("Invalid configuration file. %(error_msg)s")
|
||||
|
||||
|
||||
class InvalidMAC(Invalid):
|
||||
_msg_fmt = _("Expected a MAC address but received %(mac)s.")
|
||||
|
@ -15,9 +15,12 @@
|
||||
|
||||
"""Utilities and helper functions."""
|
||||
|
||||
import re
|
||||
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from nimble.common import exception
|
||||
from nimble.common.i18n import _LW
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -38,3 +41,34 @@ def safe_rstrip(value, chars=None):
|
||||
return value
|
||||
|
||||
return value.rstrip(chars) or value
|
||||
|
||||
|
||||
def is_valid_mac(address):
|
||||
"""Verify the format of a MAC address.
|
||||
|
||||
Check if a MAC address is valid and contains six octets. Accepts
|
||||
colon-separated format only.
|
||||
|
||||
:param address: MAC address to be validated.
|
||||
:returns: True if valid. False if not.
|
||||
|
||||
"""
|
||||
m = "[0-9a-f]{2}(:[0-9a-f]{2}){5}$"
|
||||
return (isinstance(address, six.string_types) and
|
||||
re.match(m, address.lower()))
|
||||
|
||||
|
||||
def validate_and_normalize_mac(address):
|
||||
"""Validate a MAC address and return normalized form.
|
||||
|
||||
Checks whether the supplied MAC address is formally correct and
|
||||
normalize it to all lower case.
|
||||
|
||||
:param address: MAC address to be validated and normalized.
|
||||
:returns: Normalized and validated MAC address.
|
||||
:raises: InvalidMAC If the MAC address is not valid.
|
||||
|
||||
"""
|
||||
if not is_valid_mac(address):
|
||||
raise exception.InvalidMAC(mac=address)
|
||||
return address.lower()
|
||||
|
0
nimble/objects/__init__.py
Normal file
0
nimble/objects/__init__.py
Normal file
96
nimble/objects/base.py
Normal file
96
nimble/objects/base.py
Normal file
@ -0,0 +1,96 @@
|
||||
# Copyright 2016 Huawei Technologies Co.,LTD.
|
||||
# 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.
|
||||
|
||||
"""Nimble common internal object model"""
|
||||
|
||||
from oslo_utils import versionutils
|
||||
from oslo_versionedobjects import base as object_base
|
||||
|
||||
from nimble import objects
|
||||
from nimble.objects import fields as object_fields
|
||||
|
||||
|
||||
class NimbleObjectRegistry(object_base.VersionedObjectRegistry):
|
||||
def registration_hook(self, cls, index):
|
||||
# NOTE(jroll): blatantly stolen from nova
|
||||
# NOTE(danms): This is called when an object is registered,
|
||||
# and is responsible for maintaining nimble.objects.$OBJECT
|
||||
# as the highest-versioned implementation of a given object.
|
||||
version = versionutils.convert_version_to_tuple(cls.VERSION)
|
||||
if not hasattr(objects, cls.obj_name()):
|
||||
setattr(objects, cls.obj_name(), cls)
|
||||
else:
|
||||
cur_version = versionutils.convert_version_to_tuple(
|
||||
getattr(objects, cls.obj_name()).VERSION)
|
||||
if version >= cur_version:
|
||||
setattr(objects, cls.obj_name(), cls)
|
||||
|
||||
|
||||
class NimbleObject(object_base.VersionedObject):
|
||||
"""Base class and object factory.
|
||||
|
||||
This forms the base of all objects that can be remoted or instantiated
|
||||
via RPC. Simply defining a class that inherits from this base class
|
||||
will make it remotely instantiatable. Objects should implement the
|
||||
necessary "get" classmethod routines as well as "save" object methods
|
||||
as appropriate.
|
||||
"""
|
||||
|
||||
OBJ_SERIAL_NAMESPACE = 'nimble_object'
|
||||
OBJ_PROJECT_NAMESPACE = 'nimble'
|
||||
|
||||
# TODO(lintan) Refactor these fields and create PersistentObject and
|
||||
# TimeStampObject like Nova when it is necessary.
|
||||
fields = {
|
||||
'created_at': object_fields.DateTimeField(nullable=True),
|
||||
'updated_at': object_fields.DateTimeField(nullable=True),
|
||||
}
|
||||
|
||||
def as_dict(self):
|
||||
return dict((k, getattr(self, k))
|
||||
for k in self.fields
|
||||
if hasattr(self, k))
|
||||
|
||||
def obj_refresh(self, loaded_object):
|
||||
"""Applies updates for objects that inherit from base.NimbleObject.
|
||||
|
||||
Checks for updated attributes in an object. Updates are applied from
|
||||
the loaded object column by column in comparison with the current
|
||||
object.
|
||||
"""
|
||||
for field in self.fields:
|
||||
if (self.obj_attr_is_set(field) and
|
||||
self[field] != loaded_object[field]):
|
||||
self[field] = loaded_object[field]
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(obj, db_object):
|
||||
"""Converts a database entity to a formal object.
|
||||
|
||||
:param obj: An object of the class.
|
||||
:param db_object: A DB model of the object
|
||||
:return: The object of the class with the database entity added
|
||||
"""
|
||||
|
||||
for field in obj.fields:
|
||||
obj[field] = db_object[field]
|
||||
|
||||
obj.obj_reset_changes()
|
||||
return obj
|
||||
|
||||
|
||||
class NimbleObjectSerializer(object_base.VersionedObjectSerializer):
|
||||
# Base class to use for object hydration
|
||||
OBJ_BASE_CLASS = NimbleObject
|
108
nimble/objects/fields.py
Normal file
108
nimble/objects/fields.py
Normal file
@ -0,0 +1,108 @@
|
||||
# Copyright 2016 Huawei Technologies Co.,LTD.
|
||||
# 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 ast
|
||||
import hashlib
|
||||
import inspect
|
||||
import six
|
||||
|
||||
from oslo_versionedobjects import fields as object_fields
|
||||
|
||||
from nimble.common import utils
|
||||
|
||||
|
||||
class IntegerField(object_fields.IntegerField):
|
||||
pass
|
||||
|
||||
|
||||
class UUIDField(object_fields.UUIDField):
|
||||
pass
|
||||
|
||||
|
||||
class StringField(object_fields.StringField):
|
||||
pass
|
||||
|
||||
|
||||
class StringAcceptsCallable(object_fields.String):
|
||||
@staticmethod
|
||||
def coerce(obj, attr, value):
|
||||
if callable(value):
|
||||
value = value()
|
||||
return super(StringAcceptsCallable, StringAcceptsCallable).coerce(
|
||||
obj, attr, value)
|
||||
|
||||
|
||||
class StringFieldThatAcceptsCallable(object_fields.StringField):
|
||||
"""Custom StringField object that allows for functions as default
|
||||
|
||||
In some cases we need to allow for dynamic defaults based on configuration
|
||||
options, this StringField object allows for a function to be passed as a
|
||||
default, and will only process it at the point the field is coerced
|
||||
"""
|
||||
|
||||
AUTO_TYPE = StringAcceptsCallable()
|
||||
|
||||
def __repr__(self):
|
||||
default = self._default
|
||||
if (self._default != object_fields.UnspecifiedDefault and
|
||||
callable(self._default)):
|
||||
default = "%s-%s" % (
|
||||
self._default.__name__,
|
||||
hashlib.md5(inspect.getsource(
|
||||
self._default).encode()).hexdigest())
|
||||
return '%s(default=%s,nullable=%s)' % (self._type.__class__.__name__,
|
||||
default, self._nullable)
|
||||
|
||||
|
||||
class DateTimeField(object_fields.DateTimeField):
|
||||
pass
|
||||
|
||||
|
||||
class BooleanField(object_fields.BooleanField):
|
||||
pass
|
||||
|
||||
|
||||
class ListOfStringsField(object_fields.ListOfStringsField):
|
||||
pass
|
||||
|
||||
|
||||
class FlexibleDict(object_fields.FieldType):
|
||||
@staticmethod
|
||||
def coerce(obj, attr, value):
|
||||
if isinstance(value, six.string_types):
|
||||
value = ast.literal_eval(value)
|
||||
return dict(value)
|
||||
|
||||
|
||||
class FlexibleDictField(object_fields.AutoTypedField):
|
||||
AUTO_TYPE = FlexibleDict()
|
||||
|
||||
# TODO(lucasagomes): In our code we've always translated None to {},
|
||||
# this method makes this field to work like this. But probably won't
|
||||
# be accepted as-is in the oslo_versionedobjects library
|
||||
def _null(self, obj, attr):
|
||||
if self.nullable:
|
||||
return {}
|
||||
super(FlexibleDictField, self)._null(obj, attr)
|
||||
|
||||
|
||||
class MACAddress(object_fields.FieldType):
|
||||
@staticmethod
|
||||
def coerce(obj, attr, value):
|
||||
return utils.validate_and_normalize_mac(value)
|
||||
|
||||
|
||||
class MACAddressField(object_fields.AutoTypedField):
|
||||
AUTO_TYPE = MACAddress()
|
Loading…
Reference in New Issue
Block a user