diff options
Diffstat (limited to 'vicn/resource/icn')
-rw-r--r-- | vicn/resource/icn/__init__.py | 0 | ||||
-rw-r--r-- | vicn/resource/icn/ccnx_consumer_producer_test.py | 109 | ||||
-rw-r--r-- | vicn/resource/icn/ccnx_keystore.py | 87 | ||||
-rw-r--r-- | vicn/resource/icn/ccnx_metis.py | 368 | ||||
-rw-r--r-- | vicn/resource/icn/ccnx_simpleTrafficGenerator.py | 106 | ||||
-rw-r--r-- | vicn/resource/icn/consumer.py | 25 | ||||
-rw-r--r-- | vicn/resource/icn/face.py | 140 | ||||
-rw-r--r-- | vicn/resource/icn/forwarder.py | 64 | ||||
-rw-r--r-- | vicn/resource/icn/icn_application.py | 37 | ||||
-rw-r--r-- | vicn/resource/icn/icn_tools.py | 26 | ||||
-rw-r--r-- | vicn/resource/icn/iping.py | 125 | ||||
-rw-r--r-- | vicn/resource/icn/ndnpingserver.py | 76 | ||||
-rw-r--r-- | vicn/resource/icn/nfd.py | 136 | ||||
-rw-r--r-- | vicn/resource/icn/producer.py | 29 | ||||
-rw-r--r-- | vicn/resource/icn/repo-ng.py | 25 | ||||
-rw-r--r-- | vicn/resource/icn/route.py | 36 | ||||
-rw-r--r-- | vicn/resource/icn/virtual-repo.py | 37 | ||||
-rw-r--r-- | vicn/resource/icn/webserver.py | 29 |
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' |