Misc bug fixes 03/6903/1
authorMarcel Enguehard <[email protected]>
Mon, 29 May 2017 11:13:32 +0000 (13:13 +0200)
committerMarcel Enguehard <[email protected]>
Mon, 29 May 2017 11:13:32 +0000 (13:13 +0200)
*IP assignment
*Node is a key attribute
*Up-to-date packages
*Trailing whitespaces
...

Change-Id: Id8e2a5f7b2c4506f326b3c4bc991fa65f53fca5c
Signed-off-by: Marcel Enguehard <[email protected]>
25 files changed:
vicn/core/attribute.py
vicn/core/resource.py
vicn/core/resource_mgr.py
vicn/core/scheduling_algebra.py
vicn/core/state.py
vicn/core/task.py
vicn/resource/application.py
vicn/resource/central.py
vicn/resource/icn/forwarder.py
vicn/resource/icn/icn_application.py
vicn/resource/interface.py
vicn/resource/ip/prefix_tree.py
vicn/resource/ip_assignment.py
vicn/resource/linux/file.py
vicn/resource/linux/keypair.py
vicn/resource/linux/link.py
vicn/resource/linux/net_device.py
vicn/resource/linux/package_manager.py
vicn/resource/linux/physical.py
vicn/resource/lxd/lxc_container.py
vicn/resource/lxd/lxc_image.py
vicn/resource/vpp/cicn.py
vicn/resource/vpp/dpdk_device.py
vicn/resource/vpp/vpp.py
vicn/resource/vpp/vpp_host.py

index 22f4448..3afe0d6 100644 (file)
@@ -267,3 +267,16 @@ class Reference:
     def __init__(self, resource, attribute=None):
         self._resource = resource
         self._attribute = attribute
+
+    def get_proxy(self):
+        if self._resource is Self:
+            resource = getattr(self, self._attribute)
+        else:
+            resource = getattr(self._resource, self._attribute)
+        return resource
+
+    def get(self, attribute_name):
+        return self.get_proxy().get(attribute_name)
+
+    def __iter__(self):
+        return iter(self.get_proxy())
index ab96daa..9044ec2 100644 (file)
@@ -168,12 +168,6 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
                 log.warning(W_UNK_ATTR.format(key, self.get_type()))
                 continue
 
-            if isinstance(value, Reference):
-                if value._resource is Self:
-                    value = getattr(self, value._attribute)
-                else:
-                    value = getattr(value._resource, value._attribute)
-
             if value and issubclass(attribute.type, Resource):
                 if attribute.is_collection:
                     new_value = list()
@@ -215,6 +209,9 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
 
         # Check requirements and default values
         for attr in self.iter_attributes():
+            # XXX fix for lambda attributes, since initialization makes no sense
+            if hasattr(attr, 'func') and attr.func:
+                continue
             if attr.name not in kwargs:
                 default = self.get_default_collection(attr) if attr.is_collection else \
                         self.get_default(attr)
@@ -311,7 +308,7 @@ class BaseResource(BaseType, ABC, metaclass=ResourceMetaclass):
                         try:
                             rv = task.execute_blocking()
                             break
-                        except LxdAPIException:
+                        except LXDAPIException:
                             log.warning("LxdAPIException, retrying to fetch value")
                             continue
                         except Exception as e:
index 4ca8060..e6029cd 100644 (file)
@@ -365,7 +365,6 @@ class ResourceManager(metaclass=Singleton):
         for resource in self.get_resources():
             if resource.get_type() == 'lxccontainer':
                 task = task | resource.__delete__()
-                print("RESOURCE", resource.name)
         self.schedule(task)
         ret = await wait_task(task)
         return ret
