diff options
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) |