aboutsummaryrefslogtreecommitdiffstats
path: root/test/mocks/netconf-pnp-simulator/docs
diff options
context:
space:
mode:
Diffstat (limited to 'test/mocks/netconf-pnp-simulator/docs')
-rw-r--r--test/mocks/netconf-pnp-simulator/docs/README.md63
-rw-r--r--test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/data.json10
-rw-r--r--test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml12
-rw-r--r--test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/model.yang29
-rwxr-xr-xtest/mocks/netconf-pnp-simulator/docs/examples/mynetconf/subscriber.py136
-rw-r--r--test/mocks/netconf-pnp-simulator/docs/images/Architecture.pngbin0 -> 58061 bytes
6 files changed, 250 insertions, 0 deletions
diff --git a/test/mocks/netconf-pnp-simulator/docs/README.md b/test/mocks/netconf-pnp-simulator/docs/README.md
new file mode 100644
index 000000000..8aa24504f
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/README.md
@@ -0,0 +1,63 @@
+# NETCONF Plug-and-Play Simulator
+
+[![GitHub Tag][gh-tag-badge]]()
+[![Docker Automated Build][dockerhub-badge]][dockerhub]
+
+## Overview
+
+This project builds a modular engine that allows the creation of NETCONF-enabled devices simulators,
+either physical (PNF) and virtual (VNF).
+
+Basically it's a docker container running Sysrepo and Netopeer2 servers enhanced with a plugger script that, at
+start-time, performs the following actions:
+
+1. Configures TLS and SSH secure accesses to the Netopeer2 server;
+2. Installs multiple YANG models into sysrepo datastore;
+3. Launches the corresponding subscriber applications.
+
+The picture below unveils the architecture of this solution.
+
+![Architecture](images/Architecture.png)
+
+A YANG module contains the following files:
+
+| Filename | Purpose
+| -------- | -------
+|`model.yang` | The YANG model specified according to [RFC-6020][yang-rfc]. Alternatively, you can use your model's name as a basename for this file. Example: `mynetconf.yang`.
+|`data.json` or `data.xml` | An optional data file used to initialize your model.
+|`subscriber.py` | The Python 3 application that implements the behavioral aspects of the YANG model.
+|`requirements.txt` | [Optional] Additional Python packages specified in the [Requirements File Format][py-requirements].
+
+## Application
+
+The `subscriber` is free to implement any wanted passive or active behaviour:
+
+**Passive Behaviour**: The subscriber will receive an event for each modification externally applied to the YANG model.
+
+**Active Behaviour**: At any point in time the subscriber can proactively change its own YANG model.
+
+## Runtime Configuration
+
+### Customizing TLS and SSH accesses
+
+The distributed docker image comes with a sample configuration for TLS and SSH, that can be found at
+`/config/tls` and `/home/netconf/.ssh` directories respectively. The user can replace one or both configurations
+by mounting a custom directory under the respective TLS or SSH mounting point.
+
+### Python Virtual Environment Support
+
+Python programs usually use additional packages not included in the standard Python distribution,
+like the `requests` package, for example.
+We support this scenario by creating isolated Python environments for each custom-provided module whenever
+a `requirements.txt` file is present in the module directory.
+
+## Example Module
+
+The directory `examples/mynetconf` contains an example YANG model and its subscriber along with a
+Docker Compose configuration file to launch a basic simulator.
+
+[dockerhub]: https://hub.docker.com/r/blueonap/netconf-pnp-simulator/
+[dockerhub-badge]: https://img.shields.io/docker/cloud/automated/blueonap/netconf-pnp-simulator
+[gh-tag-badge]: https://img.shields.io/github/v/tag/blue-onap/netconf-pnp-simulator?label=Release
+[py-requirements]: https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format
+[yang-rfc]: https://tools.ietf.org/html/rfc6020
diff --git a/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/data.json b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/data.json
new file mode 100644
index 000000000..63872eef9
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/data.json
@@ -0,0 +1,10 @@
+{
+ "mynetconf:netconflist": {
+ "netconf": [
+ {
+ "netconf-id": 3,
+ "netconf-param": 3
+ }
+ ]
+ }
+}
diff --git a/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml
new file mode 100644
index 000000000..ee70c4fd9
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml
@@ -0,0 +1,12 @@
+version: '3'
+
+services:
+ netopeer2:
+ image: nexus3.onap.org:10001/onap/integration/simulators/netconf-pnp-simulator:2.6.0
+ container_name: mynetconf
+ restart: always
+ ports:
+ - "830:830"
+ - "6513:6513"
+ volumes:
+ - ./:/config/modules/mynetconf
diff --git a/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/model.yang b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/model.yang
new file mode 100644
index 000000000..6c8c36ab0
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/model.yang
@@ -0,0 +1,29 @@
+module mynetconf {
+ yang-version 1.1;
+ namespace "urn:mynetconf:test";
+
+ prefix nft;
+
+ organization
+ "mynetconf";
+ contact
+ "my netconf address";
+ description
+ "yang model for mynetconf";
+ revision "2019-03-01" {
+ description
+ "initial version";
+ }
+
+ container netconflist {
+ list netconf {
+ key netconf-id;
+ leaf netconf-id {
+ type uint16;
+ }
+ leaf netconf-param {
+ type uint32;
+ }
+ }
+ }
+}
diff --git a/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/subscriber.py b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/subscriber.py
new file mode 100755
index 000000000..612729675
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/subscriber.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+
+__author__ = "Mislav Novakovic <mislav.novakovic@sartura.hr>"
+__copyright__ = "Copyright 2018, Deutsche Telekom AG"
+__license__ = "Apache 2.0"
+
+# 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.
+
+# This sample application demonstrates use of Python programming language bindings for sysrepo library.
+# Original c application was rewritten in Python to show similarities and differences
+# between the two.
+#
+# Most notable difference is in the very different nature of languages, c is weakly statically typed language
+# while Python is strongly dynamically typed. Python code is much easier to read and logic easier to comprehend
+# for smaller scripts. Memory safety is not an issue but lower performance can be expected.
+#
+# The original c implementation is also available in the source, so one can refer to it to evaluate trade-offs.
+
+import sysrepo as sr
+import sys
+
+
+# Helper function for printing changes given operation, old and new value.
+def print_change(op, old_val, new_val):
+ if op == sr.SR_OP_CREATED:
+ print(f"CREATED: {new_val.to_string()}")
+ elif op == sr.SR_OP_DELETED:
+ print(f"DELETED: {old_val.to_string()}")
+ elif op == sr.SR_OP_MODIFIED:
+ print(f"MODIFIED: {old_val.to_string()} to {new_val.to_string()}")
+ elif op == sr.SR_OP_MOVED:
+ print(f"MOVED: {new_val.xpath()} after {old_val.xpath()}")
+
+
+# Helper function for printing events.
+def ev_to_str(ev):
+ if ev == sr.SR_EV_VERIFY:
+ return "verify"
+ elif ev == sr.SR_EV_APPLY:
+ return "apply"
+ elif ev == sr.SR_EV_ABORT:
+ return "abort"
+ else:
+ return "unknown"
+
+
+# Function to print current configuration state.
+# It does so by loading all the items of a session and printing them out.
+def print_current_config(session, module_name):
+ select_xpath = f"/{module_name}:*//*"
+
+ values = session.get_items(select_xpath)
+
+ if values is not None:
+ print("========== BEGIN CONFIG ==========")
+ for i in range(values.val_cnt()):
+ print(values.val(i).to_string(), end='')
+ print("=========== END CONFIG ===========")
+
+
+# Function to be called for subscribed client of given session whenever configuration changes.
+def module_change_cb(sess, module_name, event, private_ctx):
+ try:
+ print("========== Notification " + ev_to_str(event) + " =============================================")
+ if event == sr.SR_EV_APPLY:
+ print_current_config(sess, module_name)
+
+ print("========== CHANGES: =============================================")
+
+ change_path = f"/{module_name}:*"
+
+ it = sess.get_changes_iter(change_path)
+
+ while True:
+ change = sess.get_change_next(it)
+ if change is None:
+ break
+ print_change(change.oper(), change.old_val(), change.new_val())
+
+ print("========== END OF CHANGES =======================================")
+ except Exception as e:
+ print(e)
+
+ return sr.SR_ERR_OK
+
+
+def main():
+ # Notable difference between c implementation is using exception mechanism for open handling unexpected events.
+ # Here it is useful because `Connection`, `Session` and `Subscribe` could throw an exception.
+ try:
+ module_name = "ietf-interfaces"
+ if len(sys.argv) > 1:
+ module_name = sys.argv[1]
+ else:
+ print("\nYou can pass the module name to be subscribed as the first argument")
+
+ print(f"Application will watch for changes in {module_name}")
+
+ # connect to sysrepo
+ conn = sr.Connection(module_name)
+
+ # start session
+ sess = sr.Session(conn)
+
+ # subscribe for changes in running config */
+ subscribe = sr.Subscribe(sess)
+
+ subscribe.module_change_subscribe(module_name, module_change_cb)
+
+ try:
+ print_current_config(sess, module_name)
+ except Exception as e:
+ print(e)
+
+ print("========== STARTUP CONFIG APPLIED AS RUNNING ==========")
+
+ sr.global_loop()
+
+ print("Application exit requested, exiting.")
+
+ except Exception as e:
+ print(e)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/mocks/netconf-pnp-simulator/docs/images/Architecture.png b/test/mocks/netconf-pnp-simulator/docs/images/Architecture.png
new file mode 100644
index 000000000..da95c9142
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/images/Architecture.png
Binary files differ