@@ -613,18 +612,18 @@ class ResourceManager(metaclass=Singleton):
         """
         self.log(resource, ' - Waiting for attribute dependencies...')
         for attr in resource.iter_attributes():
-            if issubclass(attr.type, Resource):
-                deps = resource.get(attr.name)
-                if deps is None:
-                    # Not really a dependency, we expect mandatory to prevent
-                    # us to continue if we should not
-                    continue
 
-                if not attr.is_collection:
-                    deps = [deps]
+            if not issubclass(attr.type, Resource):
+                continue
+
+            deps = resource.get(attr.name)
+            if not deps:
+                continue
+            if not attr.is_collection:
+                deps = [deps]
 
-                for dep in deps:
-                    # XXX This could be done in parallel
+            for dep in deps:
+                if attr.key:
                     if not dep.managed:
                         continue
                     dep_pfx = '{}:{}'.format(dep.get_type(), dep.get_uuid())
@@ -632,27 +631,27 @@ class ResourceManager(metaclass=Singleton):
                     await wait_resource(dep)
                     self.log(resource, S_WAIT_DEP_OK. format(dep_pfx))
 
-                    if not attr.requirements:
-                        continue
+                if not attr.requirements:
+                    continue
 
-                    for req in attr.requirements:
-                        dep_attr_name = req.requirement_type
-                        dep_attr = dep.get_attribute(dep_attr_name)
-                        assert issubclass(dep_attr.type, Resource)
-                        dep_attr_value = dep.get(dep_attr_name)
-
-                        if not dep_attr_value:
-                            dep_attr_value = dep.auto_instanciate(dep_attr)
-                            setattr(dep, dep_attr_name, dep_attr_value)
-
-                        dep_attr_value_pfx = '{}:{}'.format(
-                                dep_attr_value.get_type(),
-                                dep_attr_value.get_uuid())
-                        self.log(resource,
-                                S_WAIT_DEP.format(dep_attr_value_pfx))
-                        await wait_resource(dep_attr_value)
-                        self.log(resource,
-                                S_WAIT_DEP_OK .format(dep_attr_value_pfx))
+                for req in attr.requirements:
+                    dep_attr_name = req.requirement_type
+                    dep_attr = dep.get_attribute(dep_attr_name)
+                    assert issubclass(dep_attr.type, Resource)
+                    dep_attr_value = dep.get(dep_attr_name)
+
+                    if not dep_attr_value:
+                        dep_attr_value = dep.auto_instanciate(dep_attr)
+                        setattr(dep, dep_attr_name, dep_attr_value)
+
+                    dep_attr_value_pfx = '{}:{}'.format(
+                            dep_attr_value.get_type(),
+                            dep_attr_value.get_uuid())
+                    self.log(resource,
+                            S_WAIT_DEP.format(dep_attr_value_pfx))
+                    await wait_resource(dep_attr_value)
+                    self.log(resource,
+                            S_WAIT_DEP_OK .format(dep_attr_value_pfx))
 
     async def _resource_wait_predecessors(self, resource):
         after = resource.__after__()
@@ -981,7 +980,6 @@ class ResourceManager(metaclass=Singleton):
         ip = resource.node.management_interface.ip4_address
         if not ip:
             log.error('IP of monitored Node {} is None'.format(resource.node))
-            #return # XXX
             import os; os._exit(1)
 
         ws = self._router.add_interface('websocketclient', address=ip,
index 207856c..368ac24 100644 (file)
 # limitations under the License.
 #
 
-def SchedulingAlgebra(cls, concurrent_mixin=object, composition_mixin=object, 
+def SchedulingAlgebra(cls, concurrent_mixin=object, composition_mixin=object,
         sequential_mixin=object): # allow_none = True
 
     class BaseElement(cls):
         def __default__(cls, *elements):
-            elts = [e for e in elements 
+            elts = [e for e in elements
                 if e is not None and not isinstance(e, Empty)]
             if len(elts) == 0:
                 # The first is always Empty
                 assert len(elements) != 0
                 return elements[0]
             elif len(elts) == 1:
-                return elts[0] 
+                return elts[0]
             return cls(*elts)
 
         def __concurrent__(*elements):
index 8187679..a116ba8 100644 (file)
@@ -100,6 +100,7 @@ class PendingValue:
     def trigger(self, action, value, cur_value = None):
 
         if self.value is NEVER_SET:
+            #XXX Shouldn't we set it to None if it is demanded?
             if cur_value is not None:
                 self.value = cur_value
 
index 8346c65..49c34b1 100644 (file)
@@ -113,7 +113,6 @@ class CompositionMixin:
                 ret = await t.get_future()
             self.get_future().set_result(ret)
         except Exception as e:
-            print('we need to cancel tasks not executed...')
             self.get_future().set_exception(e)
 
 Task, EmptyTask = SchedulingAlgebra(BaseTask, ConcurrentMixin,
index f5341f2..0f24549 100644 (file)
@@ -26,4 +26,5 @@ class Application(Resource):
             mandatory = True,
             multiplicity = Multiplicity.ManyToOne,
             reverse_name = 'applications',
+            key = True,
             reverse_description = 'Applications installed on node')
index 09b2418..4398ae5 100644 (file)
@@ -20,7 +20,7 @@ import logging
 import networkx as nx
 import os
 
-from netmodel.model.type                import String
+from netmodel.model.type                import String, Integer
 from netmodel.util.misc                 import pairwise
 from vicn.core.attribute                import Attribute, Reference
 from vicn.core.exception                import ResourceNotFound
@@ -219,6 +219,9 @@ class IPRoutes(Resource):
     """
     routing_strategy = Attribute(String)
 
+    def __after__(self):
+        return ("IpAssignment",)
+
     #--------------------------------------------------------------------------
     # Resource lifecycle
     #--------------------------------------------------------------------------
@@ -246,17 +249,16 @@ class IPRoutes(Resource):
     def _get_ip_origins(self):
         origins = dict()
         for group in self.groups:
-            for node in group.iter_by_type_str('node'):
-                node_uuid = node._state.uuid
-                if not node_uuid in origins:
-                    origins[node_uuid] = list()
-                for interface in node.interfaces:
-                    # XXX temp fix (WouldBlock)
-                    try:
-                        origins[node_uuid].append(interface.ip4_address)
-                        if interface.ip6_address: #Control interfaces have no v6 address
-                            origins[node_uuid].append(interface.ip6_address)
-                    except: pass
+            for channel in group.iter_by_type_str('channel'):
+                for interface in channel.interfaces:
+                    node_uuid = interface.node._state.uuid
+                    if not node_uuid in origins:
+                        origins[node_uuid] = list()
+                    ip4 = interface.ip4_address
+                    origins[node_uuid].append(interface.ip4_address)
+                    if interface.ip6_address:
+                        ip6 = interface.ip6_address
+                        origins[node_uuid].append(interface.ip6_address)
         return origins
 
     def _get_ip_routes(self):
