aboutsummaryrefslogtreecommitdiffstats
path: root/vicn/resource/icn
diff options
context:
space:
mode:
Diffstat (limited to 'vicn/resource/icn')
-rw-r--r--vicn/resource/icn/__init__.py0
-rw-r--r--vicn/resource/icn/ccnx_consumer_producer_test.py109
-rw-r--r--vicn/resource/icn/ccnx_keystore.py87
-rw-r--r--vicn/resource/icn/ccnx_metis.py368
-rw-r--r--vicn/resource/icn/ccnx_simpleTrafficGenerator.py106
-rw-r--r--vicn/resource/icn/consumer.py25
-rw-r--r--vicn/resource/icn/face.py140
-rw-r--r--vicn/resource/icn/forwarder.py64
-rw-r--r--vicn/resource/icn/icn_application.py37
-rw-r--r--vicn/resource/icn/icn_tools.py26
-rw-r--r--vicn/resource/icn/iping.py125
-rw-r--r--vicn/resource/icn/ndnpingserver.py76
-rw-r--r--vicn/resource/icn/nfd.py136
-rw-r--r--vicn/resource/icn/producer.py29
-rw-r--r--vicn/resource/icn/repo-ng.py25
-rw-r--r--vicn/resource/icn/route.py36
-rw-r--r--vicn/resource/icn/virtual-repo.py37
-rw-r--r--vicn/resource/icn/webserver.py29
18 files changed, 1455 insertions, 0 deletions
diff --git a/vicn/resource/icn/__init__.py b/vicn/resource/icn/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/vicn/resource/icn/__init__.py
diff --git a/vicn/resource/icn/ccnx_consumer_producer_test.py b/vicn/resource/icn/ccnx_consumer_producer_test.py
new file mode 100644
index 00000000..f682657d
--- /dev/null
+++ b/vicn/resource/icn/ccnx_consumer_producer_test.py
@@ -0,0 +1,109 @@
+#!/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.
+#
+
+from netmodel.model.type import String
+from vicn.core.attribute import Attribute, Multiplicity
+from vicn.core.requirement import Requirement
+from vicn.core.task import BashTask
+from vicn.resource.icn.icn_application import ICN_SUITE_CCNX_1_0
+from vicn.resource.icn.consumer import Consumer
+from vicn.resource.icn.producer import Producer
+from vicn.resource.node import Node
+
+class CcnxConsumerTest(Consumer):
+ """
+ Resource: CcnxConsumerTest
+
+ Test consumer exchanging dummy data.
+ """
+
+ __package_names__ = ["libconsumer-producer-ccnx"]
+
+ prefixes = Attribute(String,
+ description = "Name served by the producer server test",
+ default = lambda self: self.default_name(),
+ mandatory = False,
+ multiplicity = Multiplicity.OneToMany)
+ node = Attribute(Node,
+ requirements=[
+ Requirement("forwarder",
+ capabilities = set(['ICN_SUITE_CCNX_1_0']),
+ properties = {"protocol_suites" : ICN_SUITE_CCNX_1_0})
+ ])
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def default_name(self):
+ return ['/ccnxtest']
+
+ def _def_protocol_suite(self):
+ return ICN_SUITE_CCNX_1_0
+
+ #--------------------------------------------------------------------------
+ # Methods
+ #--------------------------------------------------------------------------
+
+ def __method_start__(self):
+ template = ["consumer-test", " ccnx:{prefix}"]
+ params = {'prefix' : self.prefixes[0]}
+ return BashTask(self.node, ' '.join(template), parameters = params)
+
+ def __method_stop__(self):
+ raise NotImplementedError
+
+#------------------------------------------------------------------------------
+
+class CcnxProducerTest(Producer):
+ """
+ Resource: CcnxConsumerTest
+
+ Test producer exchanging dummy data.
+ """
+
+ __package_names__ = ["libconsumer-producer-ccnx"]
+
+ node = Attribute(Node,
+ requirements = [Requirement("forwarder",
+ capabilities = set(['ICN_SUITE_CCNX_1_0']),
+ properties = {"protocol_suites" : ICN_SUITE_CCNX_1_0})])
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def default_name(self):
+ return ['/ccnxtest']
+
+ def _def_protocol_suite(self):
+ return ICN_SUITE_CCNX_1_0
+
+ #--------------------------------------------------------------------------
+ # Methods
+ #--------------------------------------------------------------------------
+
+ def __method_start__(self):
+ template = ["producer-test", " ccnx:{prefix}"]
+ params = {'prefix' : self.prefixes[0]}
+
+ return BashTask(self.node, ' '.join(template), parameters = params)
+
+ def __method_stop__(self):
+ raise NotImplementedError
+
diff --git a/vicn/resource/icn/ccnx_keystore.py b/vicn/resource/icn/ccnx_keystore.py
new file mode 100644
index 00000000..ddd87019
--- /dev/null
+++ b/vicn/resource/icn/ccnx_keystore.py
@@ -0,0 +1,87 @@
+#!/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.
+#
+
+from netmodel.model.type import String, Integer
+from vicn.core.attribute import Attribute, Reference
+from vicn.core.task import BashTask
+from vicn.resource.linux.file import File
+from vicn.resource.linux.package_manager import Packages
+
+METIS_KEYSTORE_CREATE = ('parc-publickey -c {filename} {password} '
+ '{subject_name} {size} {validity}')
+
+# FIXME default passwords, not very sensitive
+DEFAULT_KEYSTORE_FILE = "keystore.pkcs12"
+DEFAULT_KEYSTORE_PASSWD = "password"
+DEFAULT_KEYSTORE_VALIDITY = 365
+DEFAULT_KEYSTORE_SUBJ = "password"
+DEFAULT_KEYSTORE_KEYLENGTH = 2048
+
+class MetisKeystore(File):
+ """
+ Resource: MetisKeystore
+ """
+
+ filename = Attribute(String, description = "File containing the keystore",
+ default = DEFAULT_KEYSTORE_FILE, mandatory=False)
+ password = Attribute(String,
+ description = "Password for the keystore file",
+ default = DEFAULT_KEYSTORE_PASSWD)
+ subject_name = Attribute(String,
+ description = "Subject name for the keystore",
+ default = DEFAULT_KEYSTORE_SUBJ)
+ validity = Attribute(String,
+ description = "Validity period of the keystore",
+ default = DEFAULT_KEYSTORE_VALIDITY)
+ size = Attribute(Integer, description = 'Length of the keys',
+ default = DEFAULT_KEYSTORE_KEYLENGTH)
+
+ __package_names__ = ['libparc']
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ def __subresources__(self):
+ packages = Packages(node=Reference(self, 'node'),
+ names=self._get_package_names(), owner=self)
+ return packages
+
+ def __create__(self):
+ args = {'filename' : self.filename, 'password' : self.password,
+ 'subject_name' : self.subject_name, 'validity' : self.validity,
+ 'size' : self.size}
+ return BashTask(self.node, METIS_KEYSTORE_CREATE, args)
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def _get_package_names(self):
+ package_names = list()
+ for base in self.__class__.mro():
+ if not '__package_names__' in vars(base):
+ continue
+ package_names.extend(getattr(base, '__package_names__'))
+ return package_names
+
+
+ def format_baseline(self, baseline):
+ return baseline.format(keystore_file=self.filename, password=self.password)
+
+
diff --git a/vicn/resource/icn/ccnx_metis.py b/vicn/resource/icn/ccnx_metis.py
new file mode 100644
index 00000000..ead9b9bf
--- /dev/null
+++ b/vicn/resource/icn/ccnx_metis.py
@@ -0,0 +1,368 @@
+#!/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.
+#
+
+from functools import wraps
+
+from netmodel.model.type import String, Integer, Bool
+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.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.forwarder import Forwarder
+from vicn.resource.linux.file import TextFile
+from vicn.resource.linux.service import Service
+
+METIS_CONTROL_BASELINE = (
+ 'metis_control --keystore {keystore_file} --password {password}')
+
+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} '
+ '{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_ROUTE = 'add route {route.face.id} ccnx:{route.prefix} {route.cost}'
+METIS_DAEMON_BOOTSTRAP = (
+ 'metis_daemon --port {port} --daemon --log-file {log_file} '
+ '--capacity {cs_size} --config {config}')
+METIS_DAEMON_STOP = "killall -9 metis_daemon"
+
+BASE_CONN_NAME = "conn"
+
+METIS_DEFAULT_PORT = 9596
+
+METIS_ETC_DEFAULT = "/etc/default/metis-forwarder"
+
+#------------------------------------------------------------------------------
+# Listeners
+#------------------------------------------------------------------------------
+
+class MetisListener:
+
+ def __init__(self, protocol):
+ self.protocol = protocol
+
+ @staticmethod
+ def listener_from_face(face):
+ if face.protocol is FaceProtocol.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)
+ 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):
+ 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,
+ conn_id = conn_id)
+
+ def __eq__(self, other):
+ return (isinstance(other, MetisEtherListener)
+ and (other.src_nic == self.src_nic)
+ and (other.ether_proto == self.ether_proto))
+
+ def __ne__(self, other):
+ return ((not isinstance(other, MetisEtherListener))
+ or (other.src_nic != self.src_nic)
+ or (other.ether_proto != self.ether_proto))
+
+class MetisL4Listener(MetisListener):
+
+ def __init__(self, protocol, src_ip, src_port):
+ super().__init__(protocol)
+ self.src_ip = src_ip
+ self.src_port = src_port
+
+ def _get_proto_as_str(self):
+ if self.protocol in (FaceProtocol.tcp4, FaceProtocol.tcp6):
+ return "tcp"
+ elif self.protocol in (FaceProtocol.udp4, FaceProtocol.udp6):
+ return "udp"
+
+ def get_setup_command(self, conn_id):
+ infos = '{} {}'.format(self.src_ip, self.src_port)
+
+ return CMD_ADD_LISTENER_L4.format(protocol = self._get_proto_as_str(),
+ conn_id = conn_id, infos = infos)
+
+ def __eq__(self, other):
+ return (isinstance(other, MetisL4Listener) and
+ self.protocol == other.protocol and
+ self.src_ip == other.src_ip and
+ self.src_port == other.src_port)
+
+#------------------------------------------------------------------------------
+
+class MetisForwarder(Forwarder, Service):
+ __capabilities__ = set(['ICN_SUITE_CCNX_1_0'])
+ __package_names__ = ['metis-forwarder']
+ __service_name__ = "metis-forwarder"
+
+ log_file = Attribute(String, description = 'File for metis logging',
+ default = '/tmp/ccnx-metis.log') # '/dev/null')
+ port = Attribute(Integer, description = 'TCP port for metis',
+ default = 9695)
+ 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')
+
+ #--------------------------------------------------------------------------
+ # Constructor and Accessors
+ #--------------------------------------------------------------------------
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._nb_conn = 0
+ self._listeners = []
+ self._listeners_idx = 0
+ self.keystore = None
+
+ # Cache
+ self._faces = set()
+ self._routes = set()
+
+ # Internal subresources
+ self._config = None
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ def __after__(self):
+ return ('CentralICN',)
+
+ def __subresources__(self):
+ self.keystore = MetisKeystore(node = self.node, owner = self)
+ self.env_file = self._write_environment_file()
+ return self.keystore | self.env_file
+
+ @task
+ def __get__(self):
+ raise ResourceNotFound
+
+ def __create__(self):
+
+ # Alternatively, we might put all commands in a configuration file
+ # before starting the forwarder. In that case, we need to restart it if
+ # it is already started.
+ # XXX Need to schedule subresource before and after some other tasks
+
+ _, faces = self._cmd_create_faces()
+ _, routes = self._cmd_create_routes()
+
+ cfg = list()
+ cfg.append('add listener tcp local0 127.0.0.1 9695')
+ cfg.extend(faces)
+ cfg.extend(routes)
+
+ self._config = TextFile(filename = self.config_file,
+ node = self.node,
+ owner = self,
+ content = '\n'.join(cfg),
+ overwrite = True)
+
+ self._state.manager.commit_resource(self._config)
+
+ start_or_restart = self.__method_restart__()
+
+ return wait_resource_task(self._config) > start_or_restart
+
+ #--------------------------------------------------------------------------
+ # Attributes
+ #--------------------------------------------------------------------------
+
+ # Force local management of faces and routes
+
+ _add_faces = None
+ _remove_faces = None
+ _get_faces = None
+ _set_faces = None
+
+ _add_routes = None
+ _remove_routes = None
+ _get_routes = None
+ _set_routes = None
+
+ #--------------------------------------------------------------------------
+ # Method helpers
+ #--------------------------------------------------------------------------
+
+ def _start_as_daemon(self):
+ """
+ Start the metis forwarder as normal daemon
+ """
+
+ command = METIS_DAEMON_BOOTSTRAP
+ 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)
+
+ def _restart_as_daemon(self):
+ """
+ Restart the metis forwarder as normal daemon
+ """
+
+ command = METIS_DAEMON_STOP + '; ' + METIS_DAEMON_BOOTSTRAP
+ 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)
+
+ def _start_as_service(self):
+ """
+ Start the metis forwarder as service managed by systemd
+ """
+ return super().__method_start__()
+
+ def _restart_as_service(self):
+ """
+ Restart the metis forwarder as service managed by systemd
+ """
+ return super().__method_restart__()
+
+ #--------------------------------------------------------------------------
+ # Methods
+ #--------------------------------------------------------------------------
+
+ def __method_start__(self):
+ return self._start_as_service()
+
+ def __method_restart__(self):
+ return self._restart_as_service()
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def _cmd_create_faces(self):
+ """Returns the list of commands used to update faces (delete and
+ create).
+
+ We need two lists because some delete might need to occur before create.
+ This function is used to populate the config file and also alter the
+ configuration of the forwarder live. It might be possible to further
+ optimize but keeping the two separate seems important, since delete is
+ only used for an already running metis.
+ """
+ create_cmds = list()
+ delete_cmds = list()
+
+ for face in self.faces:
+ listener = MetisListener.listener_from_face(face)
+ if listener not in self._listeners:
+ self._listeners.append(listener)
+ conn_id = self._listeners_idx
+ self._listeners_idx += 1
+ cmd = listener.get_setup_command(conn_id)
+ create_cmds.append(cmd)
+
+ 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')
+
+ else:
+ raise ValueError('Unsupported face type for Metis')
+
+ create_cmds.append(cmd)
+
+ return (delete_cmds, create_cmds)
+
+ def _cmd_create_routes(self):
+ create_cmds = list()
+ delete_cmds = list()
+ for route in self.routes:
+ cmd = CMD_ADD_ROUTE.format(route = route)
+ create_cmds.append(cmd)
+ return (delete_cmds, create_cmds)
+
+ def _task_create_faces(self):
+ delete_cmds, create_cmds = self._cmd_create_faces()
+
+ delete_task = EmptyTask()
+ if len(delete_cmds) > 0:
+ 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)
+ for command in create_cmds)
+ create_task = BashTask(self.node, cmds)
+
+ return delete_task > create_task
+
+ def _task_create_routes(self):
+ delete_cmds, create_cmds = self._cmd_create_routes()
+
+ delete_task = EmptyTask()
+ if len(delete_cmds) > 0:
+ delete_task = BashTask(self.node, "\n".join(delete_cmds))
+
+ create_task = EmptyTask()
+ if len(create_cmds) > 0:
+ 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),
+ param_log_file.format(log_file = self.log_file),
+ param_cs_capacity.format(cs_size = self.cache_size),
+ param_config.format(config = self.config_file)]
+
+ environment_file = TextFile(filename = METIS_ETC_DEFAULT,
+ node = self.node,
+ owner = self,
+ overwrite = True,
+ content = '\n'.join(env))
+ return environment_file
diff --git a/vicn/resource/icn/ccnx_simpleTrafficGenerator.py b/vicn/resource/icn/ccnx_simpleTrafficGenerator.py
new file mode 100644
index 00000000..221298fc
--- /dev/null
+++ b/vicn/resource/icn/ccnx_simpleTrafficGenerator.py
@@ -0,0 +1,106 @@
+#!/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.
+#
+
+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.resource.icn.icn_application import ICN_SUITE_CCNX_1_0
+from vicn.resource.node import Node
+
+from vicn.resource.icn.ccnx_consumer_producer_test import CcnxConsumerTest
+from vicn.resource.icn.ccnx_consumer_producer_test import CcnxProducerTest
+
+class CcnxSimpleTrafficGenerator(Resource):
+
+ prefix = Attribute(String,
+ description = "Routable prefix for the applications",
+ default = lambda self: self.default_name(),
+ mandatory = False)
+ consumers = Attribute(Node,
+ multiplicity = Multiplicity.OneToMany)
+ producers = Attribute(Node,
+ multiplicity = Multiplicity.OneToMany)
+
+ #--------------------------------------------------------------------------
+ # Constructor and Accessors
+ #--------------------------------------------------------------------------
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._sr = None
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ def __subresources__(self):
+ """
+ Create the list of consumers and producers.
+ For each of them, assign a different namespace under the same prefix.
+ """
+
+ sr = EmptyResource()
+ for producer in self.producers:
+ producer = CcnxProducerTest(node = producer,
+ owner = self,
+ prefixes = [self.prefix])
+ sr = sr | producer
+ for consumer in self.consumers:
+ full_prefix = self.prefix
+ consumer = CcnxConsumerTest(node = consumer,
+ owner = self,
+ prefixes = [full_prefix])
+ sr = sr | consumer
+ self._sr = sr
+ return sr
+
+ #--------------------------------------------------------------------------
+ # Methods
+ #--------------------------------------------------------------------------
+
+ def __method_start__(self):
+ if self._sr is None:
+ return
+
+ tasks = EmptyTask()
+ for sr in self._sr:
+ sr_task = sr.__method_start__()
+ tasks = tasks | sr_task
+ return tasks
+
+ def __method_stop__(self):
+ if self._sr is None:
+ return
+
+ tasks = EmptyTask()
+ for sr in self._sr:
+ sr_task = sr.__method_stop__()
+ tasks = tasks | sr_task
+ return tasks
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def default_name(self):
+ return ['/ccnxtest']
+
+ def _def_protocol_suite(self):
+ return ICN_SUITE_CCNX_1_0
+
diff --git a/vicn/resource/icn/consumer.py b/vicn/resource/icn/consumer.py
new file mode 100644
index 00000000..8c4c5e76
--- /dev/null
+++ b/vicn/resource/icn/consumer.py
@@ -0,0 +1,25 @@
+#!/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.
+#
+
+from vicn.resource.icn.icn_application import ICNApplication
+
+class Consumer(ICNApplication):
+ """
+ Resource: Consumer
+ """
+ pass
diff --git a/vicn/resource/icn/face.py b/vicn/resource/icn/face.py
new file mode 100644
index 00000000..db72730d
--- /dev/null
+++ b/vicn/resource/icn/face.py
@@ -0,0 +1,140 @@
+#!/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.
+#
+
+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
+
+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)
+
+#------------------------------------------------------------------------------
+
+class Face(Resource):
+ """
+ Resource: Face
+ """
+
+ node = Attribute(Node, mandatory = True,
+ requirements = [
+ Requirement('forwarder')
+ ])
+ protocol = Attribute(String,
+ description = 'Face underlying protocol',
+ mandatory = True)
+ id = Attribute(String, description = 'Local face ID',
+ ro = True)
+
+ # Cisco's extensions
+ wldr = Attribute(Bool, description = 'flag: WLDR enabled',
+ default = False)
+ x2 = Attribute(Bool, description = 'flag: X2 face',
+ default = False)
+
+ # NFD extensions
+ permanent = Attribute(Bool, description = 'flag: permanent face',
+ default = True)
+ nfd_uri = Attribute(String, description = 'Face uri',
+ func = lambda self : self._lambda_nfd_uri())
+ nfdc_flags = Attribute(String,
+ description = 'Flags for face creation with NFDC',
+ func = lambda self : self._lambda_nfdc_flags())
+
+ def __repr__(self):
+ flags = ''
+ if self.permanent:
+ flags += 'permanent '
+ if self.wldr:
+ flags += 'wldr '
+ if self.x2:
+ flags += 'x2 '
+ sibling_face_name = self.data.get('sibling_face', None)
+ sibling_face = self._state.manager.by_name(sibling_face_name) \
+ if sibling_face_name else None
+ dst_node = sibling_face.node.name if sibling_face else None
+ return '<Face {} {} on node {} -- to node {}>'.format(
+ self.nfd_uri, flags, self.node.name, dst_node)
+
+ __str__ = __repr__
+
+ # NFD specifics
+
+ def _lambda_nfd_uri(self):
+ raise NotImplementedError
+
+ def _lambda_nfdc_flags(self):
+ flags = ''
+ if self.permanent:
+ flags += '-P '
+ if self.wldr:
+ flags += '-W '
+ if self.x2:
+ flags += '-X '
+ return flags
+
+#------------------------------------------------------------------------------
+
+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)
+ ether_proto = Attribute(String,
+ description = "Ethernet protocol number used by the face",
+ default=DEFAULT_ETHER_PROTO)
+
+ def _lambda_nfd_uri(self):
+ return self.format(FMT_L2FACE)
+
+#------------------------------------------------------------------------------
+
+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)
+ dst_port = Attribute(Integer, description = "remote TCP/UDP port",
+ mandatory=True)
+
+ def _lambda_nfd_uri(self):
+ return self.format(FMT_L4FACE)
diff --git a/vicn/resource/icn/forwarder.py b/vicn/resource/icn/forwarder.py
new file mode 100644
index 00000000..a719caf7
--- /dev/null
+++ b/vicn/resource/icn/forwarder.py
@@ -0,0 +1,64 @@
+#!/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.
+#
+
+from abc import ABC
+from enum import Enum
+
+from netmodel.model.type import Integer, String
+from vicn.core.attribute import Attribute, Multiplicity
+from vicn.core.resource import FactoryResource
+from vicn.resource.icn.icn_application import ICNApplication
+from vicn.resource.icn.face import Face
+from vicn.resource.icn.route import Route
+
+DEFAULT_CACHE_SIZE = 1000 # pk
+DEFAULT_CACHE_POLICY = 'LRU'
+DEFAULT_STRATEGY = 'best-route'
+
+class Forwarder(ICNApplication, ABC):
+ """
+ Resource: Forwarder
+ """
+
+ __type__ = FactoryResource
+
+ faces = Attribute(Face, description = 'ICN ffaces of the forwarder',
+ multiplicity = Multiplicity.OneToMany,
+ reverse_name = 'forwarder')
+ routes = Attribute(Route, description = 'Routes in the ICN FIB',
+ multiplicity = Multiplicity.OneToMany,
+ reverse_name = 'forwarder')
+ cache_size = Attribute(Integer,
+ description = 'Size of the cache (in chunks)',
+ default = DEFAULT_CACHE_SIZE)
+ cache_policy = Attribute(String, description = 'Cache policy',
+ default = DEFAULT_CACHE_POLICY)
+ 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
+
+ node = Attribute(
+ reverse_name = 'forwarder',
+ reverse_description = 'ICN forwarder attached to the node',
+ reverse_auto = True,
+ multiplicity = Multiplicity.OneToOne)
diff --git a/vicn/resource/icn/icn_application.py b/vicn/resource/icn/icn_application.py
new file mode 100644
index 00000000..5abee3c5
--- /dev/null
+++ b/vicn/resource/icn/icn_application.py
@@ -0,0 +1,37 @@
+#!/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.
+#
+
+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
+
+class ICNApplication(LinuxApplication):
+ """
+ Resource: ICNApplication
+ """
+
+ protocol_suites = Attribute(Integer,
+ description = 'Protocol suites supported by the application',
+ default = lambda self: self._def_protocol_suite())
+
+ def _def_protocol_suite(self):
+ return -1
+
diff --git a/vicn/resource/icn/icn_tools.py b/vicn/resource/icn/icn_tools.py
new file mode 100644
index 00000000..54823719
--- /dev/null
+++ b/vicn/resource/icn/icn_tools.py
@@ -0,0 +1,26 @@
+#!/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.
+#
+
+from vicn.resource.icn.icn_application import ICNApplication
+
+class ICNTools(ICNApplication):
+ """
+ Resource: ICNTools
+ """
+
+ __package_names__ = ['libconsumer-producer-ccnx']
diff --git a/vicn/resource/icn/iping.py b/vicn/resource/icn/iping.py
new file mode 100644
index 00000000..0e04eadc
--- /dev/null
+++ b/vicn/resource/icn/iping.py
@@ -0,0 +1,125 @@
+#!/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.
+#
+
+from netmodel.model.type import Integer, String, Bool
+from vicn.core.attribute import Attribute, Multiplicity
+from vicn.core.requirement import Requirement
+from vicn.core.task import BashTask
+from vicn.resource.icn.icn_application import ICNApplication
+from vicn.resource.icn.icn_application import ICN_SUITE_CCNX_1_0
+from vicn.resource.icn.producer import Producer
+from vicn.resource.icn.consumer import Consumer
+from vicn.resource.node import Node
+
+DEFAULT_PING_PAYLOAD_SIZE = 64
+DEFAULT_PING_COUNT = 100
+
+class IPing(ICNApplication):
+ """
+ Resource: IPingClient
+ """
+
+ __package_names__ = ["libicnet"]
+
+ 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']),
+ 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']
+
+ def _def_protocol_suite(self):
+ return ICN_SUITE_CCNX_1_0
+
+#------------------------------------------------------------------------------
+
+class IPingClient(IPing, Producer):
+ """
+ Resource: IPingClient
+ """
+
+ flood = Attribute(Bool, description = 'enable flood mode',
+ default = False)
+ count = Attribute(Integer, description = 'number of ping to send')
+ 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}"]
+ params={'prefix' : self.prefixes[0]}
+
+ if self.flood:
+ template.append("-f")
+ else:
+ template.append("-p") #Ping mode
+
+ if self.count:
+ template.append("-c {count}")
+ params["count"] = self.count
+ if self.size:
+ template.append("-s {size}")
+ params['size'] = self.size
+ if self.interval:
+ template.append("-i {interval}")
+ params['interval'] = self.interval
+
+ return BashTask(self.node, ' '.join(template), parameters=params)
+
+#------------------------------------------------------------------------------
+
+class IPingServer(IPing, Consumer):
+
+ size = Attribute(Integer, description = "size of the payload")
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def _build_command(self):
+ template = ["iPing_Server", "-l ccnx:{prefix}"]
+ params={'prefix' : self.prefixes[0]}
+
+ if self.size:
+ template.append("-s {size}")
+ params['size'] = self.size
+
+ return BashTask(self.node, ' '.join(template), parameters=params)
diff --git a/vicn/resource/icn/ndnpingserver.py b/vicn/resource/icn/ndnpingserver.py
new file mode 100644
index 00000000..da13f59b
--- /dev/null
+++ b/vicn/resource/icn/ndnpingserver.py
@@ -0,0 +1,76 @@
+#!/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.
+#
+
+from netmodel.model.type import String
+from vicn.core.attribute import Attribute
+from vicn.core.requirement import Requirement
+from vicn.core.task import BashTask
+from vicn.resource.icn.producer import Producer
+from vicn.resource.linux.service import Service
+
+TPL_DEFAULT_PREFIX='/ndn/{node.name}'
+
+FN_ETC_DEFAULT='/etc/default/ndnping'
+
+TPL_ETC_DEFAULT='''
+# defaults for ndnping server
+
+# Prefix should be set to a valid value
+PREFIX="/ndn/server"
+
+FLAGS=""
+'''
+
+CMD_START = 'ndnpingserver {prefix} &'
+
+class NDNPingServerBase(Producer):
+ """NDNPingServer Resource
+
+ This NDNPingServer resource wraps a NDN ping server
+
+ Attributes:
+ prefixes (List[str]) : (overloaded) One-element list containing the
+ prefix on which the ping server is listening.
+
+ TODO:
+ - ndnpingserver only supports a single prefix.
+ """
+ prefixes = Attribute(String,
+ default = lambda self: self._default_prefixes())
+
+ node = Attribute(requirements = [
+ Requirement("forwarder",
+ capabilities = set(['ICN_SUITE_CCNX_1_0'])) ])
+
+ __package_names__ = ['ndnping']
+
+ def _default_prefixes(self):
+ return [self.format(TPL_DEFAULT_PREFIX)]
+
+#------------------------------------------------------------------------------
+
+class NDNPingServer(NDNPingServerBase):
+
+ def __method_start__(self):
+ return BashTask(self.node, CMD_START)
+
+#------------------------------------------------------------------------------
+
+class NDNPingService(NDNPingServerBase, Service):
+ __package_names__ = ['ndnping']
+ __service_name__ = 'ndnping'
diff --git a/vicn/resource/icn/nfd.py b/vicn/resource/icn/nfd.py
new file mode 100644
index 00000000..c65fdeb8
--- /dev/null
+++ b/vicn/resource/icn/nfd.py
@@ -0,0 +1,136 @@
+#!/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 re
+
+from vicn.core.exception import ResourceNotFound
+from vicn.core.task import inline_task, BashTask
+from vicn.core.task import ParseRegexTask
+from vicn.resource.icn.forwarder import Forwarder
+from vicn.resource.icn.icn_application import ICN_SUITE_NDN
+
+log = logging.getLogger(__name__)
+
+NFD_CONF_FILE = "/etc/ndn/nfd.conf"
+
+CMD_SET_STRATEGY_CACHE = '\n'.join([
+ 'sed -i "s/^.*cs_max_packets .*$/ cs_max_packets {nfd.cache_size}/" ' \
+ '{conf_file}',
+ 'sed -i "0,/\/ / s/\/localhost\/nfd\/strategy\/.*/' \
+ '\/localhost\/nfd\/strategy\/{nfd.fw_strategy}/" {conf_file}',
+ 'service nfd restart'])
+CMD_RESET_CACHE = '''
+sed -i "s/^.*cs_max_packets .*$/ cs_max_packets 65536/" {conf_file}
+service nfd restart
+'''
+
+CMD_ADD_ROUTE = 'nfdc register {route.prefix} {route.face.nfd_uri}'
+# or: nfdc register {route.prefix} {route.face.id}
+
+CMD_REMOVE_ROUTE = 'nfdc unregister {route.prefix} {route.face.nfd_uri}'
+# or: nfdc unregister {route.prefix} {route.face.id}
+
+CMD_ADD_FACE = 'nfdc create {face.nfdc_flags} {face.nfd_uri}'
+
+CMD_REMOVE_FACE = 'nfdc destroy {face.id}'
+# or: nfdc destroy {face.nfd_uri}
+
+# FIXME redundant with Forwarder.FaceType
+layer_2_protocols = ["udp", "udp4", "tcp", "tcp4", "ether"]
+
+NFD_DEFAULT_PORT = 6363
+
+# Regular expressions used for parsing nfdc results
+STR_ADD_FACE = ('Face creation succeeded: ControlParameters\(FaceId: '
+ '(?P<id>.*?), Uri: (?P<face_uri>.*?), \)')
+RX_ADD_FACE = re.compile(STR_ADD_FACE)
+
+class NFD(Forwarder):
+ """
+ Resource: NFD
+ """
+
+ __capabilities__ = set(['ICN_SUITE_NDN'])
+ __service_name__ = 'nfd'
+ __package_names__ = ['nfd']
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ @inline_task
+ def __get__(self):
+ # NFD is assumed not to exist
+ raise ResourceNotFound
+
+ 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)
+
+ def __delete__(self):
+ raise NotImplementedError
+
+ #--------------------------------------------------------------------------
+ # Attributes
+ #--------------------------------------------------------------------------
+
+ @inline_task
+ def _get_routes(self):
+ return {'routes': list()}
+
+ @inline_task
+ def _add_routes(self, route):
+ return BashTask(self.node, CMD_ADD_ROUTE, {'route': route})
+
+ @inline_task
+ def _remove_routes(self, route):
+ return BashTask(self.node, CMD_REMOVE_ROUTE, {'route': route})
+
+ @inline_task
+ def _get_faces(self):
+ return {'faces': list()}
+
+ @inline_task
+ def _add_faces(self, face):
+ add_face = BashTask(self.node, CMD_ADD_FACE, {'face': face})
+ set_face_id = ParseRegexTask(RX_ADD_FACE)
+ return add_face.compose(set_face_id)
+
+ @inline_task
+ def _remove_faces(self, face):
+ return BashTask(self.node, CMD_REMOVE_FACE, {'face': face})
+
+ #--------------------------------------------------------------------------
+ # Methods
+ #--------------------------------------------------------------------------
+
+ def __method_reset_cache__(self, conf_file):
+ return BashTask(self.node, CMD_RESET_CACHE, {'conf_file': conf_file})
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def _get_default_port(self):
+ return NFD_DEFAULT_PORT
+
+ def _def_protocol_suite(self):
+ return ICN_SUITE_NDN
diff --git a/vicn/resource/icn/producer.py b/vicn/resource/icn/producer.py
new file mode 100644
index 00000000..23434ebd
--- /dev/null
+++ b/vicn/resource/icn/producer.py
@@ -0,0 +1,29 @@
+#!/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.
+#
+
+from netmodel.model.type import String
+from vicn.resource.icn.icn_application import ICNApplication
+from vicn.core.attribute import Attribute, Multiplicity
+
+class Producer(ICNApplication):
+ """
+ Resource: Producer
+ """
+
+ prefixes = Attribute(String, description = 'List of served prefixes',
+ multiplicity = Multiplicity.OneToMany)
diff --git a/vicn/resource/icn/repo-ng.py b/vicn/resource/icn/repo-ng.py
new file mode 100644
index 00000000..7b654a6a
--- /dev/null
+++ b/vicn/resource/icn/repo-ng.py
@@ -0,0 +1,25 @@
+#!/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.
+#
+
+from vicn.resource.linux.service import Service
+
+class RepoNG(Service):
+ """
+ Resource: RepoNG
+ """
+ __service_name__ = 'repo-ng'
diff --git a/vicn/resource/icn/route.py b/vicn/resource/icn/route.py
new file mode 100644
index 00000000..0dc2ed2f
--- /dev/null
+++ b/vicn/resource/icn/route.py
@@ -0,0 +1,36 @@
+#!/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.
+#
+
+from netmodel.model.type import Integer, String
+from vicn.core.attribute import Attribute
+from vicn.core.resource import Resource
+from vicn.resource.icn.face import Face
+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",
+ mandatory=True)
+ cost = Attribute(Integer, default=1)
+
+ def __repr__(self):
+ return '<Route {} {} on node {}>'.format(self.prefix, self.face,
+ self.node.name)
+
+ __str__ = __repr__
diff --git a/vicn/resource/icn/virtual-repo.py b/vicn/resource/icn/virtual-repo.py
new file mode 100644
index 00000000..8cb306d9
--- /dev/null
+++ b/vicn/resource/icn/virtual-repo.py
@@ -0,0 +1,37 @@
+#!/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.
+#
+
+from netmodel.model.type import String, Integer
+from vicn.core.attribute import Attribute
+from vicn.resource.icn.producer import Producer
+
+DEFAULT_CHUNK_SIZE = 1300
+
+class VirtualRepo(Producer):
+ """
+ Resource: VirtualRepo
+
+ Note:
+ ndn-virtual-repo {self.folder} -s {self.chunk_size}
+ """
+
+ __package_names__ = ['ndn-virtual-repo']
+
+ folder = Attribute(String, description = "Folder")
+ chunk_size = Attribute(Integer, description = "Chunk size",
+ default = DEFAULT_CHUNK_SIZE)
diff --git a/vicn/resource/icn/webserver.py b/vicn/resource/icn/webserver.py
new file mode 100644
index 00000000..8b8e2ef3
--- /dev/null
+++ b/vicn/resource/icn/webserver.py
@@ -0,0 +1,29 @@
+#!/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.
+#
+
+from vicn.resource.icn.producer import Producer
+
+class WebServer(Producer):
+ """
+ Resource: WebServer
+
+ CCNX Webserver
+ """
+
+ __package_names__ = ['webserver-ccnx']
+ __service_name__ = 'webserver-ccnx'