aboutsummaryrefslogtreecommitdiffstats
path: root/vicn
diff options
context:
space:
mode:
Diffstat (limited to 'vicn')
-rw-r--r--vicn/core/attribute.py13
-rw-r--r--vicn/core/resource.py11
-rw-r--r--vicn/core/resource_mgr.py62
-rw-r--r--vicn/core/scheduling_algebra.py6
-rw-r--r--vicn/core/state.py1
-rw-r--r--vicn/core/task.py1
-rw-r--r--vicn/resource/application.py1
-rw-r--r--vicn/resource/central.py32
-rw-r--r--vicn/resource/icn/forwarder.py10
-rw-r--r--vicn/resource/icn/icn_application.py8
-rw-r--r--vicn/resource/interface.py3
-rw-r--r--vicn/resource/ip/prefix_tree.py86
-rw-r--r--vicn/resource/ip_assignment.py38
-rw-r--r--vicn/resource/linux/file.py9
-rw-r--r--vicn/resource/linux/keypair.py12
-rw-r--r--vicn/resource/linux/link.py3
-rw-r--r--vicn/resource/linux/net_device.py1
-rw-r--r--vicn/resource/linux/package_manager.py3
-rw-r--r--vicn/resource/linux/physical.py2
-rw-r--r--vicn/resource/lxd/lxc_container.py26
-rw-r--r--vicn/resource/lxd/lxc_image.py20
-rw-r--r--vicn/resource/vpp/cicn.py18
-rw-r--r--vicn/resource/vpp/dpdk_device.py6
-rw-r--r--vicn/resource/vpp/vpp.py16
-rw-r--r--vicn/resource/vpp/vpp_host.py10
25 files changed, 214 insertions, 184 deletions
diff --git a/vicn/core/attribute.py b/vicn/core/attribute.py
index 22f44487..3afe0d6e 100644
--- a/vicn/core/attribute.py
+++ b/vicn/core/attribute.py
@@ -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())
diff --git a/vicn/core/resource.py b/vicn/core/resource.py
index ab96daa5..9044ec21 100644
--- a/vicn/core/resource.py
+++ b/vicn/core/resource.py
@@ -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:
diff --git a/vicn/core/resource_mgr.py b/vicn/core/resource_mgr.py
index 4ca8060c..e6029cd7 100644
--- a/vicn/core/resource_mgr.py
+++ b/vicn/core/resource_mgr.py
@@ -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,
diff --git a/vicn/core/scheduling_algebra.py b/vicn/core/scheduling_algebra.py
index 207856c0..368ac243 100644
--- a/vicn/core/scheduling_algebra.py
+++ b/vicn/core/scheduling_algebra.py
@@ -16,19 +16,19 @@
# 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):
diff --git a/vicn/core/state.py b/vicn/core/state.py
index 81876790..a116ba82 100644
--- a/vicn/core/state.py
+++ b/vicn/core/state.py
@@ -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
diff --git a/vicn/core/task.py b/vicn/core/task.py
index 8346c65e..49c34b1f 100644
--- a/vicn/core/task.py
+++ b/vicn/core/task.py
@@ -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,
diff --git a/vicn/resource/application.py b/vicn/resource/application.py
index f5341f2b..0f245496 100644
--- a/vicn/resource/application.py
+++ b/vicn/resource/application.py
@@ -26,4 +26,5 @@ class Application(Resource):
mandatory = True,
multiplicity = Multiplicity.ManyToOne,
reverse_name = 'applications',
+ key = True,
reverse_description = 'Applications installed on node')
diff --git a/vicn/resource/central.py b/vicn/resource/central.py
index 09b24184..4398ae5a 100644
--- a/vicn/resource/central.py
+++ b/vicn/resource/central.py
@@ -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)
diff --git a/vicn/resource/icn/forwarder.py b/vicn/resource/icn/forwarder.py
index a719caf7..748532cf 100644
--- a/vicn/resource/icn/forwarder.py
+++ b/vicn/resource/icn/forwarder.py
@@ -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
diff --git a/vicn/resource/icn/icn_application.py b/vicn/resource/icn/icn_application.py
index 5abee3c5..817d9403 100644
--- a/vicn/resource/icn/icn_application.py
+++ b/vicn/resource/icn/icn_application.py
@@ -16,19 +16,19 @@
# 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())
diff --git a/vicn/resource/interface.py b/vicn/resource/interface.py
index db5f5427..0ae2dc94 100644
--- a/vicn/resource/interface.py
+++ b/vicn/resource/interface.py
@@ -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')
diff --git a/vicn/resource/ip/prefix_tree.py b/vicn/resource/ip/prefix_tree.py
index d3a8139a..f5f7d1e9 100644
--- a/vicn/resource/ip/prefix_tree.py
+++ b/vicn/resource/ip/prefix_tree.py
@@ -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:
diff --git a/vicn/resource/ip_assignment.py b/vicn/resource/ip_assignment.py
index 62a32389..55401ecd 100644
--- a/vicn/resource/ip_assignment.py
+++ b/vicn/resource/ip_assignment.py
@@ -17,17 +17,22 @@
#
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'
diff --git a/vicn/resource/linux/file.py b/vicn/resource/linux/file.py
index cddda8ed..44b4b5be 100644
--- a/vicn/resource/linux/file.py
+++ b/vicn/resource/linux/file.py
@@ -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):
diff --git a/vicn/resource/linux/keypair.py b/vicn/resource/linux/keypair.py
index a81a40d4..66c98e5b 100644
--- a/vicn/resource/linux/keypair.py
+++ b/vicn/resource/linux/keypair.py
@@ -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__()
diff --git a/vicn/resource/linux/link.py b/vicn/resource/linux/link.py
index ad77bfb9..da41fbe1 100644
--- a/vicn/resource/linux/link.py
+++ b/vicn/resource/linux/link.py
@@ -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,
diff --git a/vicn/resource/linux/net_device.py b/vicn/resource/linux/net_device.py
index e40256ea..40d3edb7 100644
--- a/vicn/resource/linux/net_device.py
+++ b/vicn/resource/linux/net_device.py
@@ -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)
diff --git a/vicn/resource/linux/package_manager.py b/vicn/resource/linux/package_manager.py
index eaf83e17..1b9d518c 100644
--- a/vicn/resource/linux/package_manager.py
+++ b/vicn/resource/linux/package_manager.py
@@ -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')
])
diff --git a/vicn/resource/linux/physical.py b/vicn/resource/linux/physical.py
index d7c0b518..f71b5856 100644
--- a/vicn/resource/linux/physical.py
+++ b/vicn/resource/linux/physical.py
@@ -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):
diff --git a/vicn/resource/lxd/lxc_container.py b/vicn/resource/lxd/lxc_container.py
index 5670d1a2..654b3bc5 100644
--- a/vicn/resource/lxd/lxc_container.py
+++ b/vicn/resource/lxd/lxc_container.py
@@ -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
diff --git a/vicn/resource/lxd/lxc_image.py b/vicn/resource/lxd/lxc_image.py
index 2cc7220d..a3a03245 100644
--- a/vicn/resource/lxd/lxc_image.py
+++ b/vicn/resource/lxd/lxc_image.py
@@ -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
}
diff --git a/vicn/resource/vpp/cicn.py b/vicn/resource/vpp/cicn.py
index be523a6c..1a68f11f 100644
--- a/vicn/resource/vpp/cicn.py
+++ b/vicn/resource/vpp/cicn.py
@@ -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)
diff --git a/vicn/resource/vpp/dpdk_device.py b/vicn/resource/vpp/dpdk_device.py
index 69449e48..472ee26f 100644
--- a/vicn/resource/vpp/dpdk_device.py
+++ b/vicn/resource/vpp/dpdk_device.py
@@ -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)
diff --git a/vicn/resource/vpp/vpp.py b/vicn/resource/vpp/vpp.py
index f9d10703..0edbe9b8 100644
--- a/vicn/resource/vpp/vpp.py
+++ b/vicn/resource/vpp/vpp.py
@@ -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
diff --git a/vicn/resource/vpp/vpp_host.py b/vicn/resource/vpp/vpp_host.py
index 600d5566..954d1d32 100644
--- a/vicn/resource/vpp/vpp_host.py
+++ b/vicn/resource/vpp/vpp_host.py
@@ -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)