aboutsummaryrefslogtreecommitdiffstats
path: root/vicn/core/resource.py
diff options
context:
space:
mode:
Diffstat (limited to 'vicn/core/resource.py')
-rw-r--r--vicn/core/resource.py341
1 files changed, 178 insertions, 163 deletions
diff --git a/vicn/core/resource.py b/vicn/core/resource.py
index f92e1255..878a8108 100644
--- a/vicn/core/resource.py
+++ b/vicn/core/resource.py
@@ -32,14 +32,17 @@ from threading import Event as ThreadEvent
# LXD workaround
from pylxd.exceptions import NotFound as LXDAPIException
+from netmodel.model.collection import Collection
+from netmodel.model.key import Key
from netmodel.model.mapper import ObjectSpecification
+from netmodel.model.object import Object
from netmodel.model.type import String, Bool, Integer, Dict
from netmodel.model.type import BaseType, Self
+from netmodel.model.uuid import UUID
from netmodel.util.deprecated import deprecated
from netmodel.util.singleton import Singleton
from vicn.core.attribute import Attribute, Multiplicity, Reference
from vicn.core.attribute import NEVER_SET
-from vicn.core.collection import Collection
from vicn.core.commands import ReturnValue
from vicn.core.event import Event, AttributeChangedEvent
from vicn.core.exception import VICNException, ResourceNotFound
@@ -47,7 +50,7 @@ from vicn.core.exception import VICNWouldBlock
from vicn.core.resource_factory import ResourceFactory
from vicn.core.requirement import Requirement, Property
from vicn.core.scheduling_algebra import SchedulingAlgebra
-from vicn.core.state import ResourceState, UUID
+from vicn.core.state import ResourceState
from vicn.core.state import Operations, InstanceState
from vicn.core.task import run_task, BashTask
@@ -73,29 +76,29 @@ class TopLevelResource: pass
class FactoryResource(TopLevelResource): pass
class CategoryResource(TopLevelResource): pass
-#------------------------------------------------------------------------------
-
-class ResourceMetaclass(ABCMeta):
- def __init__(cls, class_name, parents, attrs):
- """
- Args:
- cls: The class type we're registering.
- class_name: A String containing the class_name.
- parents: The parent class types of 'cls'.
- attrs: The attribute (members) of 'cls'.
- """
- super().__init__(class_name, parents, attrs)
-
- # We use the metaclass to create attributes for instance, even before
- # the Resource Factory is called. They are needed both for initializing
- # attributes and reverse attributes, in whatever order. Only class
- # creation allow us to clear _attributes, otherwise, we will just add
- # those from the parent, siblings, etc...
- cls._sanitize()
+##------------------------------------------------------------------------------
+#
+#class ResourceMetaclass(ABCMeta, ObjectSpecification):
+# def __init__(cls, class_name, parents, attrs):
+# """
+# Args:
+# cls: The class type we're registering.
+# class_name: A String containing the class_name.
+# parents: The parent class types of 'cls'.
+# attrs: The attribute (members) of 'cls'.
+# """
+# super().__init__(class_name, parents, attrs)
+#
+# # We use the metaclass to create attributes for instance, even before
+# # the Resource Factory is called. They are needed both for initializing
+# # attributes and reverse attributes, in whatever order. Only class
+# # creation allow us to clear _attributes, otherwise, we will just add
+# # those from the parent, siblings, etc...
+# cls._sanitize()
#------------------------------------------------------------------------------
-class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
+class BaseResource(Object): #, ABC, metaclass=ResourceMetaclass):
"""Base Resource class
The base Resource class implements all the logic related to resource
@@ -409,6 +412,12 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
if isinstance(value, UUID):
value = self.from_uuid(value)
+ # XXX XXX quick fix
+ from netmodel.model.type import InetAddress
+ if issubclass(attribute.type, InetAddress) and value is not None \
+ and not isinstance(value, InetAddress) and not isinstance(value, Reference):
+ value = attribute.type(value)
+
if set_reverse and attribute.reverse_name:
for base in self.__class__.mro():
if not hasattr(base, '_reverse_attributes'):
@@ -420,7 +429,7 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
value.set(ra.name, self, set_reverse = False)
elif ra.multiplicity == Multiplicity.ManyToOne:
for element in value:
- value.set(ra.name, self, set_reverse = False)
+ element.set(ra.name, self, set_reverse = False)
elif ra.multiplicity == Multiplicity.OneToMany:
if value is not None:
collection = value.get(ra.name)
@@ -445,7 +454,7 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
return value
def set(self, attribute_name, value, current=False, set_reverse=True,
- blocking = True):
+ blocking = None):
value = self._set(attribute_name, value, current=current,
set_reverse=set_reverse)
if self.is_local_attribute(attribute_name) or current:
@@ -455,11 +464,10 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
vars(self)[attribute_name] = value
else:
- fut = self._state.manager.attribute_set(self, attribute_name, value)
- asyncio.ensure_future(fut)
+ self._state.manager.attribute_set(self, attribute_name, value)
async def async_set(self, attribute_name, value, current=False,
- set_reverse=True, blocking=True):
+ set_reverse=True, blocking=None):
"""
Example:
- setting the ip address on a node's interface
@@ -470,8 +478,7 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
"""
value = self._set(attribute_name, value, current=current,
set_reverse=set_reverse)
- await self._state.manager.attribute_set(self, attribute_name, value,
- blocking=blocking)
+ await self._state.manager.attribute_set_async(self, attribute_name, value)
def set_many(self, attribute_dict, current=False):
if not attribute_dict:
@@ -541,101 +548,101 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
except AttributeError:
return None
- @classmethod
- def _sanitize(cls):
- """
- 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.
-
- """
- cls._reverse_attributes = dict()
- cur_reverse_attributes = dict()
- for name, obj in vars(cls).items():
- if not isinstance(obj, ObjectSpecification):
- continue
-
- # XXX it seems obj should always be an attribute, confirm !
- if isinstance(obj, Attribute):
- obj.name = name
-
- # Remember whether a reverse_name is defined before loading
- # inherited properties from parent
- has_reverse = bool(obj.reverse_name)
-
- # Handle overloaded attributes
- # By recursion, it is sufficient to look into the parent
- for base in cls.__bases__:
- if hasattr(base, name):
- parent_attribute = getattr(base, name)
- obj.merge(parent_attribute)
- assert obj.type
-
- # 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 = <class 'vicn.resource.group.Group'>
- # obj = <Attribute resources>
- # obj.type = <class 'vicn.core.Resource'>
- # reverse_attribute = <Attribute groups>
- #
- # Result:
- # 1) Group._reverse_attributes =
- # { <Attribute resources> : [<Attribute groups>, ...], ...}
- # 2) Add attribute <Attribute groups> to class Resource
- # 3) Resource._reverse_attributes =
- # { <Attribute groups> : [<Attribute resources], ...], ...}
- #
- if has_reverse:
- a = {
- 'name' : obj.reverse_name,
- 'description' : obj.reverse_description,
- 'multiplicity' : Multiplicity.reverse(obj.multiplicity),
- 'reverse_name' : obj.name,
- 'reverse_description' : obj.description,
- 'auto' : obj.reverse_auto,
- }
- reverse_attribute = Attribute(cls, **a)
- reverse_attribute.is_aggregate = True
-
- # 1) Store the reverse attributes to be later inserted in the
- # remote class, at the end of the function
- # TODO : clarify the reasons to perform this in two steps
- cur_reverse_attributes[obj.type] = reverse_attribute
-
- # 2)
- if not obj in cls._reverse_attributes:
- cls._reverse_attributes[obj] = list()
- cls._reverse_attributes[obj].append(reverse_attribute)
-
- # 3)
- if not reverse_attribute in obj.type._reverse_attributes:
- obj.type._reverse_attributes[reverse_attribute] = list()
- obj.type._reverse_attributes[reverse_attribute].append(obj)
-
- # Insert newly created reverse attributes in the remote class
- for kls, a in cur_reverse_attributes.items():
- setattr(kls, a.name, a)
+####### @classmethod
+####### def _sanitize(cls):
+####### """
+####### 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.
+#######
+####### """
+####### cls._reverse_attributes = dict()
+####### cur_reverse_attributes = dict()
+####### for name, obj in vars(cls).items():
+####### if not isinstance(obj, ObjectSpecification):
+####### continue
+#######
+####### # XXX it seems obj should always be an attribute, confirm !
+####### if isinstance(obj, Attribute):
+####### obj.name = name
+#######
+####### # Remember whether a reverse_name is defined before loading
+####### # inherited properties from parent
+####### has_reverse = bool(obj.reverse_name)
+#######
+####### # Handle overloaded attributes
+####### # By recursion, it is sufficient to look into the parent
+####### for base in cls.__bases__:
+####### if hasattr(base, name):
+####### parent_attribute = getattr(base, name)
+####### obj.merge(parent_attribute)
+####### assert obj.type
+#######
+####### # 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 = <class 'vicn.resource.group.Group'>
+####### # obj = <Attribute resources>
+####### # obj.type = <class 'vicn.core.Resource'>
+####### # reverse_attribute = <Attribute groups>
+####### #
+####### # Result:
+####### # 1) Group._reverse_attributes =
+####### # { <Attribute resources> : [<Attribute groups>, ...], ...}
+####### # 2) Add attribute <Attribute groups> to class Resource
+####### # 3) Resource._reverse_attributes =
+####### # { <Attribute groups> : [<Attribute resources], ...], ...}
+####### #
+####### if has_reverse:
+####### a = {
+####### 'name' : obj.reverse_name,
+####### 'description' : obj.reverse_description,
+####### 'multiplicity' : Multiplicity.reverse(obj.multiplicity),
+####### 'reverse_name' : obj.name,
+####### 'reverse_description' : obj.description,
+####### 'auto' : obj.reverse_auto,
+####### }
+####### reverse_attribute = Attribute(cls, **a)
+####### reverse_attribute.is_aggregate = True
+#######
+####### # 1) Store the reverse attributes to be later inserted in the
+####### # remote class, at the end of the function
+####### # TODO : clarify the reasons to perform this in two steps
+####### cur_reverse_attributes[obj.type] = reverse_attribute
+#######
+####### # 2)
+####### if not obj in cls._reverse_attributes:
+####### cls._reverse_attributes[obj] = list()
+####### cls._reverse_attributes[obj].append(reverse_attribute)
+#######
+####### # 3)
+####### if not reverse_attribute in obj.type._reverse_attributes:
+####### obj.type._reverse_attributes[reverse_attribute] = list()
+####### obj.type._reverse_attributes[reverse_attribute].append(obj)
+#######
+####### # Insert newly created reverse attributes in the remote class
+####### for kls, a in cur_reverse_attributes.items():
+####### setattr(kls, a.name, a)
@classmethod
def iter_attributes(cls, aggregates = False):
@@ -648,13 +655,54 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
yield attribute
- def iter_keys(self):
- for attribute in self.iter_attributes():
- if attribute.key == True:
- yield attribute
+ @classmethod
+ def iter_keys(cls):
+ for name in dir(cls):
+ key = getattr(cls, name)
+ if not isinstance(key, Key):
+ continue
+ yield key
+
+ def get_attributes(self, aggregates = False):
+ return list(self.iter_attributes(aggregates = aggregates))
+
+ @classmethod
+ def has_attribute(cls, name):
+ return name in [a.name for a in cls.attributes()]
+
+ def get_attribute_names(self, aggregates = False):
+ return set(a.name
+ for a in self.iter_attributes(aggregates = aggregates))
+
+ def get_attribute_dict(self, field_names = None, aggregates = False,
+ uuid = True):
+ assert not field_names or field_names.is_star()
+ attributes = self.get_attributes(aggregates = aggregates)
+
+ ret = dict()
+ for a in attributes:
+ if not a.is_set(self):
+ continue
+ value = getattr(self, a.name)
+ if a.is_collection:
+ ret[a.name] = list()
+ for x in value:
+ if uuid and isinstance(x, Resource):
+ x = x._state.uuid._uuid
+ ret[a.name].append(x)
+ else:
+ if uuid and isinstance(value, Resource):
+ value = value._state.uuid._uuid
+ ret[a.name] = value
+ return ret
+
+ @classmethod
+ def get_keys(cls):
+ return list(cls.iter_keys())
- def get_keys(self):
- return list(self.iter_keys())
+ @classmethod
+ def has_key_attribute(cls, attribute):
+ return any(attribute in key for key in cls.iter_keys())
def auto_instanciate(self, attribute):
if self.managed is False:
@@ -694,35 +742,6 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
self._state.manager.commit_resource(resource)
return resource
- def get_attributes(self, aggregates = False):
- return list(self.iter_attributes(aggregates = aggregates))
-
- def get_attribute_names(self, aggregates = False):
- return set(a.name
- for a in self.iter_attributes(aggregates = aggregates))
-
- def get_attribute_dict(self, field_names = None, aggregates = False,
- uuid = True):
- assert not field_names or field_names.is_star()
- attributes = self.get_attributes(aggregates = aggregates)
-
- ret = dict()
- for a in attributes:
- if not a.is_set(self):
- continue
- value = getattr(self, a.name)
- if a.is_collection:
- ret[a.name] = list()
- for x in value:
- if uuid and isinstance(x, Resource):
- x = x._state.uuid._uuid
- ret[a.name].append(x)
- else:
- if uuid and isinstance(value, Resource):
- value = value._state.uuid._uuid
- ret[a.name] = value
- return ret
-
def get_tuple(self):
return (self.__class__, self._get_attribute_dict())
@@ -877,10 +896,6 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
# Accessors
#---------------------------------------------------------------------------
- @classmethod
- def has_attribute(cls, name):
- return name in [a.name for a in cls.attributes()]
-
def has_callback(self, action, attribute):
return hasattr(self, '_{}_{}'.format(action, attribute.name))