@@ -595,6 +597,9 @@ class CentralIP(Resource):
             mandatory = True)
     ip4_data_prefix = Attribute(String, description="Prefix for IPv4 forwarding",
             mandatory = True)
+    ip6_max_link_prefix = Attribute(Integer,
+            description = 'Maximum prefix size assigned to each link',
+            default = 64)
 
     #--------------------------------------------------------------------------
     # Resource lifecycle
@@ -610,7 +615,8 @@ class CentralIP(Resource):
         ip4_assign = Ipv4Assignment(prefix = self.ip4_data_prefix,
                 groups = Reference(self, 'groups'))
         ip6_assign = Ipv6Assignment(prefix = self.ip6_data_prefix,
-                groups = Reference(self, 'groups'))
+                groups = Reference(self, 'groups'),
+                max_prefix_size = self.ip6_max_link_prefix)
         ip_routes = IPRoutes(owner = self,
                 groups = Reference(self, 'groups'),
                 routing_strategy = self.ip_routing_strategy)
index a719caf..748532c 100644 (file)
@@ -40,19 +40,17 @@ class Forwarder(ICNApplication, ABC):
     faces = Attribute(Face, description = 'ICN ffaces of the forwarder',
             multiplicity = Multiplicity.OneToMany,
             reverse_name = 'forwarder')
