aboutsummaryrefslogtreecommitdiffstats
path: root/vicn/resource/icn
diff options
context:
space:
mode:
authorMarcel Enguehard <mengueha+fdio@cisco.com>2017-07-19 11:26:26 +0200
committerMarcel Enguehard <mengueha+fdio@cisco.com>2017-07-19 11:51:26 +0000
commit3e6678f9c692553e8902da4d6fb1fe6c087db1f4 (patch)
tree580a46ca5de22a044319eabb295ad980d50589ec /vicn/resource/icn
parent08c4f765cf29dbd6e9a616c542552417eece14fc (diff)
* GUI resource
* MemIf interface for VPP * Better netmodel integration * Draft documentation * New tutorials * Improved monitoring and error handling * Refactored IP addresses and prefixes representation * Improved image mgmt for LXD * Various bugfixes and code refactoring Change-Id: I90da6cf7b5716bc7deb6bf4e24d3f9f01b5a9b0f Signed-off-by: Marcel Enguehard <mengueha+fdio@cisco.com>
Diffstat (limited to 'vicn/resource/icn')
-rw-r--r--vicn/resource/icn/ccnx_keystore.py9
-rw-r--r--vicn/resource/icn/ccnx_metis.py83
-rw-r--r--vicn/resource/icn/ccnx_simpleTrafficGenerator.py9
-rw-r--r--vicn/resource/icn/central.py236
-rw-r--r--vicn/resource/icn/cicn.py139
-rw-r--r--vicn/resource/icn/face.py87
-rw-r--r--vicn/resource/icn/forwarder.py2
-rw-r--r--vicn/resource/icn/icn_application.py1
-rw-r--r--vicn/resource/icn/iping.py26
-rw-r--r--vicn/resource/icn/ndnpingserver.py2
-rw-r--r--vicn/resource/icn/nfd.py8
-rw-r--r--vicn/resource/icn/producer.py6
-rw-r--r--vicn/resource/icn/route.py6
13 files changed, 501 insertions, 113 deletions
diff --git a/vicn/resource/icn/ccnx_keystore.py b/vicn/resource/icn/ccnx_keystore.py
index ddd87019..ceff1647 100644
--- a/vicn/resource/icn/ccnx_keystore.py
+++ b/vicn/resource/icn/ccnx_keystore.py
@@ -18,7 +18,7 @@
from netmodel.model.type import String, Integer
from vicn.core.attribute import Attribute, Reference
-from vicn.core.task import BashTask
+from vicn.core.task import BashTask, inherit_parent
from vicn.resource.linux.file import File
from vicn.resource.linux.package_manager import Packages
@@ -39,13 +39,13 @@ class MetisKeystore(File):
filename = Attribute(String, description = "File containing the keystore",
default = DEFAULT_KEYSTORE_FILE, mandatory=False)
- password = Attribute(String,
+ password = Attribute(String,
description = "Password for the keystore file",
default = DEFAULT_KEYSTORE_PASSWD)
- subject_name = Attribute(String,
+ subject_name = Attribute(String,
description = "Subject name for the keystore",
default = DEFAULT_KEYSTORE_SUBJ)
- validity = Attribute(String,
+ validity = Attribute(String,
description = "Validity period of the keystore",
default = DEFAULT_KEYSTORE_VALIDITY)
size = Attribute(Integer, description = 'Length of the keys',
@@ -62,6 +62,7 @@ class MetisKeystore(File):
names=self._get_package_names(), owner=self)
return packages
+ @inherit_parent
def __create__(self):
args = {'filename' : self.filename, 'password' : self.password,
'subject_name' : self.subject_name, 'validity' : self.validity,
diff --git a/vicn/resource/icn/ccnx_metis.py b/vicn/resource/icn/ccnx_metis.py
index ead9b9bf..418c7683 100644
--- a/vicn/resource/icn/ccnx_metis.py
+++ b/vicn/resource/icn/ccnx_metis.py
@@ -23,9 +23,9 @@ from vicn.core.attribute import Attribute
from vicn.core.exception import ResourceNotFound
from vicn.core.resource_mgr import wait_resource_task
from vicn.core.task import BashTask, EmptyTask, task
+from vicn.core.task import inherit_parent
from vicn.resource.icn.ccnx_keystore import MetisKeystore
-from vicn.resource.icn.face import L2Face, L4Face, FaceProtocol
-from vicn.resource.icn.face import DEFAULT_ETHER_PROTO
+from vicn.resource.icn.face import L2Face, L4Face
from vicn.resource.icn.forwarder import Forwarder
from vicn.resource.linux.file import TextFile
from vicn.resource.linux.service import Service
@@ -37,10 +37,12 @@ CMD_ADD_LISTENER_ETHER = (
'add listener ether ether{conn_id} {listener.src_nic.device_name} '
'{listener.ether_proto}')
CMD_ADD_LISTENER_L4 = 'add listener {protocol} transport{conn_id} {infos}'
-CMD_ADD_CONNECTION_ETHER = ('add connection ether {face.id} {face.dst_mac} '
+CMD_ADD_CX_ETHER = ('add connection ether {face.id} {face.dst_mac} '
'{face.src_nic.device_name}')
-CMD_ADD_CONNECTION_L4 = ('add connection {protocol} {face.id} {face.dst_ip} '
- '{face.dst_port} {face.src_ip} {face.src_port}')
+CMD_ADD_CX_L4_IPV4 = ('add connection {protocol} {face.id} {face.dst.ip4_address} '
+ '{face.dst_port} {face.src.ip4_address} {face.src_port}')
+CMD_ADD_CX_L4_IPV6 = ('add connection {protocol} {face.id} {face.dst.ip6_address} '
+ '{face.dst_port} {face.src.ip6_address} {face.src_port}')
CMD_ADD_ROUTE = 'add route {route.face.id} ccnx:{route.prefix} {route.cost}'
METIS_DAEMON_BOOTSTRAP = (
'metis_daemon --port {port} --daemon --log-file {log_file} '
@@ -64,24 +66,25 @@ class MetisListener:
@staticmethod
def listener_from_face(face):
- if face.protocol is FaceProtocol.ether:
- return MetisEtherListener(face.protocol, face.src_nic,
+ if face.protocol == 'ether':
+ return MetisEtherListener(face.protocol, face.src_nic,
face.ether_proto)
- elif face.protocol in [FaceProtocol.tcp4, FaceProtocol.tcp6,
- FaceProtocol.udp4, FaceProtocol.udp6]:
- return MetisL4Listener(face.protocol, face.src_ip, face.src_port)
+ elif face.protocol in ('tcp4', 'udp4'):
+ return MetisL4Listener(face.protocol, face.src.ip4_address, face.src_port)
+ elif face.protocol in ('tcp6', 'udp6'):
+ return MetisL4Listener(face.protocol, face.src.ip6_address, face.src_port)
else:
raise ValueError("Metis only supports Ethernet and TCP/UDP faces")
class MetisEtherListener(MetisListener):
- def __init__(self, protocol, src_nic, ether_proto=DEFAULT_ETHER_PROTO):
+ def __init__(self, protocol, src_nic, ether_proto):
super().__init__(protocol)
self.src_nic = src_nic
self.ether_proto = ether_proto
def get_setup_command(self, conn_id):
- return CMD_ADD_LISTENER_ETHER.format(listener = self,
+ return CMD_ADD_LISTENER_ETHER.format(listener = self,
conn_id = conn_id)
def __eq__(self, other):
@@ -102,9 +105,9 @@ class MetisL4Listener(MetisListener):
self.src_port = src_port
def _get_proto_as_str(self):
- if self.protocol in (FaceProtocol.tcp4, FaceProtocol.tcp6):
+ if self.protocol in ('tcp4', 'tcp6'):
return "tcp"
- elif self.protocol in (FaceProtocol.udp4, FaceProtocol.udp6):
+ elif self.protocol in ('udp4', 'udp6'):
return "udp"
def get_setup_command(self, conn_id):
@@ -126,14 +129,14 @@ class MetisForwarder(Forwarder, Service):
__package_names__ = ['metis-forwarder']
__service_name__ = "metis-forwarder"
- log_file = Attribute(String, description = 'File for metis logging',
+ log_file = Attribute(String, description = 'File for metis logging',
default = '/tmp/ccnx-metis.log') # '/dev/null')
- port = Attribute(Integer, description = 'TCP port for metis',
+ port = Attribute(Integer, description = 'TCP port for metis',
default = 9695)
- gen_config = Attribute(Bool,
+ gen_config = Attribute(Bool,
description = 'Set to record all metis commands in a config file',
default = True)
- config_file = Attribute(String, default = '/root/.ccnx_metis.conf')
+ config_file = Attribute(String, default = '/root/.ccnx_metis.conf')
#--------------------------------------------------------------------------
# Constructor and Accessors
@@ -160,15 +163,18 @@ class MetisForwarder(Forwarder, Service):
def __after__(self):
return ('CentralICN',)
+ @inherit_parent
def __subresources__(self):
self.keystore = MetisKeystore(node = self.node, owner = self)
self.env_file = self._write_environment_file()
return self.keystore | self.env_file
+ @inherit_parent
@task
def __get__(self):
raise ResourceNotFound
+ @inherit_parent
def __create__(self):
# Alternatively, we might put all commands in a configuration file
@@ -222,7 +228,7 @@ class MetisForwarder(Forwarder, Service):
"""
command = METIS_DAEMON_BOOTSTRAP
- args = {'port' : self.port, 'log_file' : self.log_file,
+ args = {'port' : self.port, 'log_file' : self.log_file,
'cs_size' : self.cache_size, 'config' : self.config_file}
return BashTask(self.node, command, parameters = args)
@@ -232,7 +238,7 @@ class MetisForwarder(Forwarder, Service):
"""
command = METIS_DAEMON_STOP + '; ' + METIS_DAEMON_BOOTSTRAP
- args = {'port' : self.port, 'log_file' : self.log_file,
+ args = {'port' : self.port, 'log_file' : self.log_file,
'cs_size' : self.cache_size, 'config' : self.config_file}
return BashTask(self.node, command, parameters = args)
@@ -287,23 +293,16 @@ class MetisForwarder(Forwarder, Service):
face.id = 'conn{}'.format(self._nb_conn)
self._nb_conn += 1
- if face.protocol is FaceProtocol.ether:
- assert isinstance(face, L2Face), \
- 'Ethernet face should be instance of L2Face'
- cmd = CMD_ADD_CONNECTION_ETHER.format(face = face)
-
- elif face.protocol in (FaceProtocol.tcp4, FaceProtocol.tcp6):
- assert isinstance(face, L4Face), \
- "TCP/UDP face should be instance of L4Face"
- cmd = CMD_ADD_CONNECTION_L4.format(face = face,
- protocol = 'tcp')
-
- elif face.protocol in (FaceProtocol.udp4, FaceProtocol.udp6):
- assert isinstance(face, L4Face), \
- 'TCP/UDP face should be instance of L4Face'
- cmd = CMD_ADD_CONNECTION_L4.format(face = face,
- protocol = 'udp')
-
+ if face.protocol == 'ether':
+ cmd = CMD_ADD_CX_ETHER.format(face = face)
+ elif face.protocol == 'tcp4':
+ cmd = CMD_ADD_CX_L4_IPV4.format(face = face, protocol = 'tcp')
+ elif face.protocol == 'tcp6':
+ cmd = CMD_ADD_CX_L4_IPV6.format(face = face, protocol = 'tcp')
+ elif face.protocol == 'udp4':
+ cmd = CMD_ADD_CX_L4_IPV4.format(face = face, protocol = 'udp')
+ elif face.protocol == 'udp6':
+ cmd = CMD_ADD_CX_L4_IPV6.format(face = face, protocol = 'udp')
else:
raise ValueError('Unsupported face type for Metis')
@@ -324,13 +323,13 @@ class MetisForwarder(Forwarder, Service):
delete_task = EmptyTask()
if len(delete_cmds) > 0:
- cmds = '\n'.join('{} {}'.format(self._baseline, command)
+ cmds = '\n'.join('{} {}'.format(self._baseline, command)
for command in delete_cmds)
delete_task = BashTask(self.node, cmds)
create_task = EmptyTask()
if len(create_cmds) > 0:
- cmds = '\n'.join('{} {}'.format(self._baseline, command)
+ cmds = '\n'.join('{} {}'.format(self._baseline, command)
for command in create_cmds)
create_task = BashTask(self.node, cmds)
@@ -348,16 +347,16 @@ class MetisForwarder(Forwarder, Service):
create_task = BashTask(self.node, '\n'.join(create_cmds))
return delete_task > create_task
-
+
def _write_environment_file(self):
param_port = "PORT={port}"
param_log_file = "LOG_FILE={log_file}"
param_cs_capacity = "CS_SIZE={cs_size}"
param_config = "CONFIG={config}"
- env = [param_port.format(port = self.port),
+ env = [param_port.format(port = self.port),
param_log_file.format(log_file = self.log_file),
- param_cs_capacity.format(cs_size = self.cache_size),
+ param_cs_capacity.format(cs_size = self.cache_size),
param_config.format(config = self.config_file)]
environment_file = TextFile(filename = METIS_ETC_DEFAULT,
diff --git a/vicn/resource/icn/ccnx_simpleTrafficGenerator.py b/vicn/resource/icn/ccnx_simpleTrafficGenerator.py
index 221298fc..41a1b855 100644
--- a/vicn/resource/icn/ccnx_simpleTrafficGenerator.py
+++ b/vicn/resource/icn/ccnx_simpleTrafficGenerator.py
@@ -19,7 +19,7 @@
from netmodel.model.type import String
from vicn.core.attribute import Attribute, Multiplicity
from vicn.core.resource import Resource, EmptyResource
-from vicn.core.task import EmptyTask
+from vicn.core.task import EmptyTask, inherit_parent
from vicn.resource.icn.icn_application import ICN_SUITE_CCNX_1_0
from vicn.resource.node import Node
@@ -28,7 +28,7 @@ from vicn.resource.icn.ccnx_consumer_producer_test import CcnxProducerTest
class CcnxSimpleTrafficGenerator(Resource):
- prefix = Attribute(String,
+ prefix = Attribute(String,
description = "Routable prefix for the applications",
default = lambda self: self.default_name(),
mandatory = False)
@@ -44,11 +44,12 @@ class CcnxSimpleTrafficGenerator(Resource):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._sr = None
-
+
#--------------------------------------------------------------------------
# Resource lifecycle
#--------------------------------------------------------------------------
+ @inherit_parent
def __subresources__(self):
"""
Create the list of consumers and producers.
@@ -57,7 +58,7 @@ class CcnxSimpleTrafficGenerator(Resource):
sr = EmptyResource()
for producer in self.producers:
- producer = CcnxProducerTest(node = producer,
+ producer = CcnxProducerTest(node = producer,
owner = self,
prefixes = [self.prefix])
sr = sr | producer
diff --git a/vicn/resource/icn/central.py b/vicn/resource/icn/central.py
new file mode 100644
index 00000000..aa8ea357
--- /dev/null
+++ b/vicn/resource/icn/central.py
@@ -0,0 +1,236 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import logging
+import networkx as nx
+
+from netmodel.model.type import String
+from vicn.core.attribute import Attribute, Reference
+from vicn.core.exception import ResourceNotFound
+from vicn.core.resource import Resource
+from vicn.core.task import inline_task
+from vicn.resource.central import _get_l2_graph, MAP_ROUTING_STRATEGY
+from vicn.resource.icn.face import Face
+from vicn.resource.icn.route import Route
+
+log = logging.getLogger(__name__)
+
+
+def _get_icn_graph(manager, groups):
+ G = nx.Graph()
+ for group in groups:
+ # It's safer to iterate on node which we know are in the right groups,
+ # while it might not be the case for the forwarders...
+ for node in group.iter_by_type_str('node'):
+ G.add_node(node._state.uuid)
+ try:
+ forwarder = node.forwarder
+ except ResourceNotFound:
+ continue
+ for face in forwarder.faces:
+ other_face = manager.by_uuid(face._internal_data['sibling_face'])
+ other_node = other_face.node
+ if G.has_edge(node._state.uuid, other_node._state.uuid):
+ continue
+ map_node_face = { node._state.uuid: face._state.uuid,
+ other_node._state.uuid: other_face._state.uuid }
+ G.add_edge(node._state.uuid, other_node._state.uuid,
+ map_node_face = map_node_face)
+
+ return G
+
+#-------------------------------------------------------------------------------
+
+class ICNFaces(Resource):
+ """
+ Resource: ICNFaces
+
+ Centralized ICN face creation.
+ """
+ protocol_name = Attribute(String)
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ @inline_task
+ def __get__(self):
+ raise ResourceNotFound # always create faces
+
+ @inline_task
+ def __create__(self):
+ icn_faces = self._get_faces()
+ for face in icn_faces:
+ face.node.forwarder.faces << face
+
+ def __delete__(self):
+ raise NotImplementedError
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def _get_faces(self):
+ """
+ Face creation (heuristic: facemgr)
+
+ Requires: at least direct IP links
+ """
+ faces = list()
+ G = _get_l2_graph(self.groups)
+ for src_node_uuid, dst_node_uuid, data in G.edges_iter(data = True):
+ src_node = self._state.manager.by_uuid(src_node_uuid)
+ dst_node = self._state.manager.by_uuid(dst_node_uuid)
+
+ if not src_node.managed or not dst_node.managed:
+ continue
+
+ map_ = data['map_node_interface']
+ src = self._state.manager.by_uuid(map_[src_node_uuid])
+ dst = self._state.manager.by_uuid(map_[dst_node_uuid])
+
+ log.debug('{} -> {} ({} -> {})'.format(src_node_uuid,
+ dst_node_uuid, src.device_name, dst.device_name))
+
+ face_cls = Face.from_protocol(self.protocol_name)
+ if face_cls is None:
+ raise NotImplementedError
+
+ src_face = face_cls(protocol = self.protocol_name,
+ owner = self,
+ node = src_node,
+ src = src,
+ dst = dst)
+ dst_face = face_cls(protocol = self.protocol_name,
+ owner = self,
+ node = dst_node,
+ src = dst,
+ dst = src)
+
+
+ # We key the sibling face for easier building of the ICN graph
+ src_face._internal_data['sibling_face'] = dst_face._state.uuid
+ dst_face._internal_data['sibling_face'] = src_face._state.uuid
+
+ faces.append(src_face)
+ faces.append(dst_face)
+
+ return faces
+
+#------------------------------------------------------------------------------
+
+class ICNRoutes(Resource):
+ """
+ Resource: Routes
+
+ Centralized ICN route computation.
+ """
+
+ routing_strategy = Attribute(String)
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ @inline_task
+ def __get__(self):
+ raise ResourceNotFound # always create routes
+
+ @inline_task
+ def __create__(self):
+ icn_routes = self._get_icn_routes()
+ for route in icn_routes:
+ route.node.forwarder.routes << route
+
+ def __delete__(self):
+ raise NotImplementedError
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def _get_prefix_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 producer in node.producers:
+ origins[node_uuid].extend(producer.prefixes)
+ return origins
+
+ def _get_icn_routes(self):
+ strategy = MAP_ROUTING_STRATEGY.get(self.routing_strategy)
+
+ G = _get_icn_graph(self._state.manager, self.groups)
+ origins = self._get_prefix_origins()
+
+ routes = list()
+ for src, prefix, dst in strategy(G, origins):
+ src_node = self._state.manager.by_uuid(src)
+ if not src_node.managed:
+ continue
+ data = G.get_edge_data(src, dst)
+
+ map_ = data['map_node_face']
+ next_hop_face = map_[src]
+
+ route = Route(node = src,
+ owner = self,
+ prefix = prefix,
+ face = next_hop_face)
+ routes.append(route)
+
+ return routes
+
+#------------------------------------------------------------------------------
+
+class CentralICN(Resource):
+ """
+ Resource: CentralICN
+
+ Central ICN management (main resource)
+ """
+
+ # Choices: spt, max_flow
+ icn_routing_strategy = Attribute(String,
+ description = 'ICN routing strategy',
+ default = 'spt')
+ face_protocol = Attribute(String,
+ description = 'Protocol used to create faces',
+ default = 'ether')
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ def __after__(self):
+ """
+ We need to wait for IP configuration in order to be able to build
+ overload ICN faces, and producers for prefix origins.
+ """
+ return ('CentralIP',)
+
+ def __subresources__(self):
+ icn_faces = ICNFaces(owner = self, protocol_name = self.face_protocol,
+ groups = Reference(self, 'groups'))
+ icn_routes = ICNRoutes(owner = self,
+ routing_strategy = self.icn_routing_strategy,
+ groups = Reference(self, 'groups'))
+ return icn_faces > icn_routes
diff --git a/vicn/resource/icn/cicn.py b/vicn/resource/icn/cicn.py
new file mode 100644
index 00000000..76dafe0e
--- /dev/null
+++ b/vicn/resource/icn/cicn.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import re
+
+from netmodel.model.type import Integer, Bool
+from vicn.core.attribute import Attribute
+from vicn.core.exception import ResourceNotFound
+from vicn.core.requirement import Requirement
+from vicn.core.resource_mgr import wait_resource_task
+from vicn.core.task import async_task, task, BashTask, EmptyTask
+from vicn.core.task import inherit_parent
+from vicn.resource.icn.forwarder import Forwarder
+from vicn.resource.node import Node
+from vicn.resource.vpp.vpp_commands import CMD_VPP_ENABLE_PLUGIN
+
+CMD_VPP_CICN_GET = "vppctl_wrapper cicn show"
+CMD_VPP_ADD_ICN_ROUTE = 'vppctl_wrapper cicn cfg fib add prefix {route.prefix} face {route.face.id}'
+CMD_VPP_ADD_ICN_FACE = 'vppctl_wrapper cicn cfg face add local {face.src_ip}:{face.src_port} remote {face.dst_ip}:{face.dst_port}'
+
+CMD_VPP_CICN_GET_CACHE_SIZE = 'vppctl_wrapper cicn show | grep "CS entries" | grep -Eo "[0-9]+"'
+CMD_VPP_CICN_SET_CACHE_SIZE = 'vppctl_wrapper cicn control param cs size {self.cache_size}'
+_ADD_FACE_RETURN_FORMAT = "Face ID: [0-9]+"
+
+def check_face_id_return_format(data):
+ prog = re.compile(_ADD_FACE_RETURN_FORMAT)
+ return prog.match(data)
+
+def parse_face_id(data):
+ return data.partition(':')[2]
+
+class CICNForwarder(Forwarder):
+ """
+ NOTE: Based on the Vagrantfile, we recommend a node with mem=4096, cpu=4
+ """
+
+ node = Attribute(Node,
+ mandatory=True,
+ requirements = [Requirement('vpp')],
+ reverse_name='cicn')
+ numa_node = Attribute(Integer,
+ description = 'Numa node on which vpp will run',
+ default = None)
+ 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',
+ default = False)
+
+ #__packages__ = ['vpp-plugin-cicn']
+
+ def __after__(self):
+ return ['CentralICN']
+
+ @inherit_parent
+ def __get__(self):
+ 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,
+ lock = self.node.vpp.vppctl_lock, parse=parse)
+
+ @inherit_parent
+ def __create__(self):
+
+ #self.node.vpp.plugins.append("cicn")
+ lock = self.node.vpp.vppctl_lock
+ create_task = BashTask(self.node, CMD_VPP_ENABLE_PLUGIN,
+ {'plugin' : 'cicn'}, lock = lock)
+
+ face_task = EmptyTask()
+ route_task = EmptyTask()
+
+ def parse_face(rv, face):
+ if check_face_id_return_format(rv.stdout):
+ face.id = parse_face_id(rv.stdout)
+ return {}
+
+ for face in self.faces:
+ face_task = face_task > BashTask(self.node, CMD_VPP_ADD_ICN_FACE,
+ {'face':face},
+ parse = (lambda x : parse_face(x, face)), lock = lock)
+
+ if not self.routes:
+ from vicn.resource.icn.route import Route
+ for route in self._state.manager.by_type(Route):
+ if route.node is self.node:
+ self.routes.append(route)
+ for route in self.routes:
+ 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)
+
+ #--------------------------------------------------------------------------
+ # Attributes
+ #--------------------------------------------------------------------------
+
+ # Force local update
+
+ _add_faces = None
+ _remove_faces = None
+ _get_faces = None
+ _set_faces = None
+
+ _add_routes = None
+ _remove_routes = None
+ _get_routes = None
+ _set_routes = None
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def _set_cache_size(self):
+ return BashTask(self.node, CMD_VPP_CICN_SET_CACHE_SIZE, {'self': self},
+ lock = self.node.vpp.vppctl_lock)
+
+ def _get_cache_size(self):
+ def parse(rv):
+ return int(rv.stdout)
+ return BashTask(self.node, CMD_VPP_CICN_GET_CACHE_SIZE, parse=parse,
+ lock = self.node.vpp.vppctl_lock)
diff --git a/vicn/resource/icn/face.py b/vicn/resource/icn/face.py
index 641d10e7..4fd07883 100644
--- a/vicn/resource/icn/face.py
+++ b/vicn/resource/icn/face.py
@@ -16,36 +16,31 @@
# limitations under the License.
#
-from enum import Enum
-
-from netmodel.model.type import Integer, String, Bool
-from vicn.core.attribute import Attribute
-from vicn.core.requirement import Requirement
-from vicn.core.resource import Resource
-from vicn.resource.node import Node
-from vicn.resource.interface import Interface
+from netmodel.model.type import Integer, String, Bool, InetAddress
+from netmodel.model.object import ObjectMetaclass
+from vicn.core.attribute import Attribute
+from vicn.core.requirement import Requirement
+from vicn.core.resource import Resource
+from vicn.resource.interface import Interface
+from vicn.resource.node import Node
DEFAULT_ETHER_PROTO = 0x0801
-FMT_L4FACE = '{protocol.name}://{dst_ip}:{dst_port}/'
-FMT_L2FACE = '{protocol.name}://[{dst_mac}]/{src_nic.device_name}'
-
-class FaceProtocol(Enum):
- ether = 0
- ip4 = 1
- ip6 = 2
- tcp4 = 3
- tcp6 = 4
- udp4 = 5
- udp6 = 7
- app = 8
-
- @staticmethod
- def from_string(protocol):
- return getattr(FaceProtocol, protocol)
+DEFAULT_PORT = 6363
+
+FMT_L4FACE_IPV4 = '{protocol}://{dst.ip4_address}:{dst_port}/'
+FMT_L4FACE_IPV6 = '{protocol}://{dst.ip6_address}:{dst_port}/'
+FMT_L2FACE = '{protocol}://[{dst.mac_address}]/{src.device_name}'
#------------------------------------------------------------------------------
-class Face(Resource):
+class FaceMetaclass(ObjectMetaclass):
+ def __new__(mcls, name, bases, attrs):
+ cls = super(FaceMetaclass, mcls).__new__(mcls, name, bases, attrs)
+ if name != 'Face':
+ cls.register()
+ return cls
+
+class Face(Resource, metaclass=FaceMetaclass):
"""
Resource: Face
"""
@@ -57,6 +52,10 @@ class Face(Resource):
protocol = Attribute(String,
description = 'Face underlying protocol',
mandatory = True)
+
+ src = Attribute(Interface, mandatory = True)
+ dst = Attribute(Interface, mandatory = True)
+
id = Attribute(String, description = 'Local face ID',
ro = True)
@@ -75,6 +74,18 @@ class Face(Resource):
description = 'Flags for face creation with NFDC',
func = lambda self : self._lambda_nfdc_flags())
+ # map protocol -> face class
+ _map_protocol = dict()
+
+ @classmethod
+ def register(cls):
+ for protocol in cls.__protocols__:
+ Face._map_protocol[protocol] = cls
+
+ @classmethod
+ def from_protocol(cls, protocol):
+ return cls._map_protocol.get(protocol)
+
def __repr__(self):
flags = ''
if self.permanent:
@@ -95,7 +106,7 @@ class Face(Resource):
# NFD specifics
def _lambda_nfd_uri(self):
- raise NotImplementedError
+ return 'N/A' # raise NotImplementedError
def _lambda_nfdc_flags(self):
flags = ''
@@ -111,14 +122,11 @@ class Face(Resource):
class L2Face(Face):
- src_nic = Attribute(Interface,
- description = "Name of the network interface linked to the face",
- mandatory=True)
- dst_mac = Attribute(String, description = "destination MAC address",
- mandatory=True)
+ __protocols__ = ['ether']
+
ether_proto = Attribute(String,
description = "Ethernet protocol number used by the face",
- default=DEFAULT_ETHER_PROTO)
+ default = DEFAULT_ETHER_PROTO)
def _lambda_nfd_uri(self):
return self.format(FMT_L2FACE)
@@ -127,14 +135,13 @@ class L2Face(Face):
class L4Face(Face):
- ip_version = Attribute(Integer, description = "IPv4 or IPv6", default = 4)
- src_ip = Attribute(String, description = "local IP address",
- mandatory = True)
- src_port = Attribute(Integer, description = "local TCP/UDP port")
- dst_ip = Attribute(String, descrition = "remote IP address",
- mandatory=True)
+ __protocols__ = ['tcp4', 'tcp6', 'udp4', 'udp6']
+
+ src_port = Attribute(Integer, description = "local TCP/UDP port",
+ default = DEFAULT_PORT)
dst_port = Attribute(Integer, description = "remote TCP/UDP port",
- mandatory=True)
+ default = DEFAULT_PORT)
def _lambda_nfd_uri(self):
- return self.format(FMT_L4FACE)
+ fmt = FMT_L4FACE_IPV4 if self.protocol in ['tcp4', 'udp4'] else FMT_L4FACE_IPV6
+ return self.format(fmt)
diff --git a/vicn/resource/icn/forwarder.py b/vicn/resource/icn/forwarder.py
index 748532cf..e2ad944f 100644
--- a/vicn/resource/icn/forwarder.py
+++ b/vicn/resource/icn/forwarder.py
@@ -30,7 +30,7 @@ DEFAULT_CACHE_SIZE = 1000 # pk
DEFAULT_CACHE_POLICY = 'LRU'
DEFAULT_STRATEGY = 'best-route'
-class Forwarder(ICNApplication, ABC):
+class Forwarder(ICNApplication):
"""
Resource: Forwarder
"""
diff --git a/vicn/resource/icn/icn_application.py b/vicn/resource/icn/icn_application.py
index 817d9403..e8b6e19c 100644
--- a/vicn/resource/icn/icn_application.py
+++ b/vicn/resource/icn/icn_application.py
@@ -20,6 +20,7 @@ from vicn.resource.linux.application import LinuxApplication
from vicn.core.attribute import Attribute
from netmodel.model.type import Integer
+ICN_SUITE_HICN_1_0=0
ICN_SUITE_CCNX_1_0=1
ICN_SUITE_NDN=2
diff --git a/vicn/resource/icn/iping.py b/vicn/resource/icn/iping.py
index 0e04eadc..4494c79f 100644
--- a/vicn/resource/icn/iping.py
+++ b/vicn/resource/icn/iping.py
@@ -36,28 +36,28 @@ class IPing(ICNApplication):
__package_names__ = ["libicnet"]
- prefixes = Attribute(String,
+ prefixes = Attribute(String,
description = "name served by the ping server",
default = lambda self: self.default_name(),
mandatory = False,
multiplicity = Multiplicity.OneToMany)
node = Attribute(Node,
requirements=[
- Requirement("forwarder",
- capabilities = set(['ICN_SUITE_CCNX_1_0']),
+ Requirement("forwarder",
+ capabilities = set(['ICN_SUITE_CCNX_1_0']),
properties = {"protocol_suites" : ICN_SUITE_CCNX_1_0})
])
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
# Methods
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def __method_start__(self):
return self._build_command()
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
# Internal methods
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def default_name(self):
return ['/iping']
@@ -72,16 +72,16 @@ class IPingClient(IPing, Producer):
Resource: IPingClient
"""
- flood = Attribute(Bool, description = 'enable flood mode',
+ flood = Attribute(Bool, description = 'enable flood mode',
default = False)
count = Attribute(Integer, description = 'number of ping to send')
- interval = Attribute(Integer,
+ interval = Attribute(Integer,
description = 'interval between interests in ping mode')
size = Attribute(Integer, description = 'size of the interests')
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
# Internal methods
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def _build_command(self):
template = ["iPing_Client", "-l ccnx:{prefix}"]
@@ -110,9 +110,9 @@ class IPingServer(IPing, Consumer):
size = Attribute(Integer, description = "size of the payload")
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
# Internal methods
- #--------------------------------------------------------------------------
+ #--------------------------------------------------------------------------
def _build_command(self):
template = ["iPing_Server", "-l ccnx:{prefix}"]
diff --git a/vicn/resource/icn/ndnpingserver.py b/vicn/resource/icn/ndnpingserver.py
index f9cfa7cc..8fbb5fb6 100644
--- a/vicn/resource/icn/ndnpingserver.py
+++ b/vicn/resource/icn/ndnpingserver.py
@@ -54,7 +54,7 @@ class NDNPingServerBase(Producer):
default = lambda self: self._default_prefixes())
node = Attribute(requirements = [
- Requirement("forwarder",
+ Requirement("forwarder",
capabilities = set(['ICN_SUITE_NDN_1_0'])) ])
__package_names__ = ['ndnping']
diff --git a/vicn/resource/icn/nfd.py b/vicn/resource/icn/nfd.py
index c65fdeb8..1b7f39f7 100644
--- a/vicn/resource/icn/nfd.py
+++ b/vicn/resource/icn/nfd.py
@@ -22,6 +22,7 @@ import re
from vicn.core.exception import ResourceNotFound
from vicn.core.task import inline_task, BashTask
from vicn.core.task import ParseRegexTask
+from vicn.core.task import inherit_parent
from vicn.resource.icn.forwarder import Forwarder
from vicn.resource.icn.icn_application import ICN_SUITE_NDN
@@ -74,17 +75,20 @@ class NFD(Forwarder):
# Resource lifecycle
#--------------------------------------------------------------------------
+ @inherit_parent
@inline_task
def __get__(self):
# NFD is assumed not to exist
raise ResourceNotFound
+ @inherit_parent
def __create__(self):
# Modify the configuration file before running the forwarder service
conf = BashTask(self.node, CMD_SET_STRATEGY_CACHE, {'nfd': self})
- forwarder = Forwarder.__create__(self)
- return conf.then(forwarder)
+ forwarder = super().__create__()
+ return conf > forwarder
+ @inherit_parent
def __delete__(self):
raise NotImplementedError
diff --git a/vicn/resource/icn/producer.py b/vicn/resource/icn/producer.py
index 131097f2..36cbea77 100644
--- a/vicn/resource/icn/producer.py
+++ b/vicn/resource/icn/producer.py
@@ -16,6 +16,7 @@
# limitations under the License.
#
+from netmodel.model.key import Key
from netmodel.model.type import String
from vicn.resource.icn.icn_application import ICNApplication
from vicn.core.attribute import Attribute, Multiplicity
@@ -33,5 +34,6 @@ class Producer(ICNApplication):
description = 'Node on which the producer is installed',
mandatory = True,
multiplicity = Multiplicity.ManyToOne,
- reverse_name = 'producers',
- key = True)
+ reverse_name = 'producers')
+
+ __key__ = Key(node)
diff --git a/vicn/resource/icn/route.py b/vicn/resource/icn/route.py
index 0dc2ed2f..6efd909b 100644
--- a/vicn/resource/icn/route.py
+++ b/vicn/resource/icn/route.py
@@ -25,12 +25,10 @@ from vicn.resource.node import Node
class Route(Resource):
node = Attribute(Node, mandatory = True)
prefix = Attribute(String, mandatory = True)
- face = Attribute(Face, description = "face used to forward interests",
+ face = Attribute(Face, description = "face used to forward interests",
mandatory=True)
cost = Attribute(Integer, default=1)
def __repr__(self):
- return '<Route {} {} on node {}>'.format(self.prefix, self.face,
+ return '<Route {} {} on node {}>'.format(self.prefix, self.face,
self.node.name)
-
- __str__ = __repr__