From 3e6678f9c692553e8902da4d6fb1fe6c087db1f4 Mon Sep 17 00:00:00 2001 From: Marcel Enguehard Date: Wed, 19 Jul 2017 11:26:26 +0200 Subject: * GUI resource * MemIf interface for VPP * Better netmodel integration * Draft documentation * New tutorials * Improved monitoring and error handling * Refactored IP addresses and prefixes representation * Improved image mgmt for LXD * Various bugfixes and code refactoring Change-Id: I90da6cf7b5716bc7deb6bf4e24d3f9f01b5a9b0f Signed-off-by: Marcel Enguehard --- netmodel/model/object.py | 126 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 101 insertions(+), 25 deletions(-) (limited to 'netmodel/model/object.py') diff --git a/netmodel/model/object.py b/netmodel/model/object.py index 32d3a833..99dbe0c2 100644 --- a/netmodel/model/object.py +++ b/netmodel/model/object.py @@ -18,7 +18,10 @@ from abc import ABCMeta -from netmodel.model.attribute import Attribute +import sys + +from netmodel.model.attribute import Attribute, Multiplicity +from netmodel.model.key import Key from netmodel.model.type import BaseType from netmodel.model.mapper import ObjectSpecification @@ -26,11 +29,21 @@ from netmodel.model.mapper import ObjectSpecification E_UNK_RES_NAME = 'Unknown resource name for attribute {} in {} ({}) : {}' -class ObjectMetaclass(ABCMeta): +class ObjectMetaclass(type): """ Object metaclass allowing non-uniform attribute declaration. """ + def __new__(mcls, name, bases, attrs): + cls = super(ObjectMetaclass, mcls).__new__(mcls, name, bases, attrs) + if (sys.version_info < (3, 6)): + # Before Python 3.6, descriptor protocol does not include __set_name__. + # We use a metaclass to emulate the functionality. + for attr, obj in attrs.items(): + if isinstance(obj, ObjectSpecification): + obj.__set_name__(cls, attr) + return cls + def __init__(cls, class_name, parents, attrs): """ Args: @@ -67,19 +80,19 @@ class Object(BaseType, metaclass = ObjectMetaclass): else: resource = x if not resource: - raise LurchException(E_UNK_RES_NAME.format(key, + raise LurchException(E_UNK_RES_NAME.format(key, self.name, self.__class__.__name__, x)) new_value.append(resource._state.uuid) value = new_value else: if isinstance(value, str): - resource = self._state.manager.by_name(value) + resource = self._state.manager.by_name(value) elif isinstance(value, UUID): - resource = self._state.manager.by_uuid(value) + resource = self._state.manager.by_uuid(value) else: resource = value if not resource: - raise LurchException(E_UNK_RES_NAME.format(key, + raise LurchException(E_UNK_RES_NAME.format(key, self.name, self.__class__.__name__, value)) value = resource._state.uuid setattr(self, key, value) @@ -111,19 +124,20 @@ class Object(BaseType, metaclass = ObjectMetaclass): @classmethod def _sanitize(cls): - """Sanitize the object model to accomodate for multiple declaration - styles + """ + This methods performs sanitization of the object declaration. + + More specifically: + - it goes over all attributes and sets their name based on the python + object attribute name. + - it establishes mutual object relationships through reverse attributes. - In particular, this method: - - set names to all attributes """ cls._reverse_attributes = dict() cur_reverse_attributes = dict() for name, obj in vars(cls).items(): - if not isinstance(obj, ObjectSpecification): + if not isinstance(obj, Attribute): continue - if isinstance(obj, Attribute): - obj.name = name # Remember whether a reverse_name is defined before loading # inherited properties from parent @@ -135,30 +149,72 @@ class Object(BaseType, metaclass = ObjectMetaclass): if hasattr(base, name): parent_attribute = getattr(base, name) obj.merge(parent_attribute) - assert obj.type + assert obj.type, "No type for obj={} cls={}, base={}".format(obj, cls, base) # Handle reverse attribute # # NOTE: we need to do this after merging to be sure we get all # properties inherited from parent (eg. multiplicity) + # + # See "Reverse attributes" section in BaseResource docstring. + # + # Continueing with the same example, let's detail how it is handled: + # + # Original declaration: + # >>> + # class Group(Resource): + # resources = Attribute(Resource, description = 'Resources belonging to the group', + # multiplicity = Multiplicity.ManyToMany, + # default = [], + # reverse_name = 'groups', + # reverse_description = 'Groups to which the resource belongs') + # <<< + # + # Local variables: + # cls = + # obj = + # obj.type = + # reverse_attribute = + # + # Result: + # 1) Group._reverse_attributes = + # { : [, ...], ...} + # 2) Add attribute to class Resource + # 3) Resource._reverse_attributes = + # { : [