diff options
author | Marcel Enguehard <mengueha+fdio@cisco.com> | 2017-07-19 11:26:26 +0200 |
---|---|---|
committer | Marcel Enguehard <mengueha+fdio@cisco.com> | 2017-07-19 11:51:26 +0000 |
commit | 3e6678f9c692553e8902da4d6fb1fe6c087db1f4 (patch) | |
tree | 580a46ca5de22a044319eabb295ad980d50589ec /vicn/resource/icn | |
parent | 08c4f765cf29dbd6e9a616c542552417eece14fc (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.py | 9 | ||||
-rw-r--r-- | vicn/resource/icn/ccnx_metis.py | 83 | ||||
-rw-r--r-- | vicn/resource/icn/ccnx_simpleTrafficGenerator.py | 9 | ||||
-rw-r--r-- | vicn/resource/icn/central.py | 236 | ||||
-rw-r--r-- | vicn/resource/icn/cicn.py | 139 | ||||
-rw-r--r-- | vicn/resource/icn/face.py | 87 | ||||
-rw-r--r-- | vicn/resource/icn/forwarder.py | 2 | ||||
-rw-r--r-- | vicn/resource/icn/icn_application.py | 1 | ||||
-rw-r--r-- | vicn/resource/icn/iping.py | 26 | ||||
-rw-r--r-- | vicn/resource/icn/ndnpingserver.py | 2 | ||||
-rw-r--r-- | vicn/resource/icn/nfd.py | 8 | ||||
-rw-r--r-- | vicn/resource/icn/producer.py | 6 | ||||
-rw-r--r-- | vicn/resource/icn/route.py | 6 |
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__ |