-    routes = Attribute(Route, description = 'Routes in the ICN FIB', 
+    routes = Attribute(Route, description = 'Routes in the ICN FIB',
             multiplicity = Multiplicity.OneToMany,
             reverse_name = 'forwarder')
-    cache_size = Attribute(Integer, 
+    cache_size = Attribute(Integer,
             description = 'Size of the cache (in chunks)',
             default = DEFAULT_CACHE_SIZE)
-    cache_policy = Attribute(String, description = 'Cache policy', 
+    cache_policy = Attribute(String, description = 'Cache policy',
             default = DEFAULT_CACHE_POLICY)
-    strategy = Attribute(String, description = 'Forwarding Strategy', 
+    strategy = Attribute(String, description = 'Forwarding Strategy',
             default = DEFAULT_STRATEGY)
     config_file = Attribute(String, description = 'Configuration file')
-    port = Attribute(Integer, description = 'Default listening port', 
-            default = lambda self: self._get_default_port())
     log_file = Attribute(String, description = 'Log file')
 
     # Overloaded attributes
index 5abee3c..817d940 100644 (file)
 # limitations under the License.
 #
 
-from vicn.resource.linux.application    import LinuxApplication 
+from vicn.resource.linux.application    import LinuxApplication
 from vicn.core.attribute                import Attribute
 from netmodel.model.type                import Integer
 
-ICN_SUITE_CCNX_1_0=0
-ICN_SUITE_NDN=1
+ICN_SUITE_CCNX_1_0=1
+ICN_SUITE_NDN=2
 
 class ICNApplication(LinuxApplication):
     """
     Resource: ICNApplication
     """
 
-    protocol_suites = Attribute(Integer, 
+    protocol_suites = Attribute(Integer,
             description = 'Protocol suites supported by the application',
             default = lambda self: self._def_protocol_suite())
 
index db5f542..0ae2dc9 100644 (file)
@@ -30,7 +30,8 @@ class Interface(Resource):
     node = Attribute(Node, description = 'Node to which the interface belongs',
             multiplicity = Multiplicity.ManyToOne,
             reverse_name = 'interfaces',
-            mandatory = True)
+            mandatory = True,
+            key = True)
     channel = Attribute(Channel, description = 'Channel to which the interface is attached',
             multiplicity = Multiplicity.ManyToOne,
             reverse_name = 'interfaces')
index d3a8139..f5f7d1e 100644 (file)
@@ -20,8 +20,9 @@ from socket import inet_pton, inet_ntop, AF_INET6
 from struct import unpack, pack
 from abc    import ABCMeta
 
-class NotEnoughAddresses(Exception):
-    pass
+class PrefixTreeException(Exception): pass
+class NotEnoughAddresses(PrefixTreeException): pass
+class UnassignablePrefix(PrefixTreeException): pass
 
 class Prefix(metaclass=ABCMeta):
 
@@ -33,7 +34,6 @@ class Prefix(metaclass=ABCMeta):
             ip_address = self.aton(ip_address)
         self.ip_address = ip_address
         self.prefix_size = prefix_size
-        self._range = self.limits()
 
     def __contains__(self, obj):
         #it can be an IP as a integer
@@ -50,6 +50,14 @@ class Prefix(metaclass=ABCMeta):
 
         return self._contains_prefix(obj)
 
+    @classmethod
+    def mask(cls):
+        mask_len = cls.MAX_PREFIX_SIZE//8 #Converts from bits to bytes
+        mask = 0
+        for step in range(0,mask_len):
+            mask = (mask << 8) | 0xff
+        return mask
+
     def _contains_prefix(self, prefix):
         assert isinstance(prefix, type(self))
         return (prefix.prefix_size >= self.prefix_size and
@@ -58,10 +66,10 @@ class Prefix(metaclass=ABCMeta):
 
     #Returns the first address of a prefix
     def first_prefix_address(self):
-        return self.ip_address & (self.MASK << (self.MAX_PREFIX_SIZE-self.prefix_size))
+        return self.ip_address & (self.mask() << (self.MAX_PREFIX_SIZE-self.prefix_size))
 
     def last_prefix_address(self):
-        return self.ip_address | (self.MASK >> self.prefix_size)
+        return self.ip_address | (self.mask() >> self.prefix_size)
 
     def limits(self):
         return self.first_prefix_address(), self.last_prefix_address()
@@ -77,12 +85,20 @@ class Prefix(metaclass=ABCMeta):
         return hash(str(self))
 
     def __iter__(self):
-        for i in range(self._range[0], self._range[1]+1):
+        for i in range(self.first_prefix_address(), self.last_prefix_address()+1):
             yield self.ntoa(i)
 
+    #Iterates by steps of prefix_size, e.g., on all available /31 in a /24
+    def get_iterator(self, prefix_size=None):
+        if prefix_size is None:
+            prefix_size=self.MAX_PREFIX_SIZE
+        assert (prefix_size >= self.prefix_size and prefix_size<=self.MAX_PREFIX_SIZE)
+        step = 2**(self.MAX_PREFIX_SIZE - prefix_size)
+        for ip in range(self.first_prefix_address(), self.last_prefix_address()+1, step):
+            yield type(self)(ip, prefix_size)
+
 class Inet4Prefix(Prefix):
 
-    MASK = 0xffffffff
     MAX_PREFIX_SIZE = 32
 
     @classmethod
@@ -103,50 +119,48 @@ class Inet4Prefix(Prefix):
 
 class Inet6Prefix(Prefix):
 
-    MASK = 0xffffffffffffffff
-    MAX_PREFIX_SIZE = 64
-
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self._range = self.limits(True)
+    MAX_PREFIX_SIZE = 128
 
     @classmethod
-    def aton (cls, address, with_suffix=False):
-        ret, suffix = unpack(">QQ", inet_pton(AF_INET6, address))
-        if with_suffix:
-            ret = (ret << 64) | suffix
-        return ret
+    def aton (cls, address):
+        prefix, suffix = unpack(">QQ", inet_pton(AF_INET6, address))
+        return (prefix << 64) | suffix
 
     @classmethod
-    def ntoa (cls, address, with_suffix=False):
-        ret = None
-        if with_suffix:
-            ret = inet_ntop(AF_INET6, pack(">QQ", address >> 64, address & ((1 << 64) -1)))
-        else:
-            ret = inet_ntop(AF_INET6, pack(">QQ", address, 0))
-        return ret
-
-    def limits(self, with_suffix=False):
-        ret = super().limits()
-        if with_suffix:
-            ret = ret[0] << 64, ret[1] << 64 | self.MASK
-        return ret
-
-    def __iter__(self):
-        for i in range(*self._range):
-            yield self.ntoa(i, True)
+    def ntoa (cls, address):
+        return inet_ntop(AF_INET6, pack(">QQ", address >> 64, address & ((1 << 64) -1)))
+
+    #skip_internet_address: skip a:b::0, as v6 often use default /64 prefixes
+    def get_iterator(self, prefix_size=None, skip_internet_address=None):
+        if skip_internet_address is None:
+            #We skip the internet address if we iterate over Addresses
+            if prefix_size is None:
+                skip_internet_address = True
+            #But not if we iterate over prefixes
+            else:
+                skip_internet_address = False
+        it = super().get_iterator(prefix_size)
+        if skip_internet_address:
+            next(it)
+        return it
 
 ###### PREFIX TREE ######
 
 class PrefixTree:
-    def __init__(self, prefix):
+
+    #Use max_served_prefix to set a maximum served prefix size (e.g., /64 for IPv6)
+    def __init__(self, prefix, max_served_prefix=None):
         self.prefix = prefix
         self.prefix_cls = type(prefix)
+        if max_served_prefix is None:
+            max_served_prefix = self.prefix_cls.MAX_PREFIX_SIZE
+        self.max_served_prefix = max_served_prefix
         self.left = None
         self.right = None
         #When the full prefix is assigned
         self.full = False
 
+
     def find_prefix(self, prefix_size):
         ret, lret, rret = [None]*3
         if prefix_size > self.prefix.prefix_size and not self.full:
index 62a3238..55401ec 100644 (file)
 #
 
 import math
+import logging
 
 from vicn.core.resource                 import Resource
 from netmodel.model.type                import String
 from vicn.core.attribute                import Attribute
 from vicn.resource.ip.prefix_tree       import Inet6Prefix, PrefixTree, Inet4Prefix
-from vicn.core.task                     import inline_task
+from vicn.core.task                     import inline_task, async_task, EmptyTask
 from vicn.core.exception                import ResourceNotFound
 
+log = logging.getLogger(__name__)
+
 class IpAssignment(Resource):
     prefix = Attribute(String, mandatory=True)
     control_prefix = Attribute(String, description="prefix for control plane")
+    max_prefix_size = Attribute(String,
+            description="Maximum assigned prefix size for a link")
 
     PrefixClass = None
 
@@ -36,6 +41,8 @@ class IpAssignment(Resource):
         self._prefix = self.PrefixClass(self.prefix)
         self._prefix_tree = PrefixTree(self._prefix)
         self._assigned_prefixes = {}
+        if not self.max_prefix_size:
+            self.max_prefix_size = self.PrefixClass.MAX_PREFIX_SIZE
         if self.control_prefix:
             self._ctrl_prefix = self.PrefixClass(self.control_prefix)
             self._ctrl_prefix_it = iter(self._ctrl_prefix)
@@ -66,10 +73,11 @@ class IpAssignment(Resource):
     def __get__(self):
         raise ResourceNotFound
 
-    @inline_task
+    #@inline_task
     def __create__(self):
         # XXX code from Channel.__create__, until Events are properly implemented.
         # Iterate on channels for allocate IP addresses
+        task = EmptyTask()
         for group in self.groups:
             for channel in group.iter_by_type_str('channel'):
                 interfaces = sorted(channel.interfaces, key = lambda x : x.device_name)
@@ -77,27 +85,33 @@ class IpAssignment(Resource):
                     continue
 
                 min_prefix_size = math.ceil(math.log(len(channel.interfaces), 2))
-                prefix_size = min(self.DEFAULT_PREFIX_SIZE, self.MAX_PREFIX_SIZE - min_prefix_size)
-                prefix = iter(self.get_prefix(channel, prefix_size))
+                prefix_size = min(self.max_prefix_size,
+                        self.PrefixClass.MAX_PREFIX_SIZE - min_prefix_size)
+                prefix = self.get_prefix(channel, prefix_size)
+
+                it = prefix.get_iterator()
 
                 for interface in interfaces:
-                    ip = next(prefix)
-                    print('attribute ip=', ip)
-                    setattr(interface, self.ATTR_ADDRESS, ip)
-                    setattr(interface, self.ATTR_PREFIX, prefix_size)
+                    ip = next(it)
+                    interface.set(self.ATTR_PREFIX, prefix_size)
+                    #XXX Why do we need to create that async task?
+                    #XXX Probably because the PendingValue is not created
+                    #XXX in the main thread
+                    @async_task
+                    async def set_ip(interface, ip):
+                        await interface.async_set(self.ATTR_ADDRESS, self.PrefixClass.ntoa(ip.ip_address))
+                    task = task | set_ip(interface, ip)
+
+        return task
 
     __delete__ = None
 
 class Ipv6Assignment(IpAssignment):
     PrefixClass = Inet6Prefix
-    DEFAULT_PREFIX_SIZE = 64
-    MAX_PREFIX_SIZE = 128
     ATTR_ADDRESS = 'ip6_address'
     ATTR_PREFIX  = 'ip6_prefix'
 
 class Ipv4Assignment(IpAssignment):
     PrefixClass = Inet4Prefix
-    DEFAULT_PREFIX_SIZE = 32
-    MAX_PREFIX_SIZE = 32
     ATTR_ADDRESS = 'ip4_address'
     ATTR_PREFIX  = 'ip4_prefix'
index cddda8e..44b4b5b 100644 (file)
@@ -37,14 +37,16 @@ class File(Resource):
     """
     Resource: File
     """
-    filename = Attribute(String, description = 'Path to the file', 
+    filename = Attribute(String, description = 'Path to the file',
+            key = True,
             mandatory = True)
     node = Attribute(Node, description = 'Node on which the file is created',
             mandatory = True,
             multiplicity = Multiplicity.ManyToOne,
             reverse_name = 'files',
+            key = True,
             reverse_description = 'Files created on the node')
-    overwrite = Attribute(Bool, 
+    overwrite = Attribute(Bool,
             description = 'Determines whether an existing file is overwritten',
             default = False)
 
@@ -53,13 +55,12 @@ class File(Resource):
     #--------------------------------------------------------------------------
 
     def __get__(self):
-
         # UGLY
         @inline_task
         def not_found():
             raise ResourceNotFound
 
-        if self.overwrite: 
+        if self.overwrite:
             return not_found()
 
         def is_path (rv):
index a81a40d..66c98e5 100644 (file)
@@ -37,7 +37,7 @@ class Keypair(Resource):
 
     Implements a SSH keypair
     """
-    node = Attribute(Node, 
+    node = Attribute(Node,
             description = 'Node on which the certificate is created',
             mandatory = True,
             multiplicity = Multiplicity.ManyToOne)
@@ -47,24 +47,24 @@ class Keypair(Resource):
     #--------------------------------------------------------------------------
     # Resource lifecycle
     #--------------------------------------------------------------------------
-    
+
     @inline_task
     def __initialize__(self):
         self._pubkey_file = File(node = Reference(self, 'node'),
                 filename = self.key + '.pub',
                 managed = False)
-        self._key_file = File(node = Reference(self, 'node'), 
-                filename = self.key, 
+        self._key_file = File(node = Reference(self, 'node'),
+                filename = self.key,
                 managed = False)
 
     def __get__(self):
         return self._pubkey_file.__get__() | self._key_file.__get__()
 
     def __create__(self):
-        return BashTask(None, CMD_CREATE, {
+        return BashTask(self.node, CMD_CREATE, {
                 'dirname': os.path.dirname(self.key),
                 'self': self})
-    
+
     def __delete__(self):
         return self._pubkey_file.__delete__() | self._key_file.__delete__()
 
index ad77bfb..da41fbe 100644 (file)
@@ -73,8 +73,10 @@ class Link(Channel):
     delay = Attribute(String, description = 'Link propagation delay')
 
     src_node = Attribute(Node, description = 'Source node',
+            key = True,
             mandatory = True)
     dst_node = Attribute(Node, description = 'Destination node',
+            key = True,
             mandatory = True)
 
     def __init__(self, *args, **kwargs):
@@ -89,6 +91,7 @@ class Link(Channel):
         # but the resource manager has to take over for IP addresses etc.
         # Being done in initialize, those attributes won't be considered as
         # dependencies and will thus not block the resource state machine.
+
         self._src = NonTapBaseNetDevice(node = self.src_node,
                 device_name = self.dst_node.name,
                 channel = self,
index e40256e..40d3edb 100644 (file)
@@ -280,6 +280,7 @@ class BaseNetDevice(Interface, Application):
             description = 'Capacity for interface shaping (Mb/s)')
     mac_address = Attribute(String, description = 'Mac address of the device')
     ip4_address = Attribute(String, description = 'IP address of the device')
+    ip4_prefix = Attribute(Integer, description = 'Prefix for the IPv4link', default=31) #XXX 31?
     ip6_address = Attribute(String, description = 'IPv6 address of the device')
     ip6_prefix = Attribute(Integer, description = 'Prefix for the IPv6 link', default=64)
     ip6_forwarding = Attribute(Bool, description = 'IPv6 forwarding', default = True)
index eaf83e1..1b9d518 100644 (file)
@@ -78,6 +78,7 @@ class PackageManager(Resource):
             reverse_name = 'package_manager',
             reverse_auto = True,
             mandatory = True,
+            key = True,
             multiplicity = Multiplicity.OneToOne)
     trusted = Attribute(Bool,
             description="Force repository trust",
@@ -181,6 +182,7 @@ class Package(Resource):
     package_name = Attribute(String, mandatory = True)
     node = Attribute(Node,
             mandatory = True,
+            key = True,
             requirements=[
                 Requirement('package_manager')
             ])
@@ -216,6 +218,7 @@ class Packages(Resource):
     names = Attribute(String, multiplicity = Multiplicity.OneToMany)
     node = Attribute(Node,
             mandatory = True,
+            key = True,
             requirements=[
                 Requirement('package_manager')
             ])
index d7c0b51..f71b585 100644 (file)
@@ -75,7 +75,7 @@ class Physical(Node):
         """
         Require a SSH keypair to be present for authentication on nodes
         """
-        return Keypair(node = None, key = FN_KEY)
+        return Keypair(node = self, key = FN_KEY)
 
     def __initialize__(self):
         if not is_local_host(self.hostname):
index 5670d1a..654b3bc 100644 (file)
@@ -128,9 +128,6 @@ class LxcContainer(Node):
             if iface.get_type() == "dpdkdevice":
                 self.node.vpp_host.dpdk_devices.append(iface.pci_address)
 
-        if 'vpp' in self.profiles:
-            dummy = self.node.vpp_host.uio_devices
-
     @task
     def __get__(self):
         client = self.node.lxd_hypervisor.client
@@ -158,7 +155,6 @@ class LxcContainer(Node):
         log.debug('Container description: {}'.format(container))
         client = self.node.lxd_hypervisor.client
         self._container = client.containers.create(container, wait=True)
-        #self._container.start(wait = True)
 
     def _get_container_description(self):
         # Base configuration
@@ -188,23 +184,6 @@ class LxcContainer(Node):
                         'path' : '/dev/{}'.format(device),
                         'type' : 'unix-char' }
 
-#        # NETWORK (not for images)
-#
-#        if not self.is_image:
-#            container['config']['user.network_mode'] = 'link-local'
-#            device = {
-#                'type'      : 'nic',
-#                'name'      : self.host_interface.device_name,
-#                'nictype'   : 'bridged',
-#                'parent'    : self.node.bridge.device_name,
-#            }
-#            device['hwaddr'] = AddressManager().get_mac(self)
-#            prefix = 'veth-{}'.format(self.container_name)
-#            device['host_name'] = AddressManager().get('device_name', self,
-#                    prefix = prefix, scope = prefix)
-#
-#            container['devices'][device['name']] = device
-
         # SOURCE
 
         image_names = [alias['name'] for alias in self.node.lxd_hypervisor.aliases]
@@ -231,7 +210,6 @@ class LxcContainer(Node):
     @task
     def __delete__(self):
         log.info("Delete container {}".format(self.container_name))
-        import pdb; pdb.set_trace()
         self.node.lxd_hypervisor.client.containers.remove(self.name)
 
     #--------------------------------------------------------------------------
@@ -308,6 +286,10 @@ class LxcContainer(Node):
         We don't currently use an eventually available  SSH connection.
         """
 
+        if not self._container:
+            log.error("Executing command on uninitialized container", self, command)
+            import os; os._exit(1)
+
         ret = self._container.execute(shlex.split(command))
 
         # NOTE: pylxd documents the return value as a tuple, while it is in
index 2cc7220..a3a0324 100644 (file)
@@ -43,22 +43,22 @@ class LxcImage(Resource):
     image = Attribute(Self, description = 'image', default = None)
     applications = Attribute(Application, multiplicity = Multiplicity.OneToMany)
 
-    #--------------------------------------------------------------------------- 
+    #---------------------------------------------------------------------------
     # Constructor / Accessors
-    #--------------------------------------------------------------------------- 
+    #---------------------------------------------------------------------------
 
     def __init__(self, *args, **kwargs):
         self.fingerprint = None
         self._tmp_container = None
         super().__init__(*args, **kwargs)
 
-    #--------------------------------------------------------------------------- 
+    #---------------------------------------------------------------------------
     # Resource lifecycle
-    #--------------------------------------------------------------------------- 
+    #---------------------------------------------------------------------------
 
     @task
     def __get__(self):
-        aliases = [alias['name'] for images in self.node.lxd_hypervisor.client.images.all() 
+        aliases = [alias['name'] for images in self.node.lxd_hypervisor.client.images.all()
                          for alias in images.aliases]
         if not self.image in aliases:
             raise ResourceNotFound
@@ -75,10 +75,8 @@ class LxcImage(Resource):
         Image creation consists in setting up a temporary container, stopping
         it, publishing an image of it, setting an alias, and deleting it.
         """
-
-
         tmp_container.setup()
-        
+
         print("TODO: Installing applications...")
         for application in self.applications:
             print('Installing application on image')
@@ -103,13 +101,13 @@ class LxcImage(Resource):
     def __delete__(self):
         self.node.lxd_hypervisor.client.images.delete(self.name)
 
-    #--------------------------------------------------------------------------- 
+    #---------------------------------------------------------------------------
     # Public methods
-    #--------------------------------------------------------------------------- 
+    #---------------------------------------------------------------------------
 
     def set_alias(self):
         alias_dict = {
-            "description": "Ubuntu 14.04 image with ICN software already installed",
+            "description": "Ubuntu 16.04 image with ICN software already installed",
             "target": self.fingerprint,
             "name": self.name
         }
index be523a6..1a68f11 100644 (file)
@@ -51,14 +51,14 @@ class CICNForwarder(Forwarder):
             mandatory=True,
             requirements = [Requirement('vpp')],
             reverse_name='cicn')
-    numa_node = Attribute(Integer, 
-            description = 'Numa node on which vpp will run', 
+    numa_node = Attribute(Integer,
+            description = 'Numa node on which vpp will run',
             default = None)
-    core = Attribute(Integer, 
+    core = Attribute(Integer,
             description = 'Core belonging the numa node on which vpp will run',
             default = None)
-    enable_worker = Attribute(Bool, 
-            description = 'Enable one worker for packet processing', 
+    enable_worker = Attribute(Bool,
+            description = 'Enable one worker for packet processing',
             default = False)
 
     #__packages__ = ['vpp-plugin-cicn']
@@ -70,14 +70,14 @@ class CICNForwarder(Forwarder):
         def parse(rv):
             if rv.return_value > 0 or 'cicn: not enabled' in rv.stdout:
                 raise ResourceNotFound
-        return BashTask(self.node, CMD_VPP_CICN_GET, 
+        return BashTask(self.node, CMD_VPP_CICN_GET,
                 lock = self.node.vpp.vppctl_lock, parse=parse)
 
     def __create__(self):
 
         #self.node.vpp.plugins.append("cicn")
         lock = self.node.vpp.vppctl_lock
-        create_task = BashTask(self.node, CMD_VPP_ENABLE_PLUGIN, 
+        create_task = BashTask(self.node, CMD_VPP_ENABLE_PLUGIN,
                 {'plugin' : 'cicn'}, lock = lock)
 
         face_task = EmptyTask()
@@ -89,7 +89,7 @@ class CICNForwarder(Forwarder):
             return {}
 
         for face in self.faces:
-            face_task = face_task > BashTask(self.node, CMD_VPP_ADD_ICN_FACE, 
+            face_task = face_task > BashTask(self.node, CMD_VPP_ADD_ICN_FACE,
                     {'face':face},
                     parse = (lambda x : parse_face(x, face)), lock = lock)
 
@@ -99,7 +99,7 @@ class CICNForwarder(Forwarder):
                 if route.node is self.node:
                     self.routes.append(route)
         for route in self.routes:
-            route_task = route_task > BashTask(self.node, 
+            route_task = route_task > BashTask(self.node,
                     CMD_VPP_ADD_ICN_ROUTE, {'route' : route}, lock = lock)
 
         return (wait_resource_task(self.node.vpp) > create_task) > (face_task > route_task)
index 69449e4..472ee26 100644 (file)
@@ -27,9 +27,9 @@ class DpdkDevice(PhyInterface):
     A DpdkDevice is a physical net device supported by Dpdk and with parameters
     specific to VPP.
     """
-    numa_node = Attribute(Integer, 
+    numa_node = Attribute(Integer,
             description = 'NUMA node on the same PCI bus as the DPDK card')
-    socket_mem = Attribute(Integer, 
-            description = 'Memory used by the vpp forwarder', 
+    socket_mem = Attribute(Integer,
+            description = 'Memory used by the vpp forwarder',
             default = 512)
     mac_address = Attribute(String)
index f9d1070..0edbe9b 100644 (file)
@@ -55,11 +55,11 @@ class VPP(Resource):
     node = Attribute(Node,
             multiplicity = Multiplicity.OneToOne,
             reverse_name = 'vpp')
-    numa_node = Attribute(Integer, 
+    numa_node = Attribute(Integer,
             description = 'Numa node on which vpp will run')
-    core = Attribute(Integer, 
+    core = Attribute(Integer,
             description = 'Core belonging the numa node on which vpp will run')
-    enable_worker = Attribute(Bool, 
+    enable_worker = Attribute(Bool,
             description = 'Enable one worker for packet processing',
             default = False)
 
@@ -88,8 +88,8 @@ class VPP(Resource):
         return BashTask(self.node, CMD_GET)
 
     def __subresources__(self):
-        self.dpdk_setup_file = TextFile(node = self.node, 
-                filename = FN_VPP_DPDK_SCRIPT, 
+        self.dpdk_setup_file = TextFile(node = self.node,
+                filename = FN_VPP_DPDK_SCRIPT,
                 overwrite = True)
         return self.dpdk_setup_file
 
@@ -137,7 +137,7 @@ class VPP(Resource):
 
         # Add the core on which running vpp and the dpdk parameters
         setup = TPL_VPP_DPDK_DAEMON_SCRIPT + 'cpu {'
-            
+
         setup = setup + ''' \n  main-core ''' + str(self.core)
 
         if self.enable_worker:
@@ -145,7 +145,7 @@ class VPP(Resource):
             setup = setup + '''\n  corelist-workers ''' + str(cpu_worker)
 
         setup = setup + '''\n}\n\n  dpdk { '''
-        
+
         for dpdk_dev in dpdk_list:
             setup = setup + ''' \n  ''' + dpdk_dev
 
@@ -175,7 +175,7 @@ class VPP(Resource):
     def _set_plugins(self):
         cmd = None
         for plugin in self.plugins:
-            cmd = cmd > BashTask(self.node, CMD_VPP_ENABLE_PLUGIN, 
+            cmd = cmd > BashTask(self.node, CMD_VPP_ENABLE_PLUGIN,
                     {'plugin' : plugin})
         return cmd
 
index 600d556..954d1d3 100644 (file)
@@ -55,7 +55,7 @@ class VPPHost(LinuxApplication):
 
     Host must be configured to let vpp to work into container:
      - install new apparmor profile (to let the container to read
-       hugepages info in /sys/kernel/mm/hugepages) 
+       hugepages info in /sys/kernel/mm/hugepages)
      - set hugepages into the host
     """
 
@@ -96,7 +96,7 @@ class VPPHost(LinuxApplication):
                 overwrite = True)
         startup_conf = TextFile(node = self.node,
                 filename = FN_VPP_DPDK_SCRIPT,
-                content = TPL_VPP_DPDK_DAEMON_SCRIPT, 
+                content = TPL_VPP_DPDK_DAEMON_SCRIPT,
                 overwrite = True)
         return app_armor_file | startup_conf
 
@@ -111,7 +111,7 @@ class VPPHost(LinuxApplication):
     def __create__(self):
         modules = BashTask(self.node, CMD_INSERT_MODULES)
         app_armor_reload = BashTask(self.node, CMD_APP_ARMOR_RELOAD)
-        sysctl_hugepages = BashTask(self.node, CMD_SYSCTL_HUGEPAGES, 
+        sysctl_hugepages = BashTask(self.node, CMD_SYSCTL_HUGEPAGES,
                 {'nb_hp': DEFAULT_NB_HUGEPAGES})
 
         # Hook
@@ -126,9 +126,9 @@ class VPPHost(LinuxApplication):
 
         create_uio = EmptyTask()
         for device in self.dpdk_devices:
-            create_uio = create_uio > BashTask(self.node, 
+            create_uio = create_uio > BashTask(self.node,
                     CMD_CREATE_UIO_DEVICES, {'pci_address' : device})
-       
+
         return ((modules | app_armor_reload) | sysctl_hugepages) > \
             (disable_vpp > create_uio)