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):
|
class ConfigInvalid(NimbleException):
|
||||||
_msg_fmt = _("Invalid configuration file. %(error_msg)s")
|
_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."""
|
"""Utilities and helper functions."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from nimble.common import exception
|
||||||
from nimble.common.i18n import _LW
|
from nimble.common.i18n import _LW
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -38,3 +41,34 @@ def safe_rstrip(value, chars=None):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
return value.rstrip(chars) or 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