aboutsummaryrefslogtreecommitdiffstats
path: root/test/mocks/pnfsimulator/netconfsimulator
diff options
context:
space:
mode:
authorWojciech Sliwka <wojciech.sliwka@nokia.com>2019-07-10 13:48:52 +0200
committerGary Wu <gary.wu@futurewei.com>2019-07-19 17:18:49 +0000
commit032ff22ef20b59950a8b5fae8d2ba6d03e93ac93 (patch)
tree98058dcf2726b3f432cecffc42bd7cbccca3555e /test/mocks/pnfsimulator/netconfsimulator
parent79c9b78adb7fbc943fd2aee7d333fd3cadf5b8f3 (diff)
Opensourcing new version of Simulator
Additional info in README.md Issue-ID: INT-1134 Signed-off-by: Wojciech Sliwka <wojciech.sliwka@nokia.com> Change-Id: I06d41fd3f361b7a451b30b702882810e4136a129
Diffstat (limited to 'test/mocks/pnfsimulator/netconfsimulator')
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/README.md276
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/config/netconf.env5
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/docker-compose.yml96
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/docker/Dockerfile4
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/ftpes/files/ftpes-noone.txt0
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/ftpes/files/onap/ftpes-onap.txt0
-rwxr-xr-xtest/mocks/pnfsimulator/netconfsimulator/ftpes/tls/pure-ftpd.pem49
-rwxr-xr-xtest/mocks/pnfsimulator/netconfsimulator/ftpes/userpass/pureftpd.passwd1
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/__init__.py19
-rwxr-xr-xtest/mocks/pnfsimulator/netconfsimulator/netconf/initialize_netopeer.sh54
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/load_server_certs.xml40
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/newmodel.xml24
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/newmodel.yang9
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/pnf-simulator.data.xml24
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/pnf-simulator.yang9
-rwxr-xr-xtest/mocks/pnfsimulator/netconfsimulator/netconf/set-up-xmls.py153
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/test_yang_loader_server.py121
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/tls_listen.xml48
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/yang_loader_server.py172
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/Application.cpp74
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/Application.h43
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/CMakeLists.txt20
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/KafkaWrapper.cpp105
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/KafkaWrapper.h44
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/SysrepoCallback.cpp108
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/SysrepoCallback.h42
-rwxr-xr-xtest/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/libsysrepo-cpp.sobin0 -> 442112 bytes
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/main.cpp48
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo.h2015
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Connection.hpp63
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Internal.hpp80
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Session.hpp245
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Struct.hpp514
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Sysrepo.hpp177
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Tree.hpp176
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Xpath.hpp97
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/plugins.h139
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/trees.h226
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/values.h196
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/xpath.h232
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/pom.xml278
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/Configuration.java34
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/Main.java31
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/SwaggerConfig.java43
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/Config.java70
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/MessageDTO.java31
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreController.java59
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreService.java91
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerEntry.java36
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandler.java67
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/model/KafkaMessage.java37
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/NetconfController.java111
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfBeanConfiguration.java60
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditor.java50
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReader.java57
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationService.java76
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationTO.java32
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConnectionParams.java37
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfSessionHelper.java37
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/LoadModelResponse.java40
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderService.java104
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/EndpointConfig.java46
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/NetconfEndpoint.java95
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageEncoder.java34
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListener.java51
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/main/resources/application.properties8
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/integration/NetconfFunctionsIT.java211
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/integration/NetconfSimulatorClient.java150
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/EmbeddedKafkaConfig.java69
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreControllerTest.java86
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreServiceTest.java103
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandlerTest.java87
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/NetconfControllerTest.java172
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditorTest.java69
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReaderTest.java94
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationServiceTest.java102
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderServiceTest.java121
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/NetconfEndpointTest.java135
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListenerTest.java73
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/resources/application-it.properties0
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/resources/initialConfig.xml23
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/resources/invalidXmlFile.xml23
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/resources/newYangModel.yang8
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/resources/updatedConfig.xml24
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/src/test/resources/updatedConfigForCmHistory.xml24
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/ssh/ssh_host_rsa_key49
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/ssh/ssh_host_rsa_key.pub1
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/tls/ca.crt24
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/tls/server_cert.crt24
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/tls/server_key.pem27
90 files changed, 9062 insertions, 0 deletions
diff --git a/test/mocks/pnfsimulator/netconfsimulator/README.md b/test/mocks/pnfsimulator/netconfsimulator/README.md
new file mode 100644
index 000000000..94bcd760f
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/README.md
@@ -0,0 +1,276 @@
+# Netconf Simulator
+A simulator that is able to receive and print history of CM configurations.
+
+## Required software
+To run the simulator, the following software should be installed:
+- JDK 1.8
+- Maven
+- docker
+- docker-compose
+
+### API
+Simulator exposes both HTTP and native netconf interface.
+
+### Running simulator
+In order to run simulator, invoke *mvn clean install docker:build* to build required images.
+Add executable permission to initialize_netopeer.sh (by executing `sudo chmod +x netconf/initialize_netopeer.sh`)
+and then invoke *docker-compose up* command.
+In case of copying simulator files to another location, keep in mind to copy also *docker-compose.yml* and directories: *config, templates, netopeer-change-saver-native and netconf*.
+
+#### Restarting
+Restarting simulator can be done by first typing *docker-compose restart* in terminal.
+
+#### Shutting down
+The command *docker-compose down* can be used to shut the simulator down.
+
+## Usage of simulator
+
+### Netconf TLS support
+Embedded netconf server supports connections over TLS on port 6513. Default server and CA certificate have been taken from Netopeer2 repository: https://github.com/CESNET/Netopeer2/tree/master/server/configuration/tls
+
+Mentioned Github repository contains sample client certificate, which works out of the box.
+#### Replacing server certificates
+In order to replace TLS certificates with third-party ones, the following naming schema must be followed:
+* CA certificate file should be named 'ca.crt'
+* Netconf server certificate file should be named 'server_cert.crt'
+* Netconf server keyfile file should be named 'server_key.pem'
+
+Certificates and keys should follow PEM formatting guidelines.
+Prepared files should be placed under _tls/_ directory (existing files must be overwritten).
+After copying, it is necessary to restart the Netconf Simulator (please refer to [restarting simulator](restarting) guide).
+
+This is a sample curl command to test client connection (the example assumes that Netconf Simulator runs on 127.0.0.1):
+```
+curl -k -v https://127.0.0.1:6513 --cacert ca.crt --key client.key --cert client.crt
+```
+
+
+### Capturing netconf configuration changes
+
+The netconfsimulator tool will intercept changes in netconf configuration, done by edit-config command (invoked through simulator's edit-configuration endpoint or directly through exposed netconf-compliant interface). The following changes are intercepted:
+- creating new item
+- moving an item
+- modifying an item
+- deleting an item
+
+Each captured change contains fully qualified parameter name (including xpath - namespace and container name)
+
+#### REST API usage with examples
+
+Application of native netconf operations on YANG model is covered by REST API layer.
+Example invocation of operations with its requests and results are presented below.
+For basic edit-config and get config actions, response is in plain XML format, whereas stored data that can be accessed via API is returned in JSON format.
+
+**Load new YANG model**
+http method: POST
+```
+URL: http:<simulator_ip>:9000/netconf/model/<moduleName>
+```
+request: file content to be sent as multipart (form data)
+```
+module pnf-simulator {
+ namespace "http://onap.org/pnf-simulator";
+ prefix config;
+ container config {
+ config true;
+ leaf itemValue1 {type uint32;}
+ leaf itemValue2 {type uint32;}
+ leaf itemValue3 {type uint32;}
+ leaf-list allow-user {
+ type string;
+ ordered-by user;
+ description "A sample list of user names.";
+ }
+ }
+}
+```
+
+**Delete existing YANG model**
+http method: DELETE
+```
+URL: http:<simulator_ip>:9000/netconf/model/<moduleName>
+```
+request body should be empty.
+response: a HTTP 200 code indicating successful operation or 400/500 in case of errors.
+
+**Get all running configurations**
+http method: GET
+```
+URL: http:<simulator_ip>:9000/netconf/get
+```
+response: plain XML
+```
+<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>2781</itemValue1>
+ <itemValue2>3782</itemValue2>
+ <itemValue3>3333</itemValue3>
+</config>
+<config2 xmlns="http://onap.org/pnf-simulator2" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>2781</itemValue1>
+ <itemValue2>3782</itemValue2>
+ <itemValue3>3333</itemValue3>
+</config2>
+```
+
+**Get running configuration**
+http method: GET
+```
+URL: http:<simulator_ip>:9000/netconf/get/'moduleName'/'container'
+```
+response: plain XML
+```
+<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>2781</itemValue1>
+ <itemValue2>3782</itemValue2>
+ <itemValue3>3333</itemValue3>
+</config>
+```
+
+**Edit configuration**
+To edit configuration XML file must be prepared. No plain request body is used here,
+request content must be passed as multipart file (form data) with file name/key='editConfigXml' and file content in XML format
+
+http method: POST
+```
+URL: http:<simulator_ip>:9000/netconf/edit-config
+```
+request: file content to be sent as multipart (form data)
+```
+<config xmlns="http://onap.org/pnf-simulator">
+ <itemValue1>2781</itemValue1>
+ <itemValue2>3782</itemValue2>
+ <itemValue3>3333</itemValue3>
+</config>
+```
+
+response: actual, running configuration after editing config:
+```
+<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>2781</itemValue1>
+ <itemValue2>3782</itemValue2>
+ <itemValue3>3333</itemValue3>
+</config>"
+```
+
+Captured change, that can be obtained from db also via REST API:
+
+http method: GET
+```
+URL: http://<simulator_ip>:9000/store/less?offset=1
+```
+response:
+```
+[{"timestamp": 1542877413979, "configuration": "CREATED: /pnf-simulator:config/itemValue3 = 3333"}]
+```
+
+Notice: if new value is the same as the old one, the change won’t be intercepted (because there is no state change). This is a limitation of used netconf implementation (Netopeer2).
+
+**Modify request**
+http method: POST
+```
+URL: http:<simulator_ip>:9000/netconf/edit-config
+```
+file content to be sent as multipart (form data):
+```
+<config xmlns="http://onap.org/pnf-simulator" >
+ <itemValue1>111</itemValue1>
+ <itemValue2>222</itemValue2>
+</config>
+```
+
+response: actual, running configuration after editing config:
+```
+<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>111</itemValue1>
+ <itemValue2>222</itemValue2>
+</config>"
+```
+
+Captured change:
+http method: GET
+```
+URL: http://<simulator_ip>:9000/store/less?offset=2
+```
+```
+[{"timestamp": 1542877413979, "configuration": "MODIFIED: : old value: /pnf-simulator:config/itemValue1 = 2781, new value: /pnf-simulator:config/itemValue1 = 111",
+ {"timestamp": 1542877413979, "configuration": "MODIFIED: : old value: /pnf-simulator:config/itemValue2 = 3782, new value: /pnf-simulator:config/itemValue2 = 222"}]
+```
+
+**Move request** (inserting a value into leaf-list which in turn rearranges remaining elements)
+http method: POST
+```
+URL: http:<simulator_ip>:9000/netconf/edit-config
+```
+file content to be sent as multipart (form data):
+```
+<config xmlns="http://onap.org/pnf-simulator" xmlns:yang="urn:ietf:params:xml:ns:yang:1" xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <allow-user xc:operation="create" yang:insert="before" yang:value="bob">mike</allow-user>
+</config>
+```
+
+Captured change:
+http method: GET
+```
+URL: http://<simulator_ip>:9000/store/less?offset=2
+```
+```
+[{"timestamp": 1542877413979, "configuration": "CREATED: /pnf-simulator:config/allow-user = mike"},
+ {"timestamp": 1542877413979, "configuration": "MOVED: /pnf-simulator:config/allow-user = mike after /pnf-simulator:config/allow-user = alice"}]
+```
+
+**Delete request**
+http method: POST
+```
+URL: http:<simulator_ip>:9000/netconf/edit-config
+```
+file content to be sent as multipart (form data):
+```
+<config xmlns="http://onap.org/pnf-simulator">
+ <itemValue1>1111</itemValue1>
+ <itemValue2 xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0" xc:operation="delete"/>
+</config>
+```
+
+Captured change:
+http method: GET
+```
+URL: http://<simulator_ip>:9000/store/less?offset=1
+```
+```
+[{"timestamp": 1542877413979, "configuration": "DELETED: /pnf-simulator:config/itemValue2 = 222"}]
+```
+
+Getting all configuration changes:
+http method: GET
+```
+URL: http://<simulator_ip>:9000/store/cm-history
+```
+response:
+```
+[{"timestamp":1542877413979,"configuration":"MODIFIED: : old value: /pnf-simulator:config/itemValue1 = 2781, new value: /pnf-simulator:config/itemValue1 = 111"},
+ {"timestamp":1542877413979,"configuration":"MODIFIED: : old value: /pnf-simulator:config/itemValue2 = 3782, new value: /pnf-simulator:config/itemValue2 = 222"},
+ {"timestamp":1542877414000,"configuration":"CREATED: : /pnf-simulator:config/itemValue3 = 3333"},
+ {"timestamp":1542877414104,"configuration":"CREATED: : CREATED: /pnf-simulator:config/allow-user = mike"}
+ {"timestamp":1542877414107,"configuration":"MOVED: /pnf-simulator:config/allow-user = mike after /pnf-simulator:config/allow-user = alice"},
+ {"timestamp":1542877414275,"configuration":"DELETED: /pnf-simulator:config/itemValue2 = 222"}]
+```
+
+### Logging
+
+### Swagger
+
+## Developers Guide
+
+### Integration tests
+Integration tests use docker-compose for setting up cluster with all services.
+Those tests are not part of build pipeline, but can be run manually by invoking *mvn verify -DskipITs=false* from project command line.
+Tests can be found in netconfsimulator project in src/integration directory.
+
+## Troubleshooting
+Q: Simulator throws errors after shutting down with *docker-compose down* or *docker-compose restart*
+
+A: Remove docker containers that were left after stopping the simulator with the following commands:
+```
+docker stop $(docker ps | grep netconfsimulator | awk '{print $1;}')
+docker rm $(docker ps -a | grep netconfsimulator | awk '{print $1;}')
+```
diff --git a/test/mocks/pnfsimulator/netconfsimulator/config/netconf.env b/test/mocks/pnfsimulator/netconfsimulator/config/netconf.env
new file mode 100644
index 000000000..6cf310a27
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/config/netconf.env
@@ -0,0 +1,5 @@
+NETCONF_ADDRESS=netopeer
+NETCONF_PORT=830
+NETCONF_MODEL=pnf-simulator
+NETCONF_MAIN_CONTAINER=config
+TZ=Europe/Warsaw
diff --git a/test/mocks/pnfsimulator/netconfsimulator/docker-compose.yml b/test/mocks/pnfsimulator/netconfsimulator/docker-compose.yml
new file mode 100644
index 000000000..fd3339162
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/docker-compose.yml
@@ -0,0 +1,96 @@
+version: '3'
+
+services:
+ zookeeper:
+ image: wurstmeister/zookeeper
+ ports:
+ - "2181:2181"
+ networks:
+ - netconfnetwork
+
+ kafka1:
+ image: wurstmeister/kafka:1.1.0
+ ports:
+ - "9092:9092"
+ hostname: kafka1
+ networks:
+ - netconfnetwork
+ environment:
+ KAFKA_ADVERTISED_PORT: 9092
+ KAFKA_ADVERTISED_HOST_NAME: kafka1
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+ KAFKA_CREATE_TOPICS: "config:1:1"
+ KAFKA_DELETE_RETENTION_MS: 604800000
+ KAFKA_LOG_CLEANER_DELETE_RETENTION_MS: 604800000
+ depends_on:
+ - zookeeper
+
+ netconf-simulator:
+ image: nexus3.onap.org:10003/onap/netconfsimulator:5.0.0-SNAPSHOT
+ ports:
+ - "9000:8080"
+ restart: on-failure
+ hostname: netconf-simulator
+ networks:
+ - netconfnetwork
+ depends_on:
+ - zookeeper
+ - kafka1
+ - netopeer
+
+ netopeer:
+ image: sysrepo/sysrepo-netopeer2:latest
+ ports:
+ - "830:830"
+ - "5002:5002"
+ - "6513:6513"
+ volumes:
+ - ./netconf:/netconf
+ - ./netopeer-change-saver-native:/netopeer-change-saver
+ - ./tls:/tls
+ env_file:
+ - ./config/netconf.env
+ restart: on-failure
+ networks:
+ - netconfnetwork
+ depends_on:
+ - sftp-server
+ - ftpes-server
+ environment:
+ http_proxy: ${http_proxy}
+ https_proxy: ${https_proxy}
+ command:
+ - /netconf/initialize_netopeer.sh
+
+ sftp-server:
+ image: atmoz/sftp:alpine
+ ports:
+ - "2222:22"
+ volumes:
+ - ./sftp:/home/sftp-user/sftp
+ - ./ssh/ssh_host_rsa_key.pub:/home/sftp-user/.ssh/keys/ssh_host_rsa_key.pub
+ networks:
+ - netconfnetwork
+ restart: on-failure
+ command: sftp-user::1001
+
+ ftpes-server:
+ image: stilliard/pure-ftpd:latest
+ ports:
+ - "2221:21"
+ - "30000-30009:30000-30009"
+ volumes:
+ - ./ftpes/files:/home/ftpusers/onap
+ - ./ftpes/userpass/:/etc/pure-ftpd/passwd/
+ - ./ftpes/tls/:/etc/ssl/private/
+ networks:
+ - netconfnetwork
+ environment:
+ PUBLICHOST: localhost
+ ADDED_FLAGS: --tls=2
+ FTP_USER_HOME: onap
+ restart: on-failure
+
+networks:
+ netconfnetwork:
+ driver: bridge
diff --git a/test/mocks/pnfsimulator/netconfsimulator/docker/Dockerfile b/test/mocks/pnfsimulator/netconfsimulator/docker/Dockerfile
new file mode 100644
index 000000000..0e25fd310
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/docker/Dockerfile
@@ -0,0 +1,4 @@
+FROM openjdk:8-jre-alpine
+ADD libs /app/libs
+ADD netconfsimulator-5.0.0-SNAPSHOT.jar /app/netconf-simulator.jar
+CMD java -cp /app/libs/*:/app/netconf-simulator.jar org.onap.netconfsimulator.Main
diff --git a/test/mocks/pnfsimulator/netconfsimulator/ftpes/files/ftpes-noone.txt b/test/mocks/pnfsimulator/netconfsimulator/ftpes/files/ftpes-noone.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/ftpes/files/ftpes-noone.txt
diff --git a/test/mocks/pnfsimulator/netconfsimulator/ftpes/files/onap/ftpes-onap.txt b/test/mocks/pnfsimulator/netconfsimulator/ftpes/files/onap/ftpes-onap.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/ftpes/files/onap/ftpes-onap.txt
diff --git a/test/mocks/pnfsimulator/netconfsimulator/ftpes/tls/pure-ftpd.pem b/test/mocks/pnfsimulator/netconfsimulator/ftpes/tls/pure-ftpd.pem
new file mode 100755
index 000000000..0ce676efa
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/ftpes/tls/pure-ftpd.pem
@@ -0,0 +1,49 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHbSk5/cABTpCt
+q54QyTkhuhb84nEz5ztKL0hY56fsVtAA2gSAde+qV9YwUIuReOUhAF4RVVt2Lkn/
+1R0yX+0IjrXnO7jYzDj6QhgyqYKb3iQlvShZMMQ7qihn8qBxobk7+O10x6VLS2L8
+WYpQxGXu7T1qXbw10RhrqG8nbXYX+aHMsv9zMt9OYqKSI073OZR2vk3K49Uqcurj
+sXuRJOa10MRsxgA726pr8OLWAWejsoFaqP2fQS3HeT2RnAqPyAgPc0P6n7gxo0JU
+U5dPnrPbsvfdegIFxfc57oZXrLz7nYXkJEcjYTBFSQ+JAaRfx9kNXZ7Gft7EAMyF
+BLemY/0VAgMBAAECggEARD9bSHlKaCgW4xhEM8JpRt2EWG62BukvJSghPiupD/x1
+mpUBzWSO7GC68DXgTZxt7WlOx+fKMRuOP3sTTtX9LFyKa+PIUokxRpOv7EaOaAER
+pciiMkO6JCELSueBeOG7noaF3N0l+CqIaYvLBfDwYV/XELubWV+BV/aAc6HGNFWi
+4bjM+BOBLQstrEeJh2jVylzv4CTtlTs2pwiHFSyrHhudTk5nnATAHn1gi+X42v1A
+zk3UfqADZJmMI0/Roup4YPZ3+6zUzDN2i+qasHexL0OKIjRcSqpgqQoIeKEbKKfw
+sOgiWIR2Xvj7EJmhzJlWgKjk8OLs/7U4QpnD+s0agQKBgQDu3ojqKNWnPHy0Nupm
+tmAs28WLK76R0iZeAd2nwsU2K6lnm9z5o2ab3ffTJVB9kAetKJa3UerKskF/qF9C
+MtjlEn6F++uYFitpLjQevnyrKSqFqbzytDXrQlk+gZLglmi6YylT5k9qLSREAu55
+XS/wbm9XU2Q7sl8oTnZHXptT7QKBgQDVunvqdDn1FaNU9EwQCGPS3QGu+go22xkM
+4Rs2CoHWfqmhGOo8lJKBElDqsXvxggrZLWJe/1lgnELT/9aXS8QLWBnZxpTj9wfd
+igH+CJc3mWnLThmUGdSV/tuHon2IdQ8/1CiGSwIr9kYCnStidUtOXjIbgc6kUTTi
+5wtIGHh4yQKBgQDXJ/0dJbDklRgiX4CdCdLxNPfnlnxt7mN+s6GK1WY7l/JcD8ln
+1qW66aGrP2YT42L2tqOi9hdNgmh66xb6ksBI/XKXjsWz1Ow/Lk3mD2BN76OMh8pY
+trgGc1ndcmrw/qnQkTcNilqn4YdT92wER0rB/0cs2kFjgBQ0QxBI0s+INQKBgA6Y
+2fW9UmgGvk0DEl7V89tm9MJ6mU/9zswuY6lhNlTr+bHi/bx9eTQPiC8/R/PKqesD
+SoCqd/Q9N+M6yfEzX4RW1A0nnuui54qd7lznQUyu0abtApo22WoVKfEti91SAWSe
+nNXvMYrHGyj6iwgCcs47aLiwOOjIExCcLw0RfsjhAoGAc1zaRbrtjjh66FJYjLiJ
+Q6EXfm31ptaQQUn5rQyHMD2VRlajCYV+fv75tezf2dQvJcqHYWrEuY8U+OTbB1TB
+IEqN8ETUeLegl5RgvWoyWinqdbv/0d9LtwVBdtiEQLoYumD934mshEDgzCOOjrBe
+Salcd1vc6y6NiFooPlvloXQ=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDYDCCAkigAwIBAgIJAMH2upKd2yAJMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTgwOTEwMTI1ODE2WhcNMzgwOTA1MTI1ODE2WjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAx20pOf3AAU6QraueEMk5IboW/OJxM+c7Si9IWOen7FbQANoEgHXvqlfW
+MFCLkXjlIQBeEVVbdi5J/9UdMl/tCI615zu42Mw4+kIYMqmCm94kJb0oWTDEO6oo
+Z/KgcaG5O/jtdMelS0ti/FmKUMRl7u09al28NdEYa6hvJ212F/mhzLL/czLfTmKi
+kiNO9zmUdr5NyuPVKnLq47F7kSTmtdDEbMYAO9uqa/Di1gFno7KBWqj9n0Etx3k9
+kZwKj8gID3ND+p+4MaNCVFOXT56z27L33XoCBcX3Oe6GV6y8+52F5CRHI2EwRUkP
+iQGkX8fZDV2exn7exADMhQS3pmP9FQIDAQABo1MwUTAdBgNVHQ4EFgQUt51lQ+ab
+MTq+w2U/knCsIPb3wrkwHwYDVR0jBBgwFoAUt51lQ+abMTq+w2U/knCsIPb3wrkw
+DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAQ69AktYLQ+VRbojz
+zC0XQ2M1FAkfJI2P0LvPoYxZgId2CnZW3sMIdnJdF+KjvOqeGyFmw+hn8WkoKiWj
+0sxuGmrWt5t+5bF2wcq0CtTeF1/o6DsRhRiJBzmcLe81ItrN6emZSg96xCKzkHBZ
+3nF4fG88vtiYgD932lMStDqQzSTx0FsCGpGaKh9xDmKvlP24NWdM9gyOEsRbDvqd
+vS1Q45Jx0jzkp7X5d0casqBWIZak3z0EVdK7c8Y/GxxTcWfIMINCl9+F9kpTA/ZX
+uARYzrPWaBfDBi2r5acWi/AHJM3U+LgzO5nCKa+38vtjNw3NtbslA4InQ5cU2B8X
+QN8NlQ==
+-----END CERTIFICATE-----
diff --git a/test/mocks/pnfsimulator/netconfsimulator/ftpes/userpass/pureftpd.passwd b/test/mocks/pnfsimulator/netconfsimulator/ftpes/userpass/pureftpd.passwd
new file mode 100755
index 000000000..7961e710d
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/ftpes/userpass/pureftpd.passwd
@@ -0,0 +1 @@
+onap:$6$Guq6OMhBdNZ6nTk0$7dLt6hOrAv.in36jzWGd5UgWeDqN3CuKjrzJ.izRTdgZRTszeNYbT2dk7UDh9CLD7pohnB0.k1NSZmRIUB/ID/:1001:1001::/home/ftpusers/onap/./::::::::::::
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netconf/__init__.py b/test/mocks/pnfsimulator/netconfsimulator/netconf/__init__.py
new file mode 100644
index 000000000..0f144c21e
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netconf/__init__.py
@@ -0,0 +1,19 @@
+###
+# ============LICENSE_START=======================================================
+# Simulator
+# ================================================================================
+# Copyright (C) 2019 Nokia. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+###
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netconf/initialize_netopeer.sh b/test/mocks/pnfsimulator/netconfsimulator/netconf/initialize_netopeer.sh
new file mode 100755
index 000000000..71c1f215b
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netconf/initialize_netopeer.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+###
+# ============LICENSE_START=======================================================
+# Simulator
+# ================================================================================
+# Copyright (C) 2019 Nokia. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+###
+
+cat > /etc/apt/apt.conf << EOF
+Acquire::http {
+ No-Cache "true";
+ No-Store "true";
+ Pipeline-Depth "0";
+};
+EOF
+
+NETOPEER_CHANGE_SAVER=netopeer-change-saver
+
+cp /tls/* /usr/local/etc/keystored/keys/
+cp /netconf/*.xml /tmp/
+
+chmod +x /netconf/set-up-xmls.py
+/netconf/set-up-xmls.py /tls ca.crt server_cert.crt server_key.pem /tmp/load_server_certs.xml /tmp/tls_listen.xml
+
+/usr/bin/supervisord -c /etc/supervisord.conf &
+sysrepoctl --install --yang=/netconf/pnf-simulator.yang --owner=netconf:nogroup --permissions=777
+sysrepocfg --import=/netconf/pnf-simulator.data.xml --datastore=startup --format=xml --level=3 pnf-simulator
+sysrepocfg --merge=/tmp/load_server_certs.xml --format=xml --datastore=startup ietf-keystore
+sysrepocfg --merge=/tmp/tls_listen.xml --format=xml --datastore=startup ietf-netconf-server
+
+apt-get update
+apt-get install -y python3 python3-pip librdkafka-dev
+pip3 install flask flask_restful
+nohup python3 /netconf/yang_loader_server.py &
+
+/bin/cp -R /$NETOPEER_CHANGE_SAVER /opt/dev/
+ln -s /opt/dev/sysrepo/build/src/libsysrepo.so /$NETOPEER_CHANGE_SAVER/libsysrepo.so
+cd /opt/dev/$NETOPEER_CHANGE_SAVER && cmake .
+cd /opt/dev/$NETOPEER_CHANGE_SAVER && make
+/opt/dev/$NETOPEER_CHANGE_SAVER/bin/netopeer-change-saver pnf-simulator kafka1 config
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netconf/load_server_certs.xml b/test/mocks/pnfsimulator/netconfsimulator/netconf/load_server_certs.xml
new file mode 100644
index 000000000..2524e08b0
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netconf/load_server_certs.xml
@@ -0,0 +1,40 @@
+<!--
+ ============LICENSE_START=======================================================
+ Simulator
+ ================================================================================
+ Copyright (C) 2019 Nokia. All rights reserved.
+ ================================================================================
+ 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.
+ ============LICENSE_END=========================================================
+ -->
+
+<keystore xmlns="urn:ietf:params:xml:ns:yang:ietf-keystore">
+ <private-keys>
+ <private-key>
+ <name>SERVER_KEY_NAME</name>
+ <certificate-chains>
+ <certificate-chain>
+ <name>SERVER_CERT_NAME</name>
+ <certificate>SERVER_CERTIFICATE_HERE</certificate>
+ </certificate-chain>
+ </certificate-chains>
+ </private-key>
+ </private-keys>
+ <trusted-certificates>
+ <name>test_trusted_ca_list</name>
+ <trusted-certificate>
+ <name>CA_CERT_NAME</name>
+ <certificate>CA_CERTIFICATE_HERE</certificate>
+ </trusted-certificate>
+ </trusted-certificates>
+</keystore>
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netconf/newmodel.xml b/test/mocks/pnfsimulator/netconfsimulator/netconf/newmodel.xml
new file mode 100644
index 000000000..90a3451d4
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netconf/newmodel.xml
@@ -0,0 +1,24 @@
+<!--
+ ============LICENSE_START=======================================================
+ Simulator
+ ================================================================================
+ Copyright (C) 2019 Nokia. All rights reserved.
+ ================================================================================
+ 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.
+ ============LICENSE_END=========================================================
+ -->
+
+<config2 xmlns="http://onap.org/pnf-simulator2">
+ <item1>500</item1>
+ <item2>1000</item2>
+</config2>
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netconf/newmodel.yang b/test/mocks/pnfsimulator/netconfsimulator/netconf/newmodel.yang
new file mode 100644
index 000000000..544f46725
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netconf/newmodel.yang
@@ -0,0 +1,9 @@
+module newmodel {
+ namespace "http://onap.org/pnf-simulator2";
+ prefix config2;
+ container config2 {
+ config true;
+ leaf item1 {type uint32;}
+ leaf item2 {type uint32;}
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netconf/pnf-simulator.data.xml b/test/mocks/pnfsimulator/netconfsimulator/netconf/pnf-simulator.data.xml
new file mode 100644
index 000000000..c235f6405
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netconf/pnf-simulator.data.xml
@@ -0,0 +1,24 @@
+<!--
+ ============LICENSE_START=======================================================
+ Simulator
+ ================================================================================
+ Copyright (C) 2019 Nokia. All rights reserved.
+ ================================================================================
+ 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.
+ ============LICENSE_END=========================================================
+ -->
+
+<config xmlns="http://onap.org/pnf-simulator">
+ <itemValue1>42</itemValue1>
+ <itemValue2>35</itemValue2>
+</config>
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netconf/pnf-simulator.yang b/test/mocks/pnfsimulator/netconfsimulator/netconf/pnf-simulator.yang
new file mode 100644
index 000000000..ba1158560
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netconf/pnf-simulator.yang
@@ -0,0 +1,9 @@
+module pnf-simulator {
+ namespace "http://onap.org/pnf-simulator";
+ prefix config;
+ container config {
+ config true;
+ leaf itemValue1 {type uint32;}
+ leaf itemValue2 {type uint32;}
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netconf/set-up-xmls.py b/test/mocks/pnfsimulator/netconfsimulator/netconf/set-up-xmls.py
new file mode 100755
index 000000000..d46ff91f9
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netconf/set-up-xmls.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+
+###
+# ============LICENSE_START=======================================================
+# Simulator
+# ================================================================================
+# Copyright (C) 2019 Nokia. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+###
+
+import os
+import sys
+import logging
+import logging.config
+
+logging.basicConfig()
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+
+# Placeholders definition - this needs to match placeholders in
+# load_server_certs_xml_file and tls_listen_xml_file
+SERVER_KEY_NAME = "SERVER_KEY_NAME"
+SERVER_CERT_NAME = "SERVER_CERT_NAME"
+SERVER_CERTIFICATE_HERE = "SERVER_CERTIFICATE_HERE"
+CA_CERT_NAME = "CA_CERT_NAME"
+CA_CERTIFICATE_HERE = "CA_CERTIFICATE_HERE"
+CA_FINGERPRINT_HERE = "CA_FINGERPRINT_HERE"
+CA_FINGERPRINT_ENV = "CA_FINGERPRINT"
+SERVER_CERTIFICATE_ENV = "SERVER_CERTIFICATE_ENV"
+CA_CERTIFICATE_ENV = "CA_CERTIFICATE_ENV"
+
+
+class FileHelper(object):
+ @classmethod
+ def get_file_contents(cls, filename):
+ with open(filename, "r") as f:
+ return f.read()
+
+ @classmethod
+ def write_file_contents(cls, filename, data):
+ with open(filename, "w+") as f:
+ f.write(data)
+
+
+class CertHelper(object):
+ @classmethod
+ def get_pem_content_stripped(cls, pem_dir, pem_filename):
+ cmd = "cat {}/{} | grep -v '^-'".format(pem_dir, pem_filename)
+ content = CertHelper.system(cmd)
+ return content
+
+ @classmethod
+ def get_cert_fingerprint(cls, directory, cert_filename):
+ cmd = "openssl x509 -fingerprint -noout -in {}/{} | sed -e " \
+ "'s/SHA1 Fingerprint//; s/=//; s/=//p'" \
+ .format(directory, cert_filename)
+ fingerprint = CertHelper.system(cmd)
+ return fingerprint
+
+ @classmethod
+ def print_certs_info(cls, ca_cert, ca_fingerprint, server_cert):
+ logger.info("Will use server certificate: " + server_cert)
+ logger.info("Will use CA certificate: " + ca_cert)
+ logger.info("CA certificate fingerprint: " + ca_fingerprint)
+
+ @classmethod
+ def system(cls, cmd):
+ return os.popen(cmd).read().replace("\n", "")
+
+
+class App(object):
+ @classmethod
+ def patch_server_certs(cls, data, server_key_filename_noext,
+ server_cert_filename_noext, ca_cert_filename_noext,
+ server_cert, ca_cert):
+ data = data.replace(SERVER_KEY_NAME, server_key_filename_noext)
+ data = data.replace(SERVER_CERT_NAME, server_cert_filename_noext)
+ data = data.replace(CA_CERT_NAME, ca_cert_filename_noext)
+ data = data.replace(SERVER_CERTIFICATE_HERE, server_cert)
+ data = data.replace(CA_CERTIFICATE_HERE, ca_cert)
+ return data
+
+ @classmethod
+ def patch_tls_listen(cls, data, server_cert_filename_noext, ca_fingerprint,
+ server_cert, ca_cert):
+ data = data.replace(SERVER_CERT_NAME, server_cert_filename_noext)
+ data = data.replace(CA_FINGERPRINT_HERE, ca_fingerprint)
+ data = data.replace(SERVER_CERTIFICATE_HERE, server_cert)
+ data = data.replace(CA_CERTIFICATE_HERE, ca_cert)
+ return data
+
+ @classmethod
+ def run(cls):
+ # name things
+ cert_dir = sys.argv[1]
+ ca_cert_filename = sys.argv[2]
+ server_cert_filename = sys.argv[3]
+ server_key_filename = sys.argv[4]
+ load_server_certs_xml_file = sys.argv[5]
+ tls_listen_xml_file = sys.argv[6]
+
+ # strip extensions
+ ca_cert_filename_noext = ca_cert_filename.replace(".crt", "")
+ server_cert_filename_noext = server_cert_filename.replace(".crt", "")
+ server_key_filename_noext = server_key_filename.replace(".pem", "")
+
+ # get certificates from files
+ server_cert = CertHelper.get_pem_content_stripped(cert_dir,
+ server_cert_filename)
+ ca_cert = CertHelper.get_pem_content_stripped(cert_dir,
+ ca_cert_filename)
+ ca_fingerprint = CertHelper.get_cert_fingerprint(cert_dir,
+ ca_cert_filename)
+ CertHelper.print_certs_info(ca_cert, ca_fingerprint, server_cert)
+
+ # patch TLS configuration files
+ data_srv = FileHelper.get_file_contents(load_server_certs_xml_file)
+ patched_srv = App.patch_server_certs(data_srv, server_key_filename_noext,
+ server_cert_filename_noext,
+ ca_cert_filename_noext,
+ server_cert, ca_cert)
+ FileHelper.write_file_contents(load_server_certs_xml_file, patched_srv)
+
+ data_tls = FileHelper.get_file_contents(tls_listen_xml_file)
+ patched_tls = App.patch_tls_listen(data_tls, server_cert_filename_noext,
+ ca_fingerprint, server_cert, ca_cert)
+ FileHelper.write_file_contents(tls_listen_xml_file, patched_tls)
+
+
+def main():
+ if len(sys.argv) is not 7:
+ print("Usage: {1} <cert_dir> <ca_cert_filename> <server_cert_filename> "
+ "<server_key_filename> <load_server_certs_xml_full_path> "
+ "<tls_listen_full_path>", sys.argv[0])
+ return 1
+ App.run()
+ logger.info("XML files patched successfully")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netconf/test_yang_loader_server.py b/test/mocks/pnfsimulator/netconfsimulator/netconf/test_yang_loader_server.py
new file mode 100644
index 000000000..f282517b2
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netconf/test_yang_loader_server.py
@@ -0,0 +1,121 @@
+###
+# ============LICENSE_START=======================================================
+# Simulator
+# ================================================================================
+# Copyright (C) 2019 Nokia. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+###
+
+import unittest
+
+from unittest import mock
+from werkzeug.datastructures import FileStorage
+
+from yang_loader_server import YangLoaderHelper, YangModelServer
+
+
+class TestYangLoaderHelper(unittest.TestCase):
+
+ def test_should_save_file_and_return_path(self):
+ helper = YangLoaderHelper()
+ mocked_file = mock.Mock(FileStorage)
+ mocked_file.filename = "sample"
+
+ path = helper.save_file(mocked_file)
+
+ self.assertEqual(path, "/tmp/sample")
+ mocked_file.save.assert_called_once_with("/tmp/sample")
+
+ @mock.patch('yang_loader_server.check_output')
+ def test_should_install_new_yang_model(self, mocked_output):
+ helper = YangLoaderHelper()
+
+ helper.install_new_model("path")
+
+ mocked_output.assert_called_with(
+ ['sysrepoctl', '--install', '--yang=path',
+ '--owner=netconf:nogroup', '--permissions=777'],
+ stderr=-2, universal_newlines=True)
+
+ @mock.patch('yang_loader_server.check_output')
+ def test_should_delete_yang_model(self, mocked_output):
+ helper = YangLoaderHelper()
+
+ helper.uninstall_a_model("modelName")
+
+ mocked_output.assert_called_with(
+ ['sysrepoctl', '--uninstall', '--module=modelName'],
+ stderr=-2, universal_newlines=True)
+
+ @mock.patch('yang_loader_server.check_output')
+ def test_should_set_default_configuration(self, mocked_output):
+ helper = YangLoaderHelper()
+
+ helper.set_default_configuration("samplePath", "sampleModuleName")
+
+ mocked_output.assert_called_with(
+ ['sysrepocfg', '--import=samplePath', '--datastore=startup',
+ '--format=xml', '--level=3', 'sampleModuleName'],
+ stderr=-2, universal_newlines=True)
+
+ @mock.patch('yang_loader_server.subprocess.Popen')
+ @mock.patch('yang_loader_server.check_output')
+ def test_should_verify_change_listener_for_model_properly(self, mocked_output, mocked_popen):
+ helper = YangLoaderHelper()
+
+ helper.start_change_listener_for_model("sampleModule")
+
+ mocked_output.assert_called_with(
+ ['pgrep', '-f', '/opt/dev/netopeer-change-saver/bin/netopeer-change-saver sampleModule kafka1 config'],
+ stderr=-2, universal_newlines=True)
+
+ @mock.patch('yang_loader_server.check_output')
+ def test_should_raise_exception_when_error_occurred_in_output(self,
+ mocked_output):
+ helper = YangLoaderHelper()
+ mocked_output.return_value = "abcd ERR"
+ with self.assertRaises(RuntimeError) as context:
+ helper._run_bash_command("sample command")
+
+ self.assertEqual('abcd ERR', str(context.exception))
+
+
+class TestYangModelServer(unittest.TestCase):
+
+ def __init__(self, methodName='runTest'):
+ super().__init__(methodName)
+ self._mocked_file = mock.Mock(FileStorage)
+
+ def test_should_properly_apply_and_start_new_model(self):
+ with mock.patch.object(YangModelServer, '_parse_request',
+ new=self._mock_request):
+ helper = mock.Mock(YangLoaderHelper)
+ helper.save_file.return_value = "sampleFile"
+ server = YangModelServer(helper)
+
+ server.post()
+
+ self.assertEqual(helper.save_file.call_count, 2)
+ helper.install_new_model.assert_called_once_with('sampleFile')
+ helper.set_default_configuration.assert_called_once_with(
+ 'sampleFile', 'sampleModuleName')
+ helper.start_change_listener_for_model.assert_called_once_with('sampleModuleName')
+
+ def _mock_request(self):
+ return {
+ 'yangModel': self._mocked_file,
+ 'initialConfig': self._mocked_file,
+ 'moduleName': "sampleModuleName"
+ }
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netconf/tls_listen.xml b/test/mocks/pnfsimulator/netconfsimulator/netconf/tls_listen.xml
new file mode 100644
index 000000000..4f45b28a2
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netconf/tls_listen.xml
@@ -0,0 +1,48 @@
+<!--
+ ============LICENSE_START=======================================================
+ Simulator
+ ================================================================================
+ Copyright (C) 2019 Nokia. All rights reserved.
+ ================================================================================
+ 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.
+ ============LICENSE_END=========================================================
+ -->
+
+<netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
+ <listen>
+ <endpoint>
+ <name>test_tls_listen_endpt</name>
+ <tls>
+ <address>0.0.0.0</address>
+ <port>6513</port>
+ <certificates>
+ <certificate>
+ <name>SERVER_CERT_NAME</name>
+ </certificate>
+ </certificates>
+ <client-auth>
+ <trusted-ca-certs>test_trusted_ca_list</trusted-ca-certs>
+ <cert-maps>
+ <cert-to-name>
+ <id>1</id>
+ <!-- This is not a typo - 0x02 should stay there -->
+ <fingerprint>02:CA_FINGERPRINT_HERE</fingerprint>
+ <map-type xmlns:x509c2n="urn:ietf:params:xml:ns:yang:ietf-x509-cert-to-name">x509c2n:specified</map-type>
+ <name>test</name>
+ </cert-to-name>
+ </cert-maps>
+ </client-auth>
+ </tls>
+ </endpoint>
+ </listen>
+</netconf-server>
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netconf/yang_loader_server.py b/test/mocks/pnfsimulator/netconfsimulator/netconf/yang_loader_server.py
new file mode 100644
index 000000000..716d0712e
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netconf/yang_loader_server.py
@@ -0,0 +1,172 @@
+###
+# ============LICENSE_START=======================================================
+# Simulator
+# ================================================================================
+# Copyright (C) 2019 Nokia. All rights reserved.
+# ================================================================================
+# 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.
+# ============LICENSE_END=========================================================
+###
+
+import logging
+import subprocess
+import os
+from subprocess import check_output, CalledProcessError
+from flask import Flask
+from flask_restful import Resource, Api, reqparse
+from werkzeug.datastructures import FileStorage
+import time
+
+app = Flask(__name__)
+api = Api(app)
+logger = logging.getLogger("yang-loader")
+logger.addHandler(logging.StreamHandler())
+KAFKA_BROKER_NAME="kafka1"
+KAFKA_TOPIC_NAME="config"
+
+
+class YangLoaderHelper(object):
+
+ @classmethod
+ def save_file(cls, yang_model_file: FileStorage) -> str:
+ path = "/tmp/" + yang_model_file.filename
+ yang_model_file.save(path)
+ return path
+
+ @classmethod
+ def install_new_model(cls, yang_model_path: str):
+ logger.info("Installing new model: %s", yang_model_path)
+ command = "sysrepoctl --install --yang={} --owner=netconf:nogroup --permissions=777" \
+ .format(yang_model_path)
+ cls._run_bash_command(command)
+
+ @classmethod
+ def uninstall_a_model(cls, yang_model_name: str):
+ logger.info("Uninstalling a model: %s", yang_model_name)
+ command = "sysrepoctl --uninstall --module={}" \
+ .format(yang_model_name)
+ cls._run_bash_command(command)
+
+
+ @classmethod
+ def set_default_configuration(cls, init_conf_path: str, module_name: str):
+ logger.info("Attempting to set default configuration %s for module %s", init_conf_path, module_name)
+ command = "sysrepocfg --import={} --datastore=startup --format=xml --level=3 {}" \
+ .format(init_conf_path, module_name)
+ cls._run_bash_command(command)
+
+ @classmethod
+ def start_change_listener_for_model(cls, module_name: str):
+ logger.info("Starting listener for model: %s", module_name)
+ command = "/opt/dev/netopeer-change-saver/bin/netopeer-change-saver {} {} {}" \
+ .format(module_name, KAFKA_BROKER_NAME, KAFKA_TOPIC_NAME)
+ try:
+ check_output(["pgrep", "-f" , command], stderr=subprocess.STDOUT, universal_newlines=True)
+ logger.info("Change listener for {} already exist.".format(module_name))
+ except CalledProcessError:
+ subprocess.Popen(command.split(), stdout=subprocess.PIPE)
+
+ @classmethod
+ def stop_change_listener_for_model(cls, model_name):
+ logger.info("Stopping listener for model %s", model_name)
+ pid = cls.get_pid_by_name(model_name)
+ logger.info("pid is %s", pid)
+ command = "kill -2 {}".format(pid)
+ cls._run_bash_command(command)
+
+ @classmethod
+ def _run_bash_command(cls, command: str):
+ try:
+ logger.info("Attempts to invoke %s", command)
+ output = check_output(command.split(), stderr=subprocess.STDOUT,
+ universal_newlines=True)
+ logger.info("Output: %s", output)
+ if "ERR" in output:
+ raise RuntimeError(str(output))
+ except subprocess.CalledProcessError as e:
+ raise RuntimeError(e, str(e.stdout))
+
+ @classmethod
+ def get_pid_by_name(cls, name):
+ for dirname in os.listdir('/proc'):
+ if not dirname.isdigit():
+ continue
+ try:
+ with open('/proc/{}/cmdline'.format(dirname), mode='rb') as fd:
+ content = fd.read().decode().split('\x00')
+ except Exception as e:
+ print(e)
+ continue
+
+ if name in content:
+ return dirname
+
+
+class YangModelServer(Resource):
+ logger = logging.getLogger('YangModelServer')
+
+ def __init__(self, yang_loader_helper: YangLoaderHelper = YangLoaderHelper()):
+ self._yang_loader_helper = yang_loader_helper
+
+ def post(self):
+ args = self._parse_request()
+ yang_model_file = args['yangModel']
+ initial_config_file = args['initialConfig']
+ module_name = args['moduleName']
+ model_path = self._yang_loader_helper.save_file(yang_model_file)
+ conf_path = self._yang_loader_helper.save_file(initial_config_file)
+
+ try:
+ self._yang_loader_helper.install_new_model(model_path)
+ self._yang_loader_helper.set_default_configuration(conf_path,
+ module_name)
+ self._yang_loader_helper.start_change_listener_for_model(module_name)
+ except RuntimeError as e:
+ self.logger.error(e.args, exc_info=True)
+ return str(e.args), 400
+ return "Successfully started"
+
+ def delete(self):
+ args = self._parse_request()
+ yang_model_name = args['yangModelName']
+
+ try:
+ self._yang_loader_helper.stop_change_listener_for_model(yang_model_name)
+ time.sleep(5)
+ self._yang_loader_helper.uninstall_a_model(yang_model_name)
+ except RuntimeError as e:
+ self.logger.error(e.args, exc_info=True)
+ return str(e.args), 400
+ return "Successfully deleted"
+
+ @classmethod
+ def _parse_request(cls) -> reqparse.Namespace:
+ parse = reqparse.RequestParser()
+ parse.add_argument('yangModel',
+ type=FileStorage,
+ location='files')
+ parse.add_argument('initialConfig',
+ type=FileStorage,
+ location='files')
+ parse.add_argument('moduleName', type=str)
+ parse.add_argument('yangModelName', type=str)
+ return parse.parse_args()
+
+
+api.add_resource(YangModelServer, '/model')
+
+if __name__ == '__main__':
+ logging.basicConfig(filename=os.path.dirname(__file__) + "/yang_loader.log",
+ filemode="w",
+ level=logging.DEBUG)
+ app.run(host='0.0.0.0', port='5002')
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/Application.cpp b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/Application.cpp
new file mode 100644
index 000000000..56c33f0de
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/Application.cpp
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+#include "Application.h"
+#include <cstdio>
+#include <unistd.h>
+#include "sysrepo/Session.hpp"
+#include "SysrepoCallback.h"
+
+Application::~Application() {
+ this->subscriber->unsubscribe();
+ this->session->session_stop();
+ sr_disconnect(this->connection->_conn);
+ std::cout << "Application closed correctly " << std::endl;
+}
+
+void Application::run() {
+ /*create kafka wrapper object*/
+ auto kafkaWrapper = std::make_shared<KafkaWrapper>(this->brokers,this->topic_name);
+
+ std::cout << "Application will watch for changes in " << module_name << std::endl;
+ /* connect to sysrepo */
+ this->connection = new sysrepo::Connection("example_application");
+ sysrepo::S_Connection conn(new sysrepo::Connection("example_application"));
+
+ /* start session */
+ sysrepo::S_Session sess(new sysrepo::Session(conn));
+
+ this->session = sess;
+ /* subscribe for changes in running config */
+ sysrepo::S_Subscribe subscribe(new sysrepo::Subscribe(sess));
+ std::shared_ptr<SysrepoCallback> cb(new SysrepoCallback(kafkaWrapper));
+
+ subscribe->module_change_subscribe(module_name, cb);
+ this->subscriber = subscribe;
+
+ /* read startup config */
+ std::cout << "\n ========== READING STARTUP CONFIG: ==========\n" << std::endl;
+
+ cb->print_current_config(sess, module_name);
+
+ std::cout << "\n ========== STARTUP CONFIG APPLIED AS RUNNING ==========\n" << std::endl;
+
+ /* loop until ctrl-c is pressed / SIGINT is received */
+ while (!exit_application) {
+ sleep(1000); /* or do some more useful work... */
+ }
+
+ std::cout << "Application exit requested, exiting." << std::endl;
+
+}
+
+Application::Application(const char *module_name, const char *brokers, const char *topic_name) {
+ this->module_name = module_name;
+ this->brokers = brokers;
+ this->topic_name = topic_name;
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/Application.h b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/Application.h
new file mode 100644
index 000000000..c41f7e28b
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/Application.h
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+#ifndef NETOPEER_CHANGE_SAVER_CPP_APPLICATION_H
+#define NETOPEER_CHANGE_SAVER_CPP_APPLICATION_H
+#include "sysrepo/Sysrepo.hpp"
+
+extern volatile int exit_application;
+
+class Application {
+private:
+ const char *module_name;
+ const char *brokers;
+ const char *topic_name;
+ sysrepo::S_Session session;
+ sysrepo::S_Subscribe subscriber;
+ sysrepo::Connection *connection;
+
+public:
+ Application(const char *module_name, const char *brokers, const char *topic_name);
+ ~Application();
+ void run();
+
+};
+
+#endif //NETOPEER_CHANGE_SAVER_CPP_APPLICATION_H
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/CMakeLists.txt b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/CMakeLists.txt
new file mode 100644
index 000000000..35a3a85ad
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.7)
+project(netopeer-change-saver)
+
+set(CMAKE_CXX_STANDARD 11)
+set(THREADS_PREFER_PTHREAD_FLAG ON)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+set (CMAKE_EXE_LINKER_FLAGS "-Wl,--unresolved-symbols=ignore-all")
+link_directories(.)
+
+find_package(Threads REQUIRED)
+
+add_executable(netopeer-change-saver main.cpp sysrepo.h Application.cpp Application.h KafkaWrapper.cpp KafkaWrapper.h
+ SysrepoCallback.cpp SysrepoCallback.h)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/libsysrepo-cpp.so
+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/bin)
+
+target_link_libraries(netopeer-change-saver libsysrepo-cpp.so)
+target_link_libraries(netopeer-change-saver libsysrepo.so)
+target_link_libraries(netopeer-change-saver Threads::Threads)
+target_link_libraries(netopeer-change-saver librdkafka.so) \ No newline at end of file
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/KafkaWrapper.cpp b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/KafkaWrapper.cpp
new file mode 100644
index 000000000..cd018a33f
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/KafkaWrapper.cpp
@@ -0,0 +1,105 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+#include "KafkaWrapper.h"
+#include <cstdlib>
+#include <cinttypes>
+#include <iostream>
+
+extern "C" {
+ rd_kafka_resp_err_t rd_kafka_last_error (void);
+ rd_kafka_resp_err_t rd_kafka_flush (rd_kafka_t *rk, int timeout_ms);
+}
+
+extern "C" {
+void kafka_delivery_report_callback(rd_kafka_t *rk, const rd_kafka_message_t *rkmessage, void *opaque) {
+#ifdef DEBUG
+ if (rkmessage->err)
+ std::cout<<"%% Message delivery failed: %s\n"<<rd_kafka_err2str(rkmessage->err)<<std::endl;
+ else
+ std::cout<<
+ "%% Message delivered ("<<rkmessage->len <<" bytes, partition " << rkmessage->partition <<")" << std::endl;
+ /* The rkmessage is destroyed automatically by librdkafka */
+#endif
+}
+}
+
+KafkaWrapper::KafkaWrapper(const char *brokers, const char *topic_name) {
+ this->brokers = brokers;
+ this->topic_name = topic_name;
+
+ init();
+}
+
+KafkaWrapper::~KafkaWrapper() {
+ std::cerr<<"%% Flushing final messages..."<<std::endl;
+ rd_kafka_flush(rk, 10 * 1000);
+ rd_kafka_destroy(rk);
+}
+
+void KafkaWrapper::init() {
+ /*Kafka stuff*/
+ conf = rd_kafka_conf_new();
+ if (rd_kafka_conf_set(conf, "bootstrap.servers", brokers, errstr, sizeof(errstr)) != RD_KAFKA_CONF_OK) {
+ perror(errstr);
+ exit(1);
+ }
+
+ rd_kafka_conf_set_dr_msg_cb(conf, kafka_delivery_report_callback);
+ rk = rd_kafka_new(RD_KAFKA_PRODUCER, conf, errstr, sizeof(errstr));
+ if (!rk) {
+ std::cerr<<"%% Failed to create new producer: %s\n"<<errstr<<std::endl;
+ exit(1);
+ }
+
+ rkt = rd_kafka_topic_new(rk, topic_name, nullptr);
+ if (!rkt) {
+ std::cerr<<"%% Failed to create topic object: %s\n"<<
+ rd_kafka_err2str(rd_kafka_last_error())<<std::endl;
+ rd_kafka_destroy(rk);
+ exit(1);
+ }
+}
+
+void KafkaWrapper::kafka_send_message(std::string message) {
+ size_t len = message.length();
+ int retry = 1;
+ while (retry) {
+#ifdef DEBUG
+ std::cout<<"Sending the message to topic...\n"<<std::endl;
+#endif
+ if (rd_kafka_produce(rkt, RD_KAFKA_PARTITION_UA, RD_KAFKA_MSG_F_COPY, (void *) message.c_str(), len, nullptr, 0,
+ nullptr)) {
+ retry = 1;
+ rd_kafka_resp_err_t last_error = rd_kafka_last_error();
+ std::cerr<<"%% Failed to produce to topic %s: %s\n"<<topic_name<<rd_kafka_err2str(last_error)<<std::endl;
+ if (last_error == RD_KAFKA_RESP_ERR__QUEUE_FULL) {
+ rd_kafka_poll(rk, 1000);
+ } else {
+ std::cerr<<"%% Enqueued message (%zd bytes) for topic %s\n"<<len<<topic_name<<std::endl;
+ }
+ } else {
+ retry = 0;
+ }
+ }
+ rd_kafka_poll(rk, 0/*non-blocking*/);
+}
+
+
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/KafkaWrapper.h b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/KafkaWrapper.h
new file mode 100644
index 000000000..804afa758
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/KafkaWrapper.h
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+#ifndef NETOPEER_CHANGE_SAVER_CPP_KAFKAWRAPPER_H
+#define NETOPEER_CHANGE_SAVER_CPP_KAFKAWRAPPER_H
+#include "librdkafka/rdkafka.h"
+#include <string>
+
+class KafkaWrapper {
+private:
+ char errstr[512];
+ const char *brokers;
+ const char *topic_name;
+ rd_kafka_t *rk;
+ rd_kafka_topic_t *rkt;
+ rd_kafka_conf_t *conf;
+
+ void init();
+
+public:
+ KafkaWrapper(const char *brokers, const char *topic_name);
+ ~KafkaWrapper();
+ void kafka_send_message(std::string message);
+};
+
+
+#endif //NETOPEER_CHANGE_SAVER_CPP_KAFKAWRAPPER_H
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/SysrepoCallback.cpp b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/SysrepoCallback.cpp
new file mode 100644
index 000000000..225fe03a4
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/SysrepoCallback.cpp
@@ -0,0 +1,108 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+#include "SysrepoCallback.h"
+#define CREATED "CREATED"
+#define DELETED "DELETED"
+#define MODIFIED "MODIFIED"
+#define MOVED "MOVED"
+#define XPATH_MAX_LEN 100
+
+
+int SysrepoCallback::module_change(sysrepo::S_Session sess, const char *module_name, sr_notif_event_t event, void *private_ctx) {
+ {
+ if (event == SR_EV_APPLY) {
+ char change_path[XPATH_MAX_LEN];
+
+ try {
+#ifdef DEBUG
+ std::cout << "\n ========== CHANGES: =============================================\n" << std::endl;
+#endif
+ snprintf(change_path, XPATH_MAX_LEN, "/%s:*", module_name);
+ auto it = sess->get_changes_iter(&change_path[0]);
+ while (auto change = sess->get_change_next(it)) {
+ std::string message = create_message(change);
+ std::cout<<message<<std::endl;
+ kafkaWrapper->kafka_send_message(message);
+ }
+#ifdef DEBUG
+ std::cout << "\n ========== END OF CHANGES =======================================\n" << std::endl;
+#endif
+ } catch( const std::exception& e ) {
+ std::cerr << e.what() << std::endl;
+ }
+ }
+ return SR_ERR_OK;
+ }
+}
+
+SysrepoCallback::SysrepoCallback(std::shared_ptr<KafkaWrapper> wrapper) {
+ this->kafkaWrapper = wrapper;
+}
+
+std::string SysrepoCallback::create_message(sysrepo::S_Change change) {
+ std::string change_details;
+ sysrepo::S_Val new_val = change->new_val();
+ sysrepo::S_Val old_val = change->old_val();
+
+ switch (change->oper()) {
+ case SR_OP_CREATED:
+ if (nullptr != new_val) {
+ change_details.append(CREATED).append(": ").append(new_val->to_string());
+ }
+ break;
+ case SR_OP_DELETED:
+ if (nullptr != old_val) {
+ change_details.append(DELETED).append(": ").append(old_val->to_string());
+ }
+ break;
+ case SR_OP_MODIFIED:
+ if (nullptr != old_val && nullptr != new_val) {
+ change_details.append(MODIFIED).append(": ").append(": old value: ").append(old_val->to_string())
+ .append(", new value: ").append(new_val->to_string());
+ }
+ break;
+ case SR_OP_MOVED:
+ if (nullptr != old_val && nullptr != new_val) {
+ change_details.append(MOVED).append(": ").append(new_val->to_string())
+ .append(" after ").append(old_val->to_string());
+ } else if (nullptr != new_val) {
+ change_details.append(MOVED).append(": ").append(new_val->xpath()).append(" last");
+ }
+ break;
+ }
+ return change_details;
+}
+
+void SysrepoCallback::print_current_config(sysrepo::S_Session session, const char *module_name) {
+ char select_xpath[XPATH_MAX_LEN];
+ try {
+ snprintf(select_xpath, XPATH_MAX_LEN, "/%s:*//*", module_name);
+
+ auto values = session->get_items(&select_xpath[0]);
+ if (values == nullptr)
+ return;
+
+ for(unsigned int i = 0; i < values->val_cnt(); i++)
+ std::cout << values->val(i)->to_string();
+ } catch( const std::exception& e ) {
+ std::cout << e.what() << std::endl;
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/SysrepoCallback.h b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/SysrepoCallback.h
new file mode 100644
index 000000000..7d2cd7221
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/SysrepoCallback.h
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+#ifndef NETOPEER_CHANGE_SAVER_CPP_SYSREPOCALLBACK_H
+#define NETOPEER_CHANGE_SAVER_CPP_SYSREPOCALLBACK_H
+#include "KafkaWrapper.h"
+#include "sysrepo/Session.hpp"
+#include <memory>
+
+class SysrepoCallback: public sysrepo::Callback {
+private:
+ std::shared_ptr<KafkaWrapper> kafkaWrapper;
+
+public:
+ explicit SysrepoCallback(std::shared_ptr<KafkaWrapper> wrapper);
+ void print_current_config(sysrepo::S_Session session, const char *module_name);
+
+private:
+ std::string create_message(sysrepo::S_Change change);
+ int module_change(sysrepo::S_Session sess, const char *module_name, sr_notif_event_t event, void *private_ctx);
+
+};
+
+
+#endif //NETOPEER_CHANGE_SAVER_CPP_SYSREPOCALLBACK_H
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/libsysrepo-cpp.so b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/libsysrepo-cpp.so
new file mode 100755
index 000000000..efdcc135d
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/libsysrepo-cpp.so
Binary files differ
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/main.cpp b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/main.cpp
new file mode 100644
index 000000000..0329f0552
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/main.cpp
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+#include <iostream>
+#include <csignal>
+#include "Application.h"
+
+volatile int exit_application = 0;
+
+void sigint_handler(int signum) {
+ std::cout << "Interrupt signal (" << signum << ") received." << std::endl;
+ exit_application = 1;
+}
+
+int main(int argc, char *argv[]) {
+ if (argc != 4) {
+ std::cerr<<"Usage: "<<argv[0]<<" <module_name> <broker> <topic> "<<std::endl;
+ return 1;
+ }
+
+ signal(SIGINT, sigint_handler);
+
+ const char *module_name = argv[1];
+ const char *brokers = argv[2];
+ const char *topic_name = argv[3];
+
+ Application application(module_name, brokers, topic_name);
+ application.run();
+
+ return 0;
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo.h b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo.h
new file mode 100644
index 000000000..9d541d1c0
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo.h
@@ -0,0 +1,2015 @@
+/**
+ * @file sysrepo.h
+ * @author Rastislav Szabo <raszabo@cisco.com>, Lukas Macko <lmacko@cisco.com>
+ * @brief Sysrepo Client Library public API.
+ *
+ * @copyright
+ * Copyright 2015 Cisco Systems, Inc.
+ *
+ * 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.
+ */
+
+#ifndef SYSREPO_H_
+#define SYSREPO_H_
+
+/**
+ * @defgroup cl Client Library
+ * @{
+ *
+ * @brief Provides the public API towards applications using sysrepo to store
+ * their configuration data, or towards management agents.
+ *
+ * Communicates with Sysrepo Engine (@ref cm), which is running either inside
+ * of dedicated sysrepo daemon, or within this library if daemon is not alive.
+ *
+ * Access to the sysrepo datastore is connection- and session- oriented. Before
+ * calling any data access/manipulation API, one needs to connect to the datastore
+ * via ::sr_connect and open a session via ::sr_session_start. One connection
+ * can serve multiple sessions.
+ *
+ * Each data access/manipulation request call is blocking - blocks the connection
+ * until the response from Sysrepo Engine comes, or until an error occurs. It is
+ * safe to call multiple requests on the same session (or different session that
+ * belongs to the same connection) from multiple threads at the same time,
+ * however it is not effective, since each call is blocked until previous one
+ * finishes. If you need fast multi-threaded access to sysrepo, use a dedicated
+ * connection for each thread.
+ *
+ * @see
+ * See @ref main_page "Sysrepo Introduction" for details about sysrepo architecture.
+ * @see
+ * @ref xp_page "XPath Addressing" is used for node identification in data-related calls.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+#ifdef __APPLE__
+ #include <sys/types.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Common typedefs and API
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Sysrepo connection context used to identify a connection to sysrepo datastore.
+ */
+typedef struct sr_conn_ctx_s sr_conn_ctx_t;
+
+/**
+ * @brief Sysrepo session context used to identify a configuration session.
+ */
+typedef struct sr_session_ctx_s sr_session_ctx_t;
+
+/**
+ * @brief Memory context used for efficient memory management for values, trees and GPB messages.
+ */
+typedef struct sr_mem_ctx_s sr_mem_ctx_t;
+
+/**
+ * @brief Possible types of an data element stored in the sysrepo datastore.
+ */
+typedef enum sr_type_e {
+ /* special types that does not contain any data */
+ SR_UNKNOWN_T, /**< Element unknown to sysrepo (unsupported element). */
+ SR_TREE_ITERATOR_T, /**< Special type of tree node used to store all data needed for iterative tree loading. */
+
+ SR_LIST_T, /**< List instance. ([RFC 6020 sec 7.8](http://tools.ietf.org/html/rfc6020#section-7.8)) */
+ SR_CONTAINER_T, /**< Non-presence container. ([RFC 6020 sec 7.5](http://tools.ietf.org/html/rfc6020#section-7.5)) */
+ SR_CONTAINER_PRESENCE_T, /**< Presence container. ([RFC 6020 sec 7.5.1](http://tools.ietf.org/html/rfc6020#section-7.5.1)) */
+ SR_LEAF_EMPTY_T, /**< A leaf that does not hold any value ([RFC 6020 sec 9.11](http://tools.ietf.org/html/rfc6020#section-9.11)) */
+ SR_NOTIFICATION_T, /**< Notification instance ([RFC 7095 sec 7.16](https://tools.ietf.org/html/rfc7950#section-7.16)) */
+
+ /* types containing some data */
+ SR_BINARY_T, /**< Base64-encoded binary data ([RFC 6020 sec 9.8](http://tools.ietf.org/html/rfc6020#section-9.8)) */
+ SR_BITS_T, /**< A set of bits or flags ([RFC 6020 sec 9.7](http://tools.ietf.org/html/rfc6020#section-9.7)) */
+ SR_BOOL_T, /**< A boolean value ([RFC 6020 sec 9.5](http://tools.ietf.org/html/rfc6020#section-9.5)) */
+ SR_DECIMAL64_T, /**< 64-bit signed decimal number ([RFC 6020 sec 9.3](http://tools.ietf.org/html/rfc6020#section-9.3)) */
+ SR_ENUM_T, /**< A string from enumerated strings list ([RFC 6020 sec 9.6](http://tools.ietf.org/html/rfc6020#section-9.6)) */
+ SR_IDENTITYREF_T, /**< A reference to an abstract identity ([RFC 6020 sec 9.10](http://tools.ietf.org/html/rfc6020#section-9.10)) */
+ SR_INSTANCEID_T, /**< References a data tree node ([RFC 6020 sec 9.13](http://tools.ietf.org/html/rfc6020#section-9.13)) */
+ SR_INT8_T, /**< 8-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ SR_INT16_T, /**< 16-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ SR_INT32_T, /**< 32-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ SR_INT64_T, /**< 64-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ SR_STRING_T, /**< Human-readable string ([RFC 6020 sec 9.4](http://tools.ietf.org/html/rfc6020#section-9.4)) */
+ SR_UINT8_T, /**< 8-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ SR_UINT16_T, /**< 16-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ SR_UINT32_T, /**< 32-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ SR_UINT64_T, /**< 64-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ SR_ANYXML_T, /**< Unknown chunk of XML ([RFC 6020 sec 7.10](https://tools.ietf.org/html/rfc6020#section-7.10)) */
+ SR_ANYDATA_T, /**< Unknown set of nodes, encoded in XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
+} sr_type_t;
+
+/**
+ * @brief Data of an element (if applicable), properly set according to the type.
+ */
+typedef union sr_data_u {
+ char *binary_val; /**< Base64-encoded binary data ([RFC 6020 sec 9.8](http://tools.ietf.org/html/rfc6020#section-9.8)) */
+ char *bits_val; /**< A set of bits or flags ([RFC 6020 sec 9.7](http://tools.ietf.org/html/rfc6020#section-9.7)) */
+ bool bool_val; /**< A boolean value ([RFC 6020 sec 9.5](http://tools.ietf.org/html/rfc6020#section-9.5)) */
+ double decimal64_val; /**< 64-bit signed decimal number ([RFC 6020 sec 9.3](http://tools.ietf.org/html/rfc6020#section-9.3)) */
+ char *enum_val; /**< A string from enumerated strings list ([RFC 6020 sec 9.6](http://tools.ietf.org/html/rfc6020#section-9.6)) */
+ char *identityref_val; /**< A reference to an abstract identity ([RFC 6020 sec 9.10](http://tools.ietf.org/html/rfc6020#section-9.10)) */
+ char *instanceid_val; /**< References a data tree node ([RFC 6020 sec 9.13](http://tools.ietf.org/html/rfc6020#section-9.13)) */
+ int8_t int8_val; /**< 8-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ int16_t int16_val; /**< 16-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ int32_t int32_val; /**< 32-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ int64_t int64_val; /**< 64-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ char *string_val; /**< Human-readable string ([RFC 6020 sec 9.4](http://tools.ietf.org/html/rfc6020#section-9.4)) */
+ uint8_t uint8_val; /**< 8-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ uint16_t uint16_val; /**< 16-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ uint32_t uint32_val; /**< 32-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ uint64_t uint64_val; /**< 64-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ char *anyxml_val; /**< Unknown chunk of XML ([RFC 6020 sec 7.10](https://tools.ietf.org/html/rfc6020#section-7.10)) */
+ char *anydata_val; /**< Unknown set of nodes, encoded in XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
+} sr_data_t;
+
+/**
+ * @brief Structure that contains value of an data element stored in the sysrepo datastore.
+ */
+typedef struct sr_val_s {
+ /**
+ * Memory context used internally by Sysrepo for efficient storage
+ * and conversion of this structure.
+ */
+ sr_mem_ctx_t *_sr_mem;
+
+ /**
+ * XPath identifier of the data element, as defined in
+ * @ref xp_page "Path Addressing" documentation
+ */
+ char *xpath;
+
+ /** Type of an element. */
+ sr_type_t type;
+
+ /**
+ * Flag for node with default value (applicable only for leaves).
+ * It is set to TRUE only if the value was *implicitly* set by the datastore as per
+ * module schema. Explicitly set/modified data element (through the sysrepo API) always
+ * has this flag unset regardless of the entered value.
+ */
+ bool dflt;
+
+ /** Data of an element (if applicable), properly set according to the type. */
+ sr_data_t data;
+
+} sr_val_t;
+
+/**
+ * @brief A data element stored in the sysrepo datastore represented as a tree node.
+ *
+ * @note Can be safely casted to ::sr_val_t, only *xpath* member will point to node name rather
+ * than to an actual xpath.
+ */
+typedef struct sr_node_s {
+ /**
+ * Memory context used internally by Sysrepo for efficient storage
+ * and conversion of this structure.
+ */
+ sr_mem_ctx_t *_sr_mem;
+
+ /** Name of the node. */
+ char *name;
+
+ /** Type of an element. */
+ sr_type_t type;
+
+ /** Flag for default node (applicable only for leaves). */
+ bool dflt;
+
+ /** Data of an element (if applicable), properly set according to the type. */
+ sr_data_t data;
+
+ /**
+ * Name of the module that defines scheme of this node.
+ * NULL if it is the same as that of the predecessor.
+ */
+ char *module_name;
+
+ /** Pointer to the parent node (NULL in case of root node). */
+ struct sr_node_s *parent;
+
+ /** Pointer to the next sibling node (NULL if there is no one). */
+ struct sr_node_s *next;
+
+ /** Pointer to the previous sibling node (NULL if there is no one). */
+ struct sr_node_s *prev;
+
+ /** Pointer to the first child node (NULL if this is a leaf). */
+ struct sr_node_s *first_child;
+
+ /** Pointer to the last child node (NULL if this is a leaf). */
+ struct sr_node_s *last_child;
+} sr_node_t;
+
+/**
+ * @brief Sysrepo error codes.
+ */
+typedef enum sr_error_e {
+ SR_ERR_OK = 0, /**< No error. */
+ SR_ERR_INVAL_ARG, /**< Invalid argument. */
+ SR_ERR_NOMEM, /**< Not enough memory. */
+ SR_ERR_NOT_FOUND, /**< Item not found. */
+ SR_ERR_INTERNAL, /**< Other internal error. */
+ SR_ERR_INIT_FAILED, /**< Sysrepo infra initialization failed. */
+ SR_ERR_IO, /**< Input/Output error. */
+ SR_ERR_DISCONNECT, /**< The peer disconnected. */
+ SR_ERR_MALFORMED_MSG, /**< Malformed message. */
+ SR_ERR_UNSUPPORTED, /**< Unsupported operation requested. */
+ SR_ERR_UNKNOWN_MODEL, /**< Request includes unknown schema */
+ SR_ERR_BAD_ELEMENT, /**< Unknown element in existing schema */
+ SR_ERR_VALIDATION_FAILED, /**< Validation of the changes failed. */
+ SR_ERR_OPERATION_FAILED, /**< An operation failed. */
+ SR_ERR_DATA_EXISTS, /**< Item already exists. */
+ SR_ERR_DATA_MISSING, /**< Item does not exists. */
+ SR_ERR_UNAUTHORIZED, /**< Operation not authorized. */
+ SR_ERR_INVAL_USER, /**< Invalid username. */
+ SR_ERR_LOCKED, /**< Requested resource is already locked. */
+ SR_ERR_TIME_OUT, /**< Time out has expired. */
+ SR_ERR_RESTART_NEEDED, /**< Sysrepo Engine restart is needed. */
+ SR_ERR_VERSION_MISMATCH, /**< Incompatible client library used to communicate with sysrepo. */
+} sr_error_t;
+
+/**
+ * @brief Detailed sysrepo error information.
+ */
+typedef struct sr_error_info_s {
+ const char *message; /**< Error message. */
+ const char *xpath; /**< XPath to the node where the error has been discovered. */
+} sr_error_info_t;
+
+/**
+ * @brief Returns the error message corresponding to the error code.
+ *
+ * @param[in] err_code Error code.
+ *
+ * @return Error message (statically allocated, do not free).
+ */
+const char *sr_strerror(int err_code);
+
+/**
+ * @brief Log levels used to determine if message of certain severity should be printed.
+ */
+typedef enum {
+ SR_LL_NONE, /**< Do not print any messages. */
+ SR_LL_ERR, /**< Print only error messages. */
+ SR_LL_WRN, /**< Print error and warning messages. */
+ SR_LL_INF, /**< Besides errors and warnings, print some other informational messages. */
+ SR_LL_DBG, /**< Print all messages including some development debug messages. */
+} sr_log_level_t;
+
+/**
+ * @brief Enables / disables / changes log level (verbosity) of logging to
+ * standard error output.
+ *
+ * By default, logging to stderr is disabled. Setting log level to any value
+ * other than SR_LL_NONE enables the logging to stderr. Setting log level
+ * back to SR_LL_NONE disables the logging to stderr.
+ *
+ * @param[in] log_level requested log level (verbosity).
+ */
+void sr_log_stderr(sr_log_level_t log_level);
+
+/**
+ * @brief Enables / disables / changes log level (verbosity) of logging to system log.
+ *
+ * By default, logging into syslog is disabled. Setting log level to any value
+ * other than SR_LL_NONE enables the logging into syslog. Setting log level
+ * back to SR_LL_NONE disables the logging into syslog.
+ *
+ * @note Please note that enabling logging into syslog will overwrite your syslog
+ * connection settings (calls openlog), if you are connected to syslog already.
+ *
+ * @param[in] log_level requested log level (verbosity).
+ */
+void sr_log_syslog(sr_log_level_t log_level);
+
+/**
+ * @brief Sets callback that will be called when a log entry would be populated.
+ *
+ * @param[in] level Verbosity level of the log entry.
+ * @param[in] message Message of the log entry.
+ */
+typedef void (*sr_log_cb)(sr_log_level_t level, const char *message);
+
+/**
+ * @brief Sets callback that will be called when a log entry would be populated.
+ * Callback will be called for each message with any log level.
+ *
+ * @param[in] log_callback Callback to be called when a log entry would populated.
+ */
+void sr_log_set_cb(sr_log_cb log_callback);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Connection / Session Management
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Flags used to override default connection handling by ::sr_connect call.
+ */
+typedef enum sr_conn_flag_e {
+ SR_CONN_DEFAULT = 0, /**< Default behavior - instantiate library-local Sysrepo Engine if
+ the connection to sysrepo daemon is not possible. */
+ SR_CONN_DAEMON_REQUIRED = 1, /**< Require daemon connection - do not instantiate library-local Sysrepo Engine
+ if the library cannot connect to the sysrepo daemon (and return an error instead). */
+ SR_CONN_DAEMON_START = 2, /**< If sysrepo daemon is not running, and SR_CONN_DAEMON_REQUIRED was specified,
+ start it (only if the process calling ::sr_connect is running under root privileges). */
+} sr_conn_flag_t;
+
+/**
+ * @brief Options overriding default connection handling by ::sr_connect call,
+ * it is supposed to be bitwise OR-ed value of any ::sr_conn_flag_t flags.
+ */
+typedef uint32_t sr_conn_options_t;
+
+/**
+ * @brief Flags used to override default session handling (used by ::sr_session_start
+ * and ::sr_session_start_user calls).
+ */
+typedef enum sr_session_flag_e {
+ SR_SESS_DEFAULT = 0, /**< Default (normal) session behavior. */
+ SR_SESS_CONFIG_ONLY = 1, /**< Session will process only configuration data (e.g. sysrepo won't
+ return any state data by ::sr_get_items / ::sr_get_items_iter calls). */
+ SR_SESS_ENABLE_NACM = 2, /**< Enable NETCONF access control for this session (disabled by default). */
+
+ SR_SESS_MUTABLE_OPTS = 3 /**< Bit-mask of options that can be set by the user
+ (immutable flags are defined in sysrepo.proto file). */
+} sr_session_flag_t;
+
+/**
+ * @brief Options overriding default connection session handling,
+ * it is supposed to be bitwise OR-ed value of any ::sr_session_flag_t flags.
+ */
+typedef uint32_t sr_sess_options_t;
+
+/**
+ * @brief Data stores that sysrepo supports. Both are editable via implicit candidate.
+ * To make changes permanent in edited datastore ::sr_commit must be issued.
+ * @see @ref ds_page "Datastores & Sessions" information page.
+ */
+typedef enum sr_datastore_e {
+ SR_DS_STARTUP = 0, /**< Contains configuration data that should be loaded by the controlled application when it starts. */
+ SR_DS_RUNNING = 1, /**< Contains currently applied configuration and state data of a running application.
+ @note This datastore is supported only by applications that subscribe for notifications
+ about the changes made in the datastore (e.g. ::sr_module_change_subscribe). */
+ SR_DS_CANDIDATE = 2, /**< Contains configuration that can be manipulated without impacting the current configuration.
+ Its content is set to the content of running datastore by default. Changes made within
+ the candidate can be later committed to the running datastore or copied to any datastore.
+
+ @note The main difference between working with running and candidate datastore is in commit
+ operation - commit of candidate session causes the content of running configuration to be set
+ the value of the candidate configuration (running datastore is overwritten), whereas commit of
+ runnnig session merges the changes made within the session with the actual state of running. */
+} sr_datastore_t;
+
+/**
+ * @brief Connects to the sysrepo datastore (Sysrepo Engine).
+ *
+ * @note If the client library loses connection to the Sysrepo Engine during
+ * the lifetime of the application, all Sysrepo API calls will start returning
+ * ::SR_ERR_DISCONNECT error on active sessions. In this case, the application is supposed to reconnect
+ * with another ::sr_connect call and restart all lost sessions.
+ *
+ * @param[in] app_name Name of the application connecting to the datastore
+ * (can be a static string). Used only for accounting purposes.
+ * @param[in] opts Options overriding default connection handling by this call.
+ * @param[out] conn_ctx Connection context that can be used for subsequent API calls
+ * (automatically allocated, it is supposed to be released by the caller using ::sr_disconnect).
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_connect(const char *app_name, const sr_conn_options_t opts, sr_conn_ctx_t **conn_ctx);
+
+/**
+ * @brief Disconnects from the sysrepo datastore (Sysrepo Engine).
+ *
+ * Cleans up and frees connection context allocated by ::sr_connect. All sessions
+ * started within the connection will be automatically stopped and cleaned up too.
+ *
+ * @param[in] conn_ctx Connection context acquired with ::sr_connect call.
+ */
+void sr_disconnect(sr_conn_ctx_t *conn_ctx);
+
+/**
+ * @brief Starts a new configuration session.
+ *
+ * @see @ref ds_page "Datastores & Sessions" for more information about datastores and sessions.
+ *
+ * @param[in] conn_ctx Connection context acquired with ::sr_connect call.
+ * @param[in] datastore Datastore on which all sysrepo functions within this
+ * session will operate. Later on, datastore can be later changed using
+ * ::sr_session_switch_ds call. Functionality of some sysrepo calls does not depend on
+ * datastore. If your session will contain just calls like these, you can pass
+ * any valid value (e.g. SR_RUNNING).
+ * @param[in] opts Options overriding default session handling.
+ * @param[out] session Session context that can be used for subsequent API
+ * calls (automatically allocated, can be released by calling ::sr_session_stop).
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_session_start(sr_conn_ctx_t *conn_ctx, const sr_datastore_t datastore,
+ const sr_sess_options_t opts, sr_session_ctx_t **session);
+
+/**
+ * @brief Starts a new configuration session on behalf of a different user.
+ *
+ * This call is intended for northbound access to sysrepo from management
+ * applications, that need sysrepo to authorize the operations not only
+ * against the user under which the management application is running, but
+ * also against another user (e.g. user that connected to the management application).
+ *
+ * @note Be aware that authorization of specified user may fail with unexpected
+ * errors in case that the client library uses its own Sysrepo Engine at the
+ * moment and your process in not running under root privileges. To prevent
+ * this situation, consider specifying SR_CONN_DAEMON_REQUIRED flag by
+ * ::sr_connect call or using ::sr_session_start instead of this function.
+ *
+ * @see @ref ds_page "Datastores & Sessions" for more information about datastores and sessions.
+ *
+ * @param[in] conn_ctx Connection context acquired with ::sr_connect call.
+ * @param[in] user_name Effective user name used to authorize the access to
+ * datastore (in addition to automatically-detected real user name).
+ * @param[in] datastore Datastore on which all sysrepo functions within this
+ * session will operate. Functionality of some sysrepo calls does not depend on
+ * datastore. If your session will contain just calls like these, you can pass
+ * any valid value (e.g. SR_RUNNING).
+ * @param[in] opts Options overriding default session handling.
+ * @param[out] session Session context that can be used for subsequent API calls
+ * (automatically allocated, it is supposed to be released by caller using ::sr_session_stop).
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_session_start_user(sr_conn_ctx_t *conn_ctx, const char *user_name, const sr_datastore_t datastore,
+ const sr_sess_options_t opts, sr_session_ctx_t **session);
+
+/**
+ * @brief Stops current session and releases resources tied to the session.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_session_stop(sr_session_ctx_t *session);
+
+/**
+ * @brief Refreshes configuration data cached within the session and starts
+ * operating on fresh data loaded from the datastore.
+ *
+ * Call this function in case that you leave session open for longer time period
+ * and you expect that the data in the datastore may have been changed since
+ * last data (re)load (which occurs by ::sr_session_start, ::sr_commit and
+ * ::sr_discard_changes).
+ *
+ * @see @ref ds_page "Datastores & Sessions" for information about session data caching.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_session_refresh(sr_session_ctx_t *session);
+
+/**
+ * @brief Checks aliveness and validity of the session & connection tied to it.
+ *
+ * If the connection to the Sysrepo Engine has been lost in the meantime, returns SR_ERR_DICONNECT.
+ * In this case, the application is supposed to stop the session (::sr_session_stop), disconnect (::sr_disconnect)
+ * and then reconnect (::sr_connect) and start a new session (::sr_session_start).
+ *
+ * @note If the client library loses connection to the Sysrepo Engine during the lifetime of the application,
+ * all Sysrepo API calls will start returning SR_ERR_DISCONNECT error on active sessions. This is the primary
+ * mechanism that can be used to detect connection issues, ::sr_session_check is just an addition to it. Since
+ * ::sr_session_check sends a message to the Sysrepo Engine and waits for the response, it costs some extra overhead
+ * in contrast to catching SR_ERR_DISCONNECT error.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ *
+ * @return Error code (SR_ERR_OK in case that the session is healthy,
+ * SR_ERR_DICONNECT in case that connection to the Sysrepo Engine has been lost).
+ */
+int sr_session_check(sr_session_ctx_t *session);
+
+/**
+ * @brief Changes datastore to which the session is tied to. All subsequent
+ * calls will be issued on the chosen datastore.
+ *
+ * @param [in] session
+ * @param [in] ds
+ * @return Error code (SR_ERR_OK on success)
+ */
+int sr_session_switch_ds(sr_session_ctx_t *session, sr_datastore_t ds);
+
+/**
+ * @brief Alter the session options. E.g.: set/unset SR_SESS_CONFIG_ONLY flag.
+ *
+ * @param [in] session
+ * @param [in] opts - new value for session options
+ * @return Error code (SR_ERR_OK on success)
+ */
+int sr_session_set_options(sr_session_ctx_t *session, const sr_sess_options_t opts);
+
+/**
+ * @brief Retrieves detailed information about the error that has occurred
+ * during the last operation executed within provided session.
+ *
+ * If multiple errors has occurred within the last operation, only the first
+ * one is returned. This call is sufficient for all data retrieval and data
+ * manipulation functions that operate on single-item basis. For operations
+ * such as ::sr_validate or ::sr_commit where multiple errors can occur,
+ * use ::sr_get_last_errors instead.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[out] error_info Detailed error information. Be aware that
+ * returned pointer may change by the next API call executed within the provided
+ * session, so it's not safe to use this function by concurrent access to the
+ * same session within multiple threads. Do not free or modify returned values.
+ *
+ * @return Error code of the last operation executed within provided session.
+ */
+int sr_get_last_error(sr_session_ctx_t *session, const sr_error_info_t **error_info);
+
+/**
+ * @brief Retrieves detailed information about all errors that have occurred
+ * during the last operation executed within provided session.
+ *
+ * Use this call instead of ::sr_get_last_error by operations where multiple
+ * errors can occur, such as ::sr_validate or ::sr_commit.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[out] error_info Array of detailed error information. Be aware that
+ * returned pointer may change by the next API call executed within the provided
+ * session, so it's not safe to use this function by concurrent access to the
+ * same session within multiple threads. Do not free or modify returned values.
+ * @param[out] error_cnt Number of errors returned in the error_info array.
+ *
+ * @return Error code of the last operation executed within provided session.
+ */
+int sr_get_last_errors(sr_session_ctx_t *session, const sr_error_info_t **error_info, size_t *error_cnt);
+
+/**
+ * @brief Sets detailed error information into provided session. Used to notify
+ * the client library about errors that occurred in application code.
+ *
+ * @note Intended for commit verifiers (notification session) - the call has no
+ * impact on any other sessions.
+ *
+ * @param[in] session Session context passed into notification callback.
+ * @param[in] message Human-readable error message.
+ * @param[in] xpath XPath to the node where the error has occurred. NULL value
+ * is also accepted.
+ *
+ * @return Error code (SR_ERR_OK on success)
+ */
+int sr_set_error(sr_session_ctx_t *session, const char *message, const char *xpath);
+
+/**
+ * @brief Returns the assigned id of the session. Can be used to pair the session with
+ * netconf-config-change notification initiator.
+ * @param [in] session
+ * @return session id or 0 in case of error
+ */
+uint32_t sr_session_get_id(sr_session_ctx_t *session);
+
+////////////////////////////////////////////////////////////////////////////////
+// Data Retrieval API (get / get-config functionality)
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Structure that contains information about one particular schema file installed in sysrepo.
+ */
+typedef struct sr_sch_revision_s {
+ const char *revision; /**< Revision of the module/submodule. */
+ const char *file_path_yang; /**< Absolute path to file where the module/submodule is stored (YANG format). */
+ const char *file_path_yin; /**< Absolute path to file where the module/submodule is stored (.yin format). */
+} sr_sch_revision_t;
+
+/**
+ * @brief Structure that contains information about submodules of a module installed in sysrepo.
+ */
+typedef struct sr_sch_submodule_s {
+ const char *submodule_name; /**< Submodule name. */
+ sr_sch_revision_t revision; /**< Revision of the submodule. */
+} sr_sch_submodule_t;
+
+/**
+ * @brief Structure that contains information about a module installed in sysrepo.
+ */
+typedef struct sr_schema_s {
+ /**
+ * Memory context used internally by Sysrepo for efficient storage
+ * and conversion of this structure.
+ */
+ sr_mem_ctx_t *_sr_mem;
+
+ const char *module_name; /**< Name of the module. */
+ const char *ns; /**< Namespace of the module used in @ref xp_page "XPath". */
+ const char *prefix; /**< Prefix of the module. */
+ bool installed; /**< TRUE if the module was explicitly installed. */
+ bool implemented; /**< TRUE if the module is implemented (does not have to be installed),
+ not just imported. */
+
+ sr_sch_revision_t revision; /**< Revision the module. */
+
+ sr_sch_submodule_t *submodules; /**< Array of all installed submodules of the module. */
+ size_t submodule_count; /**< Number of module's submodules. */
+
+ char **enabled_features; /**< Array of enabled features */
+ size_t enabled_feature_cnt; /**< Number of enabled feature */
+} sr_schema_t;
+
+/**
+ * @brief Format types of ::sr_get_schema result
+ */
+typedef enum sr_schema_format_e {
+ SR_SCHEMA_YANG, /**< YANG format */
+ SR_SCHEMA_YIN /**< YIN format */
+} sr_schema_format_t;
+
+/**
+ * @brief Iterator used for accessing data nodes via ::sr_get_items_iter call.
+ */
+typedef struct sr_val_iter_s sr_val_iter_t;
+
+/**
+ * @brief Retrieves list of schemas installed in the sysrepo datastore.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[out] schemas Array of installed schemas information (allocated by
+ * the function, it is supposed to be freed by caller using ::sr_free_schemas call).
+ * @param[out] schema_cnt Number of schemas returned in the array.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_list_schemas(sr_session_ctx_t *session, sr_schema_t **schemas, size_t *schema_cnt);
+
+/**
+ * @brief Retrieves the content of specified schema file. If the module
+ * can not be found SR_ERR_NOT_FOUND is returned.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] module_name Name of the requested module.
+ * @param[in] revision Requested revision of the module. If NULL
+ * is passed, the latest revision will be returned.
+ * @param[in] submodule_name Name of the requested submodule. Pass NULL if you are
+ * requesting the content of the main module.
+ * @param[in] format of the returned schema
+ * @param[out] schema_content Content of the specified schema file. Automatically
+ * allocated by the function, should be freed by the caller.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_get_schema(sr_session_ctx_t *session, const char *module_name, const char *revision,
+ const char *submodule_name, sr_schema_format_t format, char **schema_content);
+
+/**
+ * @brief Retrieves the content of the specified submodule schema file. If the submodule
+ * cannot be found, SR_ERR_NOT_FOUND is returned.
+ *
+ * @param[in] session Session context acquired from ::sr_session_start call.
+ * @param[in] submodule_name Name of the requested submodule.
+ * @param[in] submodule_revision Requested revision of the submodule. If NULL
+ * is passed, the latest revision will be returned.
+ * @param[in] format of the returned schema.
+ * @param[out] schema_content Content of the specified schema file. Automatically
+ * allocated by the function, should be freed by the caller.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_get_submodule_schema(sr_session_ctx_t *session, const char *submodule_name, const char *submodule_revision,
+ sr_schema_format_t format, char **schema_content);
+
+/**
+ * @brief Retrieves a single data element stored under provided XPath. If multiple
+ * nodes matches the xpath SR_ERR_INVAL_ARG is returned.
+ *
+ * If the xpath identifies an empty leaf, a list or a container, the value
+ * has no data filled in and its type is set properly (SR_LEAF_EMPTY_T / SR_LIST_T / SR_CONTAINER_T / SR_CONTAINER_PRESENCE_T).
+ *
+ * @see @ref xp_page "Path Addressing" documentation, or
+ * https://tools.ietf.org/html/draft-ietf-netmod-yang-json#section-6.11
+ * for XPath syntax used for identification of yang nodes in sysrepo calls.
+ *
+ * @see Use ::sr_get_items or ::sr_get_items_iter for retrieving larger chunks
+ * of data from the datastore. Since they retrieve the data from datastore in
+ * larger chunks, they can work much more efficiently than multiple ::sr_get_item calls.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifier of the data element to be retrieved.
+ * @param[out] value Structure containing information about requested element
+ * (allocated by the function, it is supposed to be freed by the caller using ::sr_free_val).
+ *
+ * @return Error code (SR_ERR_OK on success)
+ */
+int sr_get_item(sr_session_ctx_t *session, const char *xpath, sr_val_t **value);
+
+/**
+ * @brief Retrieves an array of data elements matching provided XPath
+ *
+ * All data elements are transferred within one message from the datastore,
+ * which is much more efficient that calling multiple ::sr_get_item calls.
+ *
+ * If the user does not have read permission to access certain nodes, these
+ * won't be part of the result. SR_ERR_NOT_FOUND will be returned if there are
+ * no nodes matching xpath in the data tree, or the user does not have read permission to access them.
+ *
+ * If the response contains too many elements time out may be exceeded, SR_ERR_TIME_OUT
+ * will be returned, use ::sr_get_items_iter.
+ *
+ * @see @ref xp_page "Path Addressing" documentation
+ * for Path syntax used for identification of yang nodes in sysrepo calls.
+ *
+ * @see ::sr_get_items_iter can be used for the same purpose as ::sr_get_items
+ * call if you expect that ::sr_get_items could return too large data sets.
+ * Since ::sr_get_items_iter also retrieves the data from datastore in larger chunks,
+ * in can still work very efficiently for large datasets.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifier of the data element to be retrieved.
+ * @param[out] values Array of structures containing information about requested data elements
+ * (allocated by the function, it is supposed to be freed by the caller using ::sr_free_values).
+ * @param[out] value_cnt Number of returned elements in the values array.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_get_items(sr_session_ctx_t *session, const char *xpath, sr_val_t **values, size_t *value_cnt);
+
+/**
+ * @brief Creates an iterator for retrieving of the data elements stored under provided xpath.
+ *
+ * Requested data elements are transferred from the datastore in larger chunks
+ * of pre-defined size, which is much more efficient that calling multiple
+ * ::sr_get_item calls, and may be less memory demanding than calling ::sr_get_items
+ * on very large datasets.
+ *
+ * @see @ref xp_page "Path Addressing" documentation, or
+ * https://tools.ietf.org/html/draft-ietf-netmod-yang-json#section-6.11
+ * for XPath syntax used for identification of yang nodes in sysrepo calls.
+ *
+ * @see ::sr_get_item_next for iterating over returned data elements.
+ * @note Iterator allows to iterate through the values once. To start iteration
+ * from the beginning new iterator must be created.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifier of the data element / subtree to be retrieved.
+ * @param[out] iter Iterator context that can be used to retrieve individual data
+ * elements via ::sr_get_item_next calls. Allocated by the function, should be
+ * freed with ::sr_free_val_iter.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_get_items_iter(sr_session_ctx_t *session, const char *xpath, sr_val_iter_t **iter);
+
+/**
+ * @brief Returns the next item from the dataset of provided iterator created
+ * by ::sr_get_items_iter call. If there is no item left SR_ERR_NOT_FOUND is returned.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in,out] iter Iterator acquired with ::sr_get_items_iter call.
+ * @param[out] value Structure containing information about requested element
+ * (allocated by the function, it is supposed to be freed by the caller using ::sr_free_val).
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_get_item_next(sr_session_ctx_t *session, sr_val_iter_t *iter, sr_val_t **value);
+
+/**
+ * @brief Flags used to customize the behaviour of ::sr_get_subtree and ::sr_get_subtrees calls.
+ */
+typedef enum sr_get_subtree_flag_e {
+ /**
+ * Default get-subtree(s) behaviour.
+ * All matched subtrees are sent with all their content in one message.
+ */
+ SR_GET_SUBTREE_DEFAULT = 0,
+
+ /**
+ * The iterative get-subtree(s) behaviour.
+ * The matched subtrees are sent in chunks and only as needed while they are iterated
+ * through using functions ::sr_node_get_child, ::sr_node_get_next_sibling and
+ * ::sr_node_get_parent from "sysrepo/trees.h". This behaviour gives much better
+ * performance than the default one if only a small portion of matched subtree(s) is
+ * actually iterated through.
+ * @note It is considered a programming error to access \p next, \p prev, \p parent,
+ * \p first_child and \p last_child data members of ::sr_node_t on a partially loaded tree.
+ */
+ SR_GET_SUBTREE_ITERATIVE = 1
+} sr_get_subtree_flag_t;
+
+/**
+ * @brief Options for get-subtree and get-subtrees operations.
+ * It is supposed to be bitwise OR-ed value of any ::sr_get_subtree_flag_t flags.
+ */
+typedef uint32_t sr_get_subtree_options_t;
+
+/**
+ * @brief Retrieves a single subtree whose root node is stored under the provided XPath.
+ * If multiple nodes matches the xpath SR_ERR_INVAL_ARG is returned.
+ *
+ * The functions returns values and all associated information stored under the root node and
+ * all its descendants. While the same data can be obtained using ::sr_get_items in combination
+ * with the expressive power of XPath addressing, the recursive nature of the output data type
+ * also preserves the hierarchical relationships between data elements.
+ *
+ * Values of internal nodes of the subtree have no data filled in and their type is set properly
+ * (SR_LIST_T / SR_CONTAINER_T / SR_CONTAINER_PRESENCE_T), whereas leaf nodes are carrying actual
+ * data (apart from SR_LEAF_EMPTY_T).
+ *
+ * @see @ref xp_page "Path Addressing" documentation
+ * for XPath syntax used for identification of yang nodes in sysrepo calls.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifier referencing the root node of the subtree to be retrieved.
+ * @param[in] opts Options overriding default behavior of this operation.
+ * @param[out] subtree Nested structure storing all data of the requested subtree
+ * (allocated by the function, it is supposed to be freed by the caller using ::sr_free_tree).
+ *
+ * @return Error code (SR_ERR_OK on success)
+ */
+int sr_get_subtree(sr_session_ctx_t *session, const char *xpath, sr_get_subtree_options_t opts,
+ sr_node_t **subtree);
+
+/**
+ * @brief Retrieves an array of subtrees whose root nodes match the provided XPath.
+ *
+ * If the user does not have read permission to access certain nodes, these together with
+ * their descendants won't be part of the result. SR_ERR_NOT_FOUND will be returned if there are
+ * no nodes matching xpath in the data tree, or the user does not have read permission to access them.
+ *
+ * Subtrees that match the provided XPath are not merged even if they overlap. This significantly
+ * simplifies the implementation and decreases the cost of this operation. The downside is that
+ * the user must choose the XPath carefully. If the subtree selection process results in too many
+ * node overlaps, the cost of the operation may easily outshine the benefits. As an example,
+ * a common XPath expression "//." is normally used to select all nodes in a data tree, but for this
+ * operation it would result in an excessive duplication of transfered data elements.
+ * Since you get all the descendants of each matched node implicitly, you probably should not need
+ * to use XPath wildcards deeper than on the top-level.
+ * (i.e. "/." is preferred alternative to "//." for get-subtrees operation).
+ *
+ * If the response contains too many elements time out may be exceeded, SR_ERR_TIME_OUT
+ * will be returned.
+ *
+ * @see @ref xp_page "Path Addressing" documentation, or
+ * https://tools.ietf.org/html/draft-ietf-netmod-yang-json#section-6.11
+ * for XPath syntax used for identification of yang nodes in sysrepo calls.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifier referencing root nodes of subtrees to be retrieved.
+ * @param[in] opts Options overriding default behavior of this operation.
+ * @param[out] subtrees Array of nested structures storing all data of the requested subtrees
+ * (allocated by the function, it is supposed to be freed by the caller using ::sr_free_trees).
+ * @param[out] subtree_cnt Number of returned trees in the subtrees array.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_get_subtrees(sr_session_ctx_t *session, const char *xpath, sr_get_subtree_options_t opts,
+ sr_node_t **subtrees, size_t *subtree_cnt);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Data Manipulation API (edit-config functionality)
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Flags used to override default behavior of data manipulation calls.
+ */
+typedef enum sr_edit_flag_e {
+ SR_EDIT_DEFAULT = 0, /**< Default behavior - recursive and non-strict. */
+ SR_EDIT_NON_RECURSIVE = 1, /**< Non-recursive behavior:
+ by ::sr_set_item, all preceding nodes (parents) of the identified element must exist,
+ by ::sr_delete_item xpath must not identify an non-empty list or non-empty container. */
+ SR_EDIT_STRICT = 2 /**< Strict behavior:
+ by ::sr_set_item the identified element must not exist (similar to netconf create operation),
+ by ::sr_delete_item the identified element must exist (similar to netconf delete operation). */
+} sr_edit_flag_t;
+
+/**
+ * @brief Options overriding default behavior of data manipulation calls,
+ * it is supposed to be bitwise OR-ed value of any ::sr_edit_flag_t flags.
+ */
+typedef uint32_t sr_edit_options_t;
+
+/**
+ * @brief Options for specifying move direction of ::sr_move_item call.
+ */
+typedef enum sr_move_position_e {
+ SR_MOVE_BEFORE = 0, /**< Move the specified item before the selected sibling. */
+ SR_MOVE_AFTER = 1, /**< Move the specified item after the selected. */
+ SR_MOVE_FIRST = 2, /**< Move the specified item to the position of the first child. */
+ SR_MOVE_LAST = 3, /**< Move the specified item to the position of the last child. */
+} sr_move_position_t;
+
+/**
+ * @brief Sets the value of the leaf, leaf-list, list or presence container.
+ *
+ * With default options it recursively creates all missing nodes (containers and
+ * lists including their key leaves) in the xpath to the specified node (can be
+ * turned off with SR_EDIT_NON_RECURSIVE option). If SR_EDIT_STRICT flag is set,
+ * the node must not exist (otherwise an error is returned).
+ *
+ * To create a list use xpath with key values included and pass NULL as value argument.
+ *
+ * Setting of a leaf-list value appends the value at the end of the leaf-list.
+ * A value of leaf-list can be specified either by predicate in xpath or by value argument.
+ * If both are present, value argument is ignored and xpath predicate is used.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifier of the data element to be set.
+ * @param[in] value Value to be set on specified xpath. xpath member of the
+ * ::sr_val_t structure can be NULL. Value will be copied - can be allocated on stack.
+ * @param[in] opts Options overriding default behavior of this call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_set_item(sr_session_ctx_t *session, const char *xpath, const sr_val_t *value, const sr_edit_options_t opts);
+
+
+/**
+ * @brief Functions is similar to ::sr_set_item with the difference that the value to be set
+ * is provided as string.
+ * @param [in] session Session context acquired with ::sr_session_start call.
+ * @param [in] xpath @ref xp_page "Data Path" identifier of the data element to be set.
+ * @param [in] value string representation of the value to be set
+ * @param [in] opts same as for ::sr_set_item
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_set_item_str(sr_session_ctx_t *session, const char *xpath, const char *value, const sr_edit_options_t opts);
+/**
+ * @brief Deletes the nodes under the specified xpath.
+ *
+ * To delete non-empty lists or containers SR_EDIT_NON_RECURSIVE flag must not be set.
+ * If SR_EDIT_STRICT flag is set the specified node must must exist in the datastore.
+ * If the xpath includes the list keys, the specified list instance is deleted.
+ * If the xpath to list does not include keys, all instances of the list are deleted.
+ * SR_ERR_UNAUTHORIZED will be returned if the user does not have write permission to any affected node.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifier of the data element to be deleted.
+ * @param[in] opts Options overriding default behavior of this call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ **/
+int sr_delete_item(sr_session_ctx_t *session, const char *xpath, const sr_edit_options_t opts);
+
+/**
+ * @brief Move the instance of an user-ordered list or leaf-list to the specified position.
+ *
+ * Item can be move to the first or last position or positioned relatively to its sibling.
+ * @note To determine current order, you can issue a ::sr_get_items call
+ * (without specifying keys of the list in question).
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifier of the data element to be moved.
+ * @param[in] position Requested move direction.
+ * @param[in] relative_item xpath Identifier of the data element that is used
+ * to determine relative position, used only if position argument is SR_MOVE_BEFORE or SR_MOVE_AFTER.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_move_item(sr_session_ctx_t *session, const char *xpath, const sr_move_position_t position, const char *relative_item);
+
+/**
+ * @brief Perform the validation of changes made in current session, but do not
+ * commit nor discard them.
+ *
+ * Provides only YANG validation, commit verify subscribers won't be notified in this case.
+ *
+ * @see Use ::sr_get_last_errors to retrieve error information if the validation
+ * returned with an error.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_validate(sr_session_ctx_t *session);
+
+/**
+ * @brief Apply changes made in current session.
+ *
+ * @note Note that in case that you are committing to the running datstore, you also
+ * need to copy the config to startup to make changes permanent after restart.
+ *
+ * @see Use ::sr_get_last_errors to retrieve error information if the commit
+ * operation returned with an error.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_commit(sr_session_ctx_t *session);
+
+/**
+ * @brief Discard non-committed changes made in current session.
+ *
+ * @note Since the function effectively clears all the cached data within the session,
+ * the next operation will operate on fresh data loaded from the datastore
+ * (i.e. no need to call ::sr_session_refresh afterwards).
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_discard_changes(sr_session_ctx_t *session);
+
+/**
+ * @brief Replaces an entire configuration datastore with the contents of
+ * another complete configuration datastore. If the module is specified, limits
+ * the copy operation only to one specified module. If it's not specified,
+ * the operation is performed on all modules that are currently active in the
+ * source datastore.
+ *
+ * If the target datastore exists, it is overwritten. Otherwise, a new one is created.
+ *
+ * @note ::sr_session_refresh is needed to see the result of a copy-config operation
+ * in a session apart from the case when SR_DS_CANDIDATE is the destination datastore.
+ * Since the candidate is not shared among sessions, data trees are copied only to the
+ * canidate in the session issuing the copy-config operation.
+ *
+ * @note Operation may fail, if it tries to copy a not enabled configuration to the
+ * running datastore.
+ *
+ * @note \p session \p dst_datastore uncommitted changes will get discarded.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] module_name If specified, only limits the copy operation only to
+ * one specified module.
+ * @param[in] src_datastore Source datastore.
+ * @param[in] dst_datastore Destination datastore.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_copy_config(sr_session_ctx_t *session, const char *module_name,
+ sr_datastore_t src_datastore, sr_datastore_t dst_datastore);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Locking API
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Locks the datastore which the session is tied to. If there is
+ * a module locked by the other session SR_ERR_LOCKED is returned.
+ * Operation fails if there is a modified data tree in session.
+ *
+ * All data models within the datastore will be locked for writing until
+ * ::sr_unlock_datastore is called or until the session is stopped or terminated
+ * for any reason.
+ *
+ * The lock operation will not be allowed if the user does not have sufficient
+ * permissions for writing into each of the data models in the datastore.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_lock_datastore(sr_session_ctx_t *session);
+
+/**
+ * @brief Unlocks the datastore which the session is tied to.
+ *
+ * All data models within the datastore will be unlocked if they were locked
+ * by this session.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_unlock_datastore(sr_session_ctx_t *session);
+
+/**
+ * @brief Locks specified data module within the datastore which the session
+ * is tied to. Operation fails if the data tree has been modified.
+ *
+ * Specified data module will be locked for writing in the datastore until
+ * ::sr_unlock_module is called or until the session is stopped or terminated
+ * for any reason.
+ *
+ * The lock operation will not be allowed if the user does not have sufficient
+ * permissions for writing into the specified data module.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] module_name Name of the module to be locked.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_lock_module(sr_session_ctx_t *session, const char *module_name);
+
+/**
+ * @brief Unlocks specified data module within the datastore which the session
+ * is tied to.
+ *
+ * Specified data module will be unlocked if was locked in the datastore
+ * by this session.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] module_name Name of the module to be unlocked.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_unlock_module(sr_session_ctx_t *session, const char *module_name);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Change Notifications API
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Flags used to override default handling of subscriptions.
+ */
+typedef enum sr_subscr_flag_e {
+ /**
+ * @brief Default behavior of the subscription. In case of ::sr_module_change_subscribe and
+ * ::sr_subtree_change_subscribe calls it means that:
+ *
+ * - the subscriber is the "owner" of the subscribed data tree and and the data tree will be enabled in the running
+ * datastore while this subscription is alive (if not already, can be changed using ::SR_SUBSCR_PASSIVE flag),
+ * - configuration data of the subscribed module or subtree is copied from startup to running datastore
+ * (only if the module was not enabled before),
+ * - the callback will be called twice, once with ::SR_EV_VERIFY event and once with ::SR_EV_APPLY / ::SR_EV_ABORT
+ * event passed in (can be changed with ::SR_SUBSCR_APPLY_ONLY flag).
+ */
+ SR_SUBSCR_DEFAULT = 0,
+
+ /**
+ * @brief This option enables the application to re-use an already existing subscription context previously returned
+ * from any sr_*_subscribe call instead of requesting the creation of a new one. In that case a single
+ * ::sr_unsubscribe call unsubscribes from all subscriptions filed within the context.
+ */
+ SR_SUBSCR_CTX_REUSE = 1,
+
+ /**
+ * @brief The subscriber is not the "owner" of the subscribed data tree, just a passive watcher for changes.
+ * When this option is passed in to ::sr_module_change_subscribe or ::sr_subtree_change_subscribe,
+ * the subscription will have no effect on the presence of the subtree in the running datastore.
+ */
+ SR_SUBSCR_PASSIVE = 2,
+
+ /**
+ * @brief The subscriber does not support verification of the changes and wants to be notified only after
+ * the changes has been applied in the datastore, without the possibility to deny them
+ * (it will receive only ::SR_EV_APPLY events).
+ */
+ SR_SUBSCR_APPLY_ONLY = 4,
+
+ /**
+ * @brief The subscriber wants ::SR_EV_ENABLED notifications to be sent to them.
+ */
+ SR_SUBSCR_EV_ENABLED = 8,
+
+ /**
+ * @brief The subscriber will not receive ::SR_EV_ABORT if he returns an error in verify phase
+ * (if the commit is refused by other verifier ::SR_EV_ABORT will be delivered).
+ */
+ SR_SUBSCR_NO_ABORT_FOR_REFUSED_CFG = 16,
+
+ /**
+ * @brief No real-time notifications will be delivered until ::sr_event_notif_replay is called
+ * and replay has finished (::SR_EV_NOTIF_T_REPLAY_COMPLETE is delivered).
+ */
+ SR_SUBSCR_NOTIF_REPLAY_FIRST = 32,
+} sr_subscr_flag_t;
+
+/**
+ * @brief Type of the notification event that has occurred (passed to notification callbacks).
+ *
+ * @note Each change is normally notified twice: first as ::SR_EV_VERIFY event and then as ::SR_EV_APPLY or ::SR_EV_ABORT
+ * event. If the subscriber does not support verification, it can subscribe only to ::SR_EV_APPLY event by providing
+ * ::SR_SUBSCR_APPLY_ONLY subscription flag.
+ */
+typedef enum sr_notif_event_e {
+ SR_EV_VERIFY, /**< Occurs just before the changes are committed to the datastore,
+ the subscriber is supposed to verify that the changes are valid and can be applied
+ and prepare all resources required for the changes. The subscriber can still deny the changes
+ in this phase by returning an error from the callback. */
+ SR_EV_APPLY, /**< Occurs just after the changes have been successfully committed to the datastore,
+ the subscriber is supposed to apply the changes now, but it cannot deny the changes in this
+ phase anymore (any returned errors are just logged and ignored). */
+ SR_EV_ABORT, /**< Occurs in case that the commit transaction has failed (possibly because one of the verifiers
+ has denied the change / returned an error). The subscriber is supposed to return the managed
+ application to the state before the commit. Any returned errors are just logged and ignored. */
+ SR_EV_ENABLED, /**< Occurs just after the subscription. Subscriber gets notified about configuration that was copied
+ from startup to running. This allows to reuse the callback for applying changes made in running to
+ reflect the changes when the configuration is copied from startup to running during subscription process */
+} sr_notif_event_t;
+
+/**
+ * @brief Type of the operation made on an item, used by changeset retrieval in ::sr_get_change_next.
+ */
+typedef enum sr_change_oper_e {
+ SR_OP_CREATED, /**< The item has been created by the change. */
+ SR_OP_MODIFIED, /**< The value of the item has been modified by the change. */
+ SR_OP_DELETED, /**< The item has been deleted by the change. */
+ SR_OP_MOVED, /**< The item has been moved in the subtree by the change (applicable for leaf-lists and user-ordered lists). */
+} sr_change_oper_t;
+
+/**
+ * @brief State of a module as returned by the ::sr_module_install_cb callback.
+ */
+typedef enum sr_module_state_e {
+ SR_MS_UNINSTALLED, /**< The module is not installed in the sysrepo repository. */
+ SR_MS_IMPORTED, /**< The module has been implicitly installed into the sysrepo repository
+ as it is imported by another implemented/imported module. */
+ SR_MS_IMPLEMENTED /**< The module has been explicitly installed into the sysrepo repository by the user. */
+} sr_module_state_t;
+
+/**
+ * @brief Sysrepo subscription context returned from sr_*_subscribe calls,
+ * it is supposed to be released by the caller using ::sr_unsubscribe call.
+ */
+typedef struct sr_subscription_ctx_s sr_subscription_ctx_t;
+
+/**
+ * @brief Iterator used for retrieval of a changeset using ::sr_get_changes_iter call.
+ */
+typedef struct sr_change_iter_s sr_change_iter_t;
+
+/**
+ * @brief Options overriding default behavior of subscriptions,
+ * it is supposed to be a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ */
+typedef uint32_t sr_subscr_options_t;
+
+/**
+ * @brief Callback to be called by the event of changing any running datastore
+ * content within the specified module. Subscribe to it by ::sr_module_change_subscribe call.
+ *
+ * @param[in] session Automatically-created session that can be used for obtaining changed data
+ * (e.g. by ::sr_get_changes_iter call ot ::sr_get_item -like calls). Do not stop this session.
+ * @param[in] module_name Name of the module where the change has occurred.
+ * @param[in] event Type of the notification event that has occurred.
+ * @param[in] private_ctx Private context opaque to sysrepo, as passed to
+ * ::sr_module_change_subscribe call.
+ */
+typedef int (*sr_module_change_cb)(sr_session_ctx_t *session, const char *module_name,
+ sr_notif_event_t event, void *private_ctx);
+
+/**
+ * @brief Callback to be called by the event of changing any running datastore
+ * content within the specified subtree. Subscribe to it by ::sr_subtree_change_subscribe call.
+ *
+ * @param[in] session Automatically-created session that can be used for obtaining changed data
+ * (e.g. by ::sr_get_changes_iter call or ::sr_get_item -like calls). Do not stop this session.
+ * @param[in] xpath @ref xp_page "Data Path" of the subtree where the change has occurred.
+ * @param[in] event Type of the notification event that has occurred.
+ * @param[in] private_ctx Private context opaque to sysrepo, as passed to
+ * ::sr_subtree_change_subscribe call.
+ */
+typedef int (*sr_subtree_change_cb)(sr_session_ctx_t *session, const char *xpath,
+ sr_notif_event_t event, void *private_ctx);
+
+/**
+ * @brief Callback to be called by the event of installation / uninstallation
+ * of a new module into sysrepo. Subscribe to it by ::sr_module_install_subscribe call.
+ *
+ * @param[in] module_name Name of the newly installed / uinstalled module.
+ * @param[in] revision Revision of the newly installed module (if specified
+ * within the YANG model).
+ * @param[in] state The new state of the module (uninstalled vs. imported vs. implemented).
+ * @param[in] private_ctx Private context opaque to sysrepo, as passed to
+ * ::sr_module_install_subscribe call.
+ */
+typedef void (*sr_module_install_cb)(const char *module_name, const char *revision, sr_module_state_t state,
+ void *private_ctx);
+
+/**
+ * @brief Callback to be called by the event of enabling / disabling of
+ * a YANG feature within a module. Subscribe to it by ::sr_feature_enable_subscribe call.
+ *
+ * @param[in] module_name Name of the module where the feature has been enabled / disabled.
+ * @param[in] feature_name Name of the feature that has been enabled / disabled.
+ * @param[in] enabled TRUE if the feature has been enabled, FALSE if disabled.
+ * @param[in] private_ctx Private context opaque to sysrepo, as passed to
+ * ::sr_feature_enable_subscribe call.
+ */
+typedef void (*sr_feature_enable_cb)(const char *module_name, const char *feature_name, bool enabled, void *private_ctx);
+
+/**
+ * @brief Subscribes for notifications about the changes made within specified
+ * module in running datastore.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] module_name Name of the module of interest for change notifications.
+ * @param[in] callback Callback to be called when the change in the datastore occurs.
+ * @param[in] private_ctx Private context passed to the callback function, opaque to sysrepo.
+ * @param[in] priority Specifies the order in which the callbacks will be called (callbacks with higher
+ * priority will be called sooner, callbacks with the priority of 0 will be called at the end).
+ * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
+ * @note An existing context may be passed in in case that SR_SUBSCR_CTX_REUSE option is specified.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_module_change_subscribe(sr_session_ctx_t *session, const char *module_name, sr_module_change_cb callback,
+ void *private_ctx, uint32_t priority, sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);
+
+/**
+ * @brief Subscribes for notifications about the changes made within specified
+ * subtree in running datastore.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifier of the subtree of the interest for change notifications.
+ * @param[in] callback Callback to be called when the change in the datastore occurs.
+ * @param[in] private_ctx Private context passed to the callback function, opaque to sysrepo.
+ * @param[in] priority Specifies the order in which the callbacks will be called (callbacks with higher
+ * priority will be called sooner, callbacks with the priority of 0 will be called at the end).
+ * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
+ * @note An existing context may be passed in in case that SR_SUBSCR_CTX_REUSE option is specified.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_subtree_change_subscribe(sr_session_ctx_t *session, const char *xpath, sr_subtree_change_cb callback,
+ void *private_ctx, uint32_t priority, sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);
+
+/**
+ * @brief Subscribes for notifications about installation / uninstallation
+ * of a new module into sysrepo.
+ *
+ * Mainly intended for northbound management applications that need to be
+ * always aware of all active modules installed in sysrepo.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] callback Callback to be called when the event occurs.
+ * @param[in] private_ctx Private context passed to the callback function, opaque to sysrepo.
+ * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
+ * @note An existing context may be passed in in case that SR_SUBSCR_CTX_REUSE option is specified.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_module_install_subscribe(sr_session_ctx_t *session, sr_module_install_cb callback, void *private_ctx,
+ sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);
+
+/**
+ * @brief Subscribes for notifications about enabling / disabling of
+ * a YANG feature within a module.
+ *
+ * Mainly intended for northbound management applications that need to be
+ * always aware of all active features within the modules installed in sysrepo.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] callback Callback to be called when the event occurs.
+ * @param[in] private_ctx Private context passed to the callback function, opaque to sysrepo.
+ * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
+ * @note An existing context may be passed in in case that SR_SUBSCR_CTX_REUSE option is specified.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_feature_enable_subscribe(sr_session_ctx_t *session, sr_feature_enable_cb callback, void *private_ctx,
+ sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);
+
+/**
+ * @brief Unsubscribes from a subscription acquired by any of sr_*_subscribe
+ * calls and releases all subscription-related data.
+ *
+ * @note In case that the same subscription context was used to subscribe for
+ * multiple subscriptions, unsubscribes from all of them.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call. Does not
+ * need to be the same as used for subscribing. NULL can be passed too, in that case
+ * a temporary session used for unsubscribe will be automatically created by sysrepo.
+ * @param[in] subscription Subscription context acquired by any of sr_*_subscribe calls.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_unsubscribe(sr_session_ctx_t *session, sr_subscription_ctx_t *subscription);
+
+/**
+ * @brief Creates an iterator for retrieving of the changeset (list of newly
+ * added / removed / modified nodes) in notification callbacks.
+ *
+ * @see ::sr_get_change_next for iterating over the changeset using this iterator.
+ *
+ * @param[in] session Session context as passed to notication the callbacks (e.g.
+ * ::sr_module_change_cb or ::sr_subtree_change_cb). Will not work with any other sessions.
+ * @param[in] xpath @ref xp_page "Data Path" identifier of the subtree from which the changeset
+ * should be obtained. Only XPaths that would be accepted by ::sr_subtree_change_subscribe are allowed.
+ * @param[out] iter Iterator context that can be used to retrieve individual changes using
+ * ::sr_get_change_next calls. Allocated by the function, should be freed with ::sr_free_change_iter.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_get_changes_iter(sr_session_ctx_t *session, const char *xpath, sr_change_iter_t **iter);
+
+/**
+ * @brief Returns the next change from the changeset of provided iterator created
+ * by ::sr_get_changes_iter call. If there is no item left, SR_ERR_NOT_FOUND is returned.
+ *
+ * @note If the operation is ::SR_OP_MOVED the meaning of new_value and old value argument is
+ * as follows - the value pointed by new_value was moved after the old_value. If the
+ * old value is NULL it was moved to the first position.
+ *
+ * @param[in] session Session context as passed to notication the callbacks (e.g.
+ * ::sr_module_change_cb or ::sr_subtree_change_cb). Will not work with any other sessions.
+ * @param[in,out] iter Iterator acquired with ::sr_get_changes_iter call.
+ * @param[out] operation Type of the operation made on the returned item.
+ * @param[out] old_value Old value of the item (the value before the change).
+ * NULL in case that the item has been just created (operation == SR_OP_CREATED).
+ * @param[out] new_value New (modified) value of the the item. NULL in case that
+ * the item has been just deleted (operation == SR_OP_DELETED).
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_get_change_next(sr_session_ctx_t *session, sr_change_iter_t *iter, sr_change_oper_t *operation,
+ sr_val_t **old_value, sr_val_t **new_value);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// RPC (Remote Procedure Calls) API
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Check if the owner of this session is authorized by NACM to invoke the protocol
+ * operation defined in a (installed) YANG module under the given xpath (as RPC or Action).
+ *
+ * This call is intended for northbound management applications that need to implement
+ * the NETCONF Access Control Model (RFC 6536) to restrict the protocol operations that
+ * each user is authorized to execute.
+ *
+ * NETCONF access control is already included in the processing of ::sr_rpc_send,
+ * ::sr_rpc_send_tree, ::sr_action_send and ::sr_action_send_tree and thus it should be
+ * sufficient to call this function only prior to executing any of the NETCONF standard
+ * protocol operations as they cannot be always directly translated to a single sysrepo
+ * API call.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifying the protocol operation.
+ * @param[out] permitted TRUE if the user is permitted to execute the given operation, FALSE otherwise.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_check_exec_permission(sr_session_ctx_t *session, const char *xpath, bool *permitted);
+
+/**
+ * @brief Callback to be called by the delivery of RPC specified by xpath.
+ * Subscribe to it by ::sr_rpc_subscribe call.
+ *
+ * @param[in] xpath @ref xp_page "Data Path" identifying the RPC.
+ * @param[in] input Array of input parameters.
+ * @param[in] input_cnt Number of input parameters.
+ * @param[out] output Array of output parameters. Should be allocated on heap,
+ * will be freed by sysrepo after sending of the RPC response.
+ * @param[out] output_cnt Number of output parameters.
+ * @param[in] private_ctx Private context opaque to sysrepo, as passed to ::sr_rpc_subscribe call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+typedef int (*sr_rpc_cb)(const char *xpath, const sr_val_t *input, const size_t input_cnt,
+ sr_val_t **output, size_t *output_cnt, void *private_ctx);
+
+/**
+ * @brief Callback to be called by the delivery of RPC specified by xpath.
+ * This RPC callback variant operates with sysrepo trees rather than with sysrepo values,
+ * use it with ::sr_rpc_subscribe_tree and ::sr_rpc_send_tree.
+ *
+ * @param[in] xpath @ref xp_page "Data Path" identifying the RPC.
+ * @param[in] input Array of input parameters (represented as trees).
+ * @param[in] input_cnt Number of input parameters.
+ * @param[out] output Array of output parameters (represented as trees). Should be allocated on heap,
+ * will be freed by sysrepo after sending of the RPC response.
+ * @param[out] output_cnt Number of output parameters.
+ * @param[in] private_ctx Private context opaque to sysrepo, as passed to ::sr_rpc_subscribe_tree call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+typedef int (*sr_rpc_tree_cb)(const char *xpath, const sr_node_t *input, const size_t input_cnt,
+ sr_node_t **output, size_t *output_cnt, void *private_ctx);
+
+/**
+ * @brief Subscribes for delivery of RPC specified by xpath.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Schema Path" identifying the RPC.
+ * @param[in] callback Callback to be called when the RPC is called.
+ * @param[in] private_ctx Private context passed to the callback function, opaque to sysrepo.
+ * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
+ * @note An existing context may be passed in case that SR_SUBSCR_CTX_REUSE option is specified.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_rpc_subscribe(sr_session_ctx_t *session, const char *xpath, sr_rpc_cb callback, void *private_ctx,
+ sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);
+
+/**
+ * @brief Subscribes for delivery of RPC specified by xpath. Unlike ::sr_rpc_subscribe, this
+ * function expects callback of type ::sr_rpc_tree_cb, therefore use this version if you prefer
+ * to manipulate with RPC input and output data organized in a list of trees rather than as a flat
+ * enumeration of all values.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Schema Path" identifying the RPC.
+ * @param[in] callback Callback to be called when the RPC is called.
+ * @param[in] private_ctx Private context passed to the callback function, opaque to sysrepo.
+ * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
+ * @note An existing context may be passed in case that SR_SUBSCR_CTX_REUSE option is specified.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_rpc_subscribe_tree(sr_session_ctx_t *session, const char *xpath, sr_rpc_tree_cb callback,
+ void *private_ctx, sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);
+
+/**
+ * @brief Sends a RPC specified by xpath and waits for the result.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifying the RPC.
+ * @param[in] input Array of input parameters (array of all nodes that hold some
+ * data in RPC input subtree - same as ::sr_get_items would return).
+ * @param[in] input_cnt Number of input parameters.
+ * @param[out] output Array of output parameters (all nodes that hold some data
+ * in RPC output subtree). Will be allocated by sysrepo and should be freed by
+ * caller using ::sr_free_values.
+ * @param[out] output_cnt Number of output parameters.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_rpc_send(sr_session_ctx_t *session, const char *xpath,
+ const sr_val_t *input, const size_t input_cnt, sr_val_t **output, size_t *output_cnt);
+
+/**
+ * @brief Sends a RPC specified by xpath and waits for the result. Input and output data
+ * are represented as arrays of subtrees reflecting the scheme of RPC arguments.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifying the RPC.
+ * @param[in] input Array of input parameters (organized in trees).
+ * @param[in] input_cnt Number of input parameters.
+ * @param[out] output Array of output parameters (organized in trees).
+ * Will be allocated by sysrepo and should be freed by caller using ::sr_free_trees.
+ * @param[out] output_cnt Number of output parameters.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_rpc_send_tree(sr_session_ctx_t *session, const char *xpath,
+ const sr_node_t *input, const size_t input_cnt, sr_node_t **output, size_t *output_cnt);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Action API
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Callback to be called by the delivery of Action (operation connected to a specific data node)
+ * specified by xpath. Subscribe to it by ::sr_action_subscribe call.
+ * @see This type is an alias for @ref sr_rpc_cb "the RPC callback type"
+ */
+typedef sr_rpc_cb sr_action_cb;
+
+/**
+ * @brief Callback to be called by the delivery of Action (operation connected to a specific data node)
+ * specified by xpath.
+ * This callback variant operates with sysrepo trees rather than with sysrepo values,
+ * use it with ::sr_action_subscribe_tree and ::sr_action_send_tree.
+ * @see This type is an alias for tree variant of @ref sr_rpc_tree_cb "the RPC callback "
+ */
+typedef sr_rpc_tree_cb sr_action_tree_cb;
+
+/**
+ * @brief Subscribes for delivery of Action specified by xpath.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Schema Path" identifying the Action.
+ * @param[in] callback Callback to be called when the Action is called.
+ * @param[in] private_ctx Private context passed to the callback function, opaque to sysrepo.
+ * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
+ * @note An existing context may be passed in case that SR_SUBSCR_CTX_REUSE option is specified.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_action_subscribe(sr_session_ctx_t *session, const char *xpath, sr_action_cb callback, void *private_ctx,
+ sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);
+
+/**
+ * @brief Subscribes for delivery of Action specified by xpath. Unlike ::sr_action_subscribe, this
+ * function expects callback of type ::sr_action_tree_cb, therefore use this version if you prefer
+ * to manipulate with Action input and output data organized in a list of trees rather than as a flat
+ * enumeration of all values.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Schema Path" identifying the Action.
+ * @param[in] callback Callback to be called when the Action is called.
+ * @param[in] private_ctx Private context passed to the callback function, opaque to sysrepo.
+ * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
+ * @note An existing context may be passed in case that SR_SUBSCR_CTX_REUSE option is specified.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_action_subscribe_tree(sr_session_ctx_t *session, const char *xpath, sr_action_tree_cb callback,
+ void *private_ctx, sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);
+
+/**
+ * @brief Executes an action specified by xpath and waits for the result.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifying the Action.
+ * @param[in] input Array of input parameters (array of all nodes that hold some
+ * data in Action input subtree - same as ::sr_get_items would return).
+ * @param[in] input_cnt Number of input parameters.
+ * @param[out] output Array of output parameters (all nodes that hold some data
+ * in Action output subtree). Will be allocated by sysrepo and should be freed by
+ * caller using ::sr_free_values.
+ * @param[out] output_cnt Number of output parameters.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_action_send(sr_session_ctx_t *session, const char *xpath,
+ const sr_val_t *input, const size_t input_cnt, sr_val_t **output, size_t *output_cnt);
+
+/**
+ * @brief Executes an action specified by xpath and waits for the result. Input and output data
+ * are represented as arrays of subtrees reflecting the scheme of Action arguments.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifying the Action.
+ * @param[in] input Array of input parameters (organized in trees).
+ * @param[in] input_cnt Number of input parameters.
+ * @param[out] output Array of output parameters (organized in trees).
+ * Will be allocated by sysrepo and should be freed by caller using ::sr_free_trees.
+ * @param[out] output_cnt Number of output parameters.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_action_send_tree(sr_session_ctx_t *session, const char *xpath,
+ const sr_node_t *input, const size_t input_cnt, sr_node_t **output, size_t *output_cnt);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Event Notifications API
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Type of the notification passed to the ::sr_event_notif_cb and ::sr_event_notif_tree_cb callbacks.
+ */
+typedef enum sr_ev_notif_type_e {
+ SR_EV_NOTIF_T_REALTIME, /**< Real-time notification. The only possible type if you don't use ::sr_event_notif_replay. */
+ SR_EV_NOTIF_T_REPLAY, /**< Replayed notification. */
+ SR_EV_NOTIF_T_REPLAY_COMPLETE, /**< Not a real notification, just a signal that the notification replay has completed
+ (all the stored notifications from the given time interval have been delivered). */
+ SR_EV_NOTIF_T_REPLAY_STOP, /**< Not a real notification, just a signal that replay stop time has been reached
+ (delivered only if stop_time was specified to ::sr_event_notif_replay). */
+} sr_ev_notif_type_t;
+
+/**
+ * @brief Flags used to override default notification handling i the datastore.
+ */
+typedef enum sr_ev_notif_flag_e {
+ SR_EV_NOTIF_DEFAULT = 0, /**< Notification will be handled normally. */
+ SR_EV_NOTIF_EPHEMERAL = 1, /**< Notification will not be stored in the notification store
+ (and therefore will be also delivered faster). */
+} sr_ev_notif_flag_t;
+
+/**
+ * @brief Callback to be called by the delivery of event notification specified by xpath.
+ * Subscribe to it by ::sr_event_notif_subscribe call.
+ *
+ * @param[in] notif_type Type of the notification.
+ * @param[in] xpath @ref xp_page "Data Path" identifying the event notification.
+ * @param[in] values Array of all nodes that hold some data in event notification subtree.
+ * @param[in] values_cnt Number of items inside the values array.
+ * @param[in] timestamp Time when the notification was generated
+ * @param[in] private_ctx Private context opaque to sysrepo,
+ * as passed to ::sr_event_notif_subscribe call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+typedef void (*sr_event_notif_cb)(const sr_ev_notif_type_t notif_type, const char *xpath,
+ const sr_val_t *values, const size_t values_cnt, time_t timestamp, void *private_ctx);
+
+/**
+ * @brief Callback to be called by the delivery of event notification specified by xpath.
+ * This callback variant operates with sysrepo trees rather than with sysrepo values,
+ * use it with ::sr_event_notif_subscribe_tree and ::sr_event_notif_send_tree.
+ *
+ * @param[in] notif_type Type of the notification.
+ * @param[in] xpath @ref xp_page "Data Path" identifying the event notification.
+ * @param[in] trees Array of subtrees carrying event notification data.
+ * @param[in] tree_cnt Number of subtrees with data.
+ * @param[in] timestamp Time when the notification was generated
+ * @param[in] private_ctx Private context opaque to sysrepo, as passed to ::sr_event_notif_subscribe_tree call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+typedef void (*sr_event_notif_tree_cb)(const sr_ev_notif_type_t notif_type, const char *xpath,
+ const sr_node_t *trees, const size_t tree_cnt, time_t timestamp, void *private_ctx);
+
+/**
+ * @brief Subscribes for delivery of an event notification specified by xpath.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Schema Path" identifying one event notification or special
+ * path in the form of a module name in which the whole module is subscribed to.
+ * @param[in] callback Callback to be called when the event notification is send.
+ * @param[in] private_ctx Private context passed to the callback function, opaque to sysrepo.
+ * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
+ * @note An existing context may be passed in case that SR_SUBSCR_CTX_REUSE option is specified.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_event_notif_subscribe(sr_session_ctx_t *session, const char *xpath,
+ sr_event_notif_cb callback, void *private_ctx, sr_subscr_options_t opts,
+ sr_subscription_ctx_t **subscription);
+
+/**
+ * @brief Subscribes for delivery of event notification specified by xpath.
+ * Unlike ::sr_event_notif_subscribe, this function expects callback of type ::sr_event_notif_tree_cb,
+ * therefore use this version if you prefer to manipulate with event notification data organized
+ * in a list of trees rather than as a flat enumeration of all values.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Schema Path" identifying one event notification or special
+ * path in the form of a module name in which the whole module is subscribed to.
+ * @param[in] callback Callback to be called when the event notification is called.
+ * @param[in] private_ctx Private context passed to the callback function, opaque to sysrepo.
+ * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
+ * @note An existing context may be passed in case that SR_SUBSCR_CTX_REUSE option is specified.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_event_notif_subscribe_tree(sr_session_ctx_t *session, const char *xpath,
+ sr_event_notif_tree_cb callback, void *private_ctx, sr_subscr_options_t opts,
+ sr_subscription_ctx_t **subscription);
+
+/**
+ * @brief Sends an event notification specified by xpath and waits for the result.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifying the event notification.
+ * @param[in] values Array of all nodes that hold some data in event notification subtree
+ * (same as ::sr_get_items would return).
+ * @param[in] values_cnt Number of items inside the values array.
+ * @param[in] opts Options overriding default handling of the notification, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_ev_notif_flag_t flags.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_event_notif_send(sr_session_ctx_t *session, const char *xpath, const sr_val_t *values,
+ const size_t values_cnt, sr_ev_notif_flag_t opts);
+
+/**
+ * @brief Sends an event notification specified by xpath and waits for the result.
+ * The notification data are represented as arrays of subtrees reflecting the scheme
+ * of the event notification.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifying the RPC.
+ * @param[in] trees Array of subtrees carrying event notification data.
+ * @param[in] tree_cnt Number of subtrees with data.
+ * @param[in] opts Options overriding default handling of the notification, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_ev_notif_flag_t flags.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_event_notif_send_tree(sr_session_ctx_t *session, const char *xpath, const sr_node_t *trees,
+ const size_t tree_cnt, sr_ev_notif_flag_t opts);
+
+/**
+ * @brief Replays already generated notifications stored in the notification store related to
+ * the provided notification subscription (or subscriptions, in case that ::SR_SUBSCR_CTX_REUSE
+ * was used). Notification callbacks of the given susbscriptions will be called with the type set to
+ * ::SR_EV_NOTIF_T_REPLAY, ::SR_EV_NOTIF_T_REPLAY_COMPLETE or ::SR_EV_NOTIF_T_REPLAY_STOP.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] subscription Session context acquired with ::sr_session_start call.
+ * @param[in] start_time Starting time of the desired time window for notification replay.
+ * @param[in] stop_time End time of the desired time window for notification replay. If set to 0,
+ * no stop time will be applied (all notifications up to the current time will be delivered,
+ * ::SR_EV_NOTIF_T_REPLAY_STOP notification won't be delivered).
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_event_notif_replay(sr_session_ctx_t *session, sr_subscription_ctx_t *subscription,
+ time_t start_time, time_t stop_time);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Operational Data API
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Callback to be called when operational data at the selected level is requested.
+ * Subscribe to it by ::sr_dp_get_items_subscribe call.
+ *
+ * Callback handler is supposed to provide data of all nodes at the level selected by the xpath argument:
+ *
+ * - If the xpath identifies a container, the provider is supposed to return all leaves and leaf-lists values within it.
+ * Nested lists and containers should not be provided - sysrepo will ask for them in subsequent calls.
+ * - If the xpath identifies a list, the provider is supposed to return all leaves (except for keys!) and
+ * leaf-lists values within all instances of the list. Nested lists and containers should not be provided - sysrepo
+ * will ask for them in subsequent calls.
+ * - If the xpath identifies a leaf-list, the provider is supposed to return all leaf-list values.
+ * - If the xpath identifies a leaf, the provider is supposed to return just the leaf in question.
+ *
+ * The xpath argument passed to callback can be only the xpath that was used for the subscription, or xpath of
+ * any nested lists or containers.
+ *
+ * @param[in] xpath @ref xp_page "Data Path" identifying the level under which the nodes are requested.
+ * @param[out] values Array of values at the selected level (allocated by the provider).
+ * @param[out] values_cnt Number of values returned.
+ * @param[in] request_id An ID identifying the originating request.
+ * @param[in] private_ctx Private context opaque to sysrepo, as passed to ::sr_dp_get_items_subscribe call.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+typedef int (*sr_dp_get_items_cb)(const char *xpath, sr_val_t **values, size_t *values_cnt, uint64_t request_id, void *private_ctx);
+
+/**
+ * @brief Registers for providing of operational data under given xpath.
+ *
+ * @note The XPath must be generic - must not include any list key values.
+ * @note This API works only for operational data (subtrees marked in YANG as "config false").
+ * Subscribing as a data provider for configuration data does not have any effect.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] xpath @ref xp_page "Data Path" identifying the subtree under which the provider is able to provide
+ * operational data.
+ * @param[in] callback Callback to be called when the operational data nder given xpat is needed.
+ * @param[in] private_ctx Private context passed to the callback function, opaque to sysrepo.
+ * @param[in] opts Options overriding default behavior of the subscription, it is supposed to be
+ * a bitwise OR-ed value of any ::sr_subscr_flag_t flags.
+ * @param[in,out] subscription Subscription context that is supposed to be released by ::sr_unsubscribe.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_dp_get_items_subscribe(sr_session_ctx_t *session, const char *xpath, sr_dp_get_items_cb callback, void *private_ctx,
+ sr_subscr_options_t opts, sr_subscription_ctx_t **subscription);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Application-local File Descriptor Watcher API
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Event that has occurred on a monitored file descriptor.
+ */
+typedef enum sr_fd_event_e {
+ SR_FD_INPUT_READY = 1, /**< File descriptor is now readable without blocking. */
+ SR_FD_OUTPUT_READY = 2, /**< File descriptor is now writable without blocking. */
+} sr_fd_event_t;
+
+/**
+ * @brief Action that needs to be taken on a file descriptor.
+ */
+typedef enum sr_fd_action_s {
+ SR_FD_START_WATCHING, /**< Start watching for the specified event on the file descriptor. */
+ SR_FD_STOP_WATCHING, /**< Stop watching for the specified event on the file descriptor. */
+} sr_fd_action_t;
+
+/**
+ * @brief Structure representing a change in the set of file descriptors monitored by the application.
+ */
+typedef struct sr_fd_change_s {
+ int fd; /**< File descriptor whose monitored state should be changed. */
+ int events; /**< Monitoring events tied to the change (or-ed value of ::sr_fd_event_t). */
+ sr_fd_action_t action; /**< Action that is supposed to be performed by application-local file descriptor watcher. */
+} sr_fd_change_t;
+
+/**
+ * @brief Callback when the subscription manager is terminated
+ */
+typedef void (*sr_fd_sm_terminated_cb)();
+
+/**
+ * @brief Initializes application-local file descriptor watcher.
+ *
+ * This can be used in those applications that subscribe for changes or providing data in sysrepo, which have their
+ * own event loop that is capable of monitoring of the events on provided file descriptors. In case that the
+ * application-local file descriptor watcher is initialized, sysrepo client library won't use a separate thread
+ * for the delivery of the notifications and for calling the callbacks - they will be called from the main thread of the
+ * application's event loop (inside of ::sr_fd_event_process calls).
+ *
+ * @note Calling this function has global consequences on the behavior of the sysrepo client library within the process
+ * that called it. It is supposed to be called as the first sysrepo API call within the application.
+ *
+ * @param[out] fd Initial file descriptor that is supposed to be monitored for readable events by the application.
+ * Once there is an event detected on this file descriptor, the application is supposed to call ::sr_fd_event_process.
+ *
+ * @param[in] sm_terminate_cb Function to be called when the subscription manager is terminated. If this callback is provided,
+ * it shall block until all pending events on any file descriptor associated with sysrepo have been handled. I.e., ensure that
+ * the event loop has called sr_fd_event_process() for all pending events before returning from this callback. If this callback
+ * doesn't block, errors will be shown in the log.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_fd_watcher_init(int *fd, sr_fd_sm_terminated_cb sm_terminate_cb);
+
+/**
+ * @brief Cleans-up the application-local file descriptor watcher previously initiated by ::sr_fd_watcher_init.
+ * It is supposed to be called as the last sysrepo API within the application.
+ */
+void sr_fd_watcher_cleanup();
+
+/**
+ * @brief Processes an event that has occurred on one of the file descriptors that the application is monitoring for
+ * sysrepo client library purposes. As a result of this event, another file descriptors may need to be started or
+ * stopped monitoring by the application. These are returned as \p fd_change_set array.
+ *
+ * @param[in] fd File descriptor where an event occurred.
+ * @param[in] event Type of the event that occurred on the given file descriptor.
+ * @param[out] fd_change_set Array of file descriptors that need to be started or stopped monitoring for specified event
+ * by the application. The application is supposed to free this array after it processes it.
+ * @param[out] fd_change_set_cnt Count of the items in the \p fd_change_set array.
+ *
+ * @return Error code (SR_ERR_OK on success).
+ */
+int sr_fd_event_process(int fd, sr_fd_event_t event, sr_fd_change_t **fd_change_set, size_t *fd_change_set_cnt);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Cleanup Routines
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Frees ::sr_val_t structure and all memory allocated within it.
+ *
+ * @param[in] value Value to be freed.
+ */
+void sr_free_val(sr_val_t *value);
+
+/**
+ * @brief Frees array of ::sr_val_t structures (and all memory allocated
+ * within of each array element).
+ *
+ * @param[in] values Array of values to be freed.
+ * @param[in] count Number of elements stored in the array.
+ */
+void sr_free_values(sr_val_t *values, size_t count);
+
+/**
+ * @brief Frees ::sr_val_iter_t iterator and all memory allocated within it.
+ *
+ * @param[in] iter Iterator to be freed.
+ */
+void sr_free_val_iter(sr_val_iter_t *iter);
+
+/**
+ * @brief Frees ::sr_change_iter_t iterator and all memory allocated within it.
+ *
+ * @param[in] iter Iterator to be freed.
+ */
+void sr_free_change_iter(sr_change_iter_t *iter);
+
+/**
+ * @brief Frees array of ::sr_schema_t structures (and all memory allocated
+ * within of each array element).
+ *
+ * @param [in] schemas Array of schemas to be freed.
+ * @param [in] count Number of elements stored in the array.
+ */
+void sr_free_schemas(sr_schema_t *schemas, size_t count);
+
+/**
+ * @brief Frees sysrepo tree data.
+ *
+ * @param[in] tree Tree data to be freed.
+ */
+void sr_free_tree(sr_node_t *tree);
+
+/**
+ * @brief Frees array of sysrepo trees. For each tree, the ::sr_free_tree is called too.
+ *
+ * @param[in] trees
+ * @param[in] count length of array
+ */
+void sr_free_trees(sr_node_t *trees, size_t count);
+
+/**@} cl */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SYSREPO_H_ */
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Connection.hpp b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Connection.hpp
new file mode 100644
index 000000000..eb944f72c
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Connection.hpp
@@ -0,0 +1,63 @@
+/**
+ * @file Connection.h
+ * @author Mislav Novakovic <mislav.novakovic@sartura.hr>
+ * @brief Sysrepo Connection class header.
+ *
+ * @copyright
+ * Copyright 2016 Deutsche Telekom AG.
+ * Modifications Copyright (C) 2019 Nokia. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef CONNECTION_H
+#define CONNECTION_H
+
+#include <iostream>
+
+#include "Sysrepo.hpp"
+#include "Internal.hpp"
+
+extern "C" {
+#include "../sysrepo.h"
+}
+
+namespace sysrepo {
+
+/**
+ * @defgroup classes C++/Python
+ * @{
+ */
+
+/**
+ * @brief Class for wrapping sr_conn_ctx_t.
+ * @class Connection
+ */
+class Connection
+{
+public:
+ /** Wrapper for [sr_connect](@ref sr_connect) */
+ Connection(const char *app_name, const sr_conn_options_t opts = CONN_DEFAULT);
+ ~Connection();
+
+ sr_conn_ctx_t *_conn;
+ friend class Session;
+
+private:
+ sr_conn_options_t _opts;
+};
+
+/**@} */
+}
+
+#endif
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Internal.hpp b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Internal.hpp
new file mode 100644
index 000000000..aec62f9f1
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Internal.hpp
@@ -0,0 +1,80 @@
+/**
+ * @file Internal.h
+ * @author Mislav Novakovic <mislav.novakovic@sartura.hr>
+ * @brief Sysrepo class header for internal C++ classes.
+ *
+ * @copyright
+ * Copyright 2016 Deutsche Telekom AG.
+ *
+ * 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.
+ */
+
+#ifndef INTERNAL_H
+#define INTERNAL_H
+
+#include <iostream>
+#include <memory>
+
+extern "C" {
+#include "../sysrepo.h"
+#include "../sysrepo/trees.h"
+}
+
+namespace sysrepo {
+
+enum class Free_Type {
+ VAL,
+ VALS,
+ VALS_POINTER,
+ TREE,
+ TREES,
+ TREES_POINTER,
+ SCHEMAS,
+ SESSION,
+};
+
+typedef union value_e {
+ sr_val_t *_val;
+ sr_val_t **p_vals;
+ sr_node_t *_tree;
+ sr_node_t **p_trees;
+ sr_schema_t *_sch;
+ sr_session_ctx_t *_sess;
+} value_t;
+
+typedef union count_e {
+ size_t _cnt;
+ size_t *p_cnt;
+} count_t;
+
+class Deleter
+{
+public:
+ Deleter(sr_val_t *val);
+ Deleter(sr_val_t *vals, size_t cnt);
+ Deleter(sr_val_t **vals, size_t *cnt);
+ Deleter(sr_node_t *tree);
+ Deleter(sr_node_t *trees, size_t cnt);
+ Deleter(sr_node_t **trees, size_t *cnt);
+ Deleter(sr_schema_t *sch, size_t cnt);
+ Deleter(sr_session_ctx_t *sess);
+ ~Deleter();
+
+private:
+ count_t c;
+ value_t v;
+ Free_Type _t;
+};
+
+}
+#endif
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Session.hpp b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Session.hpp
new file mode 100644
index 000000000..02d03ed38
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Session.hpp
@@ -0,0 +1,245 @@
+/**
+ * @file Session.h
+ * @author Mislav Novakovic <mislav.novakovic@sartura.hr>
+ * @brief Sysrepo Session class header.
+ *
+ * @copyright
+ * Copyright 2016 Deutsche Telekom AG.
+ *
+ * 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.
+ */
+
+#ifndef SESSION_H
+#define SESSION_H
+
+#include <iostream>
+#include <memory>
+#include <map>
+#include <vector>
+
+#include "Sysrepo.hpp"
+#include "Internal.hpp"
+#include "Struct.hpp"
+#include "Tree.hpp"
+#include "Connection.hpp"
+#include "Session.hpp"
+
+extern "C" {
+#include "../sysrepo.h"
+}
+
+namespace sysrepo {
+
+/**
+ * @defgroup classes C++/Python
+ * @{
+ */
+
+/**
+ * @brief Class for wrapping sr_session_ctx_t.
+ * @class Session
+ */
+class Session
+{
+
+public:
+ /** Wrapper for [sr_session_start](@ref sr_session_start) and [sr_session_start_user](@ref sr_session_start_user)
+ * if user_name is set.*/
+ Session(S_Connection conn, sr_datastore_t datastore = (sr_datastore_t) DS_RUNNING, \
+ const sr_sess_options_t opts = SESS_DEFAULT, const char *user_name = nullptr);
+ /** Wrapper for [sr_session_ctx_t](@ref sr_session_ctx_t), for internal use only.*/
+ Session(sr_session_ctx_t *sess, sr_sess_options_t opts = SESS_DEFAULT, S_Deleter deleter = nullptr);
+ /** Wrapper for [sr_session_stop](@ref sr_session_stop) */
+ void session_stop();
+ /** Wrapper for [sr_session_switch_ds](@ref sr_session_switch_ds) */
+ void session_switch_ds(sr_datastore_t ds);
+ /** Wrapper for [sr_get_last_error](@ref sr_get_last_error) */
+ S_Error get_last_error();
+ /** Wrapper for [sr_get_last_errors](@ref sr_get_last_errors) */
+ S_Errors get_last_errors();
+ /** Wrapper for [sr_list_schemas](@ref sr_list_schemas) */
+ S_Yang_Schemas list_schemas();
+ /** Wrapper for [sr_get_schema](@ref sr_get_schema) */
+ std::string get_schema(const char *module_name, const char *revision,
+ const char *submodule_name, sr_schema_format_t format);
+ /** Wrapper for [sr_get_item](@ref sr_get_item) */
+ S_Val get_item(const char *xpath);
+ /** Wrapper for [sr_get_items](@ref sr_get_items) */
+ S_Vals get_items(const char *xpath);
+ /** Wrapper for [sr_get_items_iter](@ref sr_get_items_iter) */
+ S_Iter_Value get_items_iter(const char *xpath);
+ /** Wrapper for [sr_get_item_next](@ref sr_get_item_next) */
+ S_Val get_item_next(S_Iter_Value iter);
+ /** Wrapper for [sr_get_subtree](@ref sr_get_subtree) */
+ S_Tree get_subtree(const char *xpath, sr_get_subtree_options_t opts = GET_SUBTREE_DEFAULT);
+ /** Wrapper for [sr_get_subtrees](@ref sr_get_subtrees) */
+ S_Trees get_subtrees(const char *xpath, sr_get_subtree_options_t opts = GET_SUBTREE_DEFAULT);
+
+ /** Wrapper for [sr_node_get_child](@ref sr_node_get_child) */
+ S_Tree get_child(S_Tree in_tree);
+ /** Wrapper for [sr_node_get_next_sibling](@ref sr_node_get_next_sibling) */
+ S_Tree get_next_sibling(S_Tree in_tree);
+ /** Wrapper for [sr_node_get_parent](@ref sr_node_get_parent) */
+ S_Tree get_parent(S_Tree in_tree);
+
+ /** Wrapper for [sr_set_item](@ref sr_set_item) */
+ void set_item(const char *xpath, S_Val value = nullptr, const sr_edit_options_t opts = EDIT_DEFAULT);
+ /** Wrapper for [sr_set_item_str](@ref sr_set_item_str) */
+ void set_item_str(const char *xpath, const char *value, const sr_edit_options_t opts = EDIT_DEFAULT);
+ /** Wrapper for [sr_delete_item](@ref sr_delete_item) */
+ void delete_item(const char *xpath, const sr_edit_options_t opts = EDIT_DEFAULT);
+ /** Wrapper for [sr_move_item](@ref sr_move_item) */
+ void move_item(const char *xpath, const sr_move_position_t position, const char *relative_item = nullptr);
+ /** Wrapper for [sr_session_refresh](@ref sr_session_refresh) */
+ void refresh();
+ /** Wrapper for [sr_validate](@ref sr_validate) */
+ void validate();
+ /** Wrapper for [sr_commit](@ref sr_commit) */
+ void commit();
+ /** Wrapper for [sr_lock_datastore](@ref sr_lock_datastore) */
+ void lock_datastore();
+ /** Wrapper for [sr_unlock_datastore](@ref sr_unlock_datastore) */
+ void unlock_datastore();
+ /** Wrapper for [sr_lock_module](@ref sr_lock_module) */
+ void lock_module(const char *module_name);
+ /** Wrapper for [sr_unlock_module](@ref sr_unlock_module) */
+ void unlock_module(const char *module_name);
+ /** Wrapper for [sr_discard_changes](@ref sr_discard_changes) */
+ void discard_changes();
+ /** Wrapper for [sr_copy_config](@ref sr_copy_config) */
+ void copy_config(const char *module_name, sr_datastore_t src_datastore, sr_datastore_t dst_datastore);
+ /** Wrapper for [sr_session_set_options](@ref sr_session_set_options) */
+ void set_options(const sr_sess_options_t opts);
+ /** Wrapper for [sr_get_changes_iter](@ref sr_get_changes_iter) */
+ S_Iter_Change get_changes_iter(const char *xpath);
+ /** Wrapper for [sr_get_change_next](@ref sr_get_change_next) */
+ S_Change get_change_next(S_Iter_Change iter);
+ ~Session();
+
+ /** Wrapper for [sr_rpc_send](@ref sr_rpc_send) */
+ S_Vals rpc_send(const char *xpath, S_Vals input);
+ /** Wrapper for [sr_rpc_send_tree](@ref sr_rpc_send_tree) */
+ S_Trees rpc_send(const char *xpath, S_Trees input);
+ /** Wrapper for [sr_action_send](@ref sr_action_send) */
+ S_Vals action_send(const char *xpath, S_Vals input);
+ /** Wrapper for [sr_action_send_tree](@ref sr_action_send_tree) */
+ S_Trees action_send(const char *xpath, S_Trees input);
+ /** Wrapper for [sr_event_notif_send](@ref sr_event_notif_send) */
+ void event_notif_send(const char *xpath, S_Vals values, const sr_ev_notif_flag_t options = SR_EV_NOTIF_DEFAULT);
+ /** Wrapper for [sr_event_notif_send_tree](@ref sr_event_notif_send_tree) */
+ void event_notif_send(const char *xpath, S_Trees trees, const sr_ev_notif_flag_t options = SR_EV_NOTIF_DEFAULT);
+
+ friend class Subscribe;
+
+private:
+ sr_session_ctx_t *_sess;
+ sr_datastore_t _datastore;
+ sr_sess_options_t _opts;
+ S_Connection _conn;
+ S_Deleter _deleter;
+};
+
+/**
+ * @brief Helper class for calling C callbacks, C++ only.
+ * @class Callback
+ */
+class Callback
+{
+public:
+ Callback();
+ virtual ~Callback();
+
+ /** Wrapper for [sr_module_change_cb](@ref sr_module_change_cb) callback.*/
+ virtual int module_change(S_Session session, const char *module_name, sr_notif_event_t event, void *private_ctx) {return SR_ERR_OK;};
+ /** Wrapper for [sr_subtree_change_cb](@ref sr_subtree_change_cb) callback.*/
+ virtual int subtree_change(S_Session session, const char *xpath, sr_notif_event_t event, void *private_ctx) {return SR_ERR_OK;};
+ /** Wrapper for [sr_module_install_cb](@ref sr_module_install_cb) callback.*/
+ virtual void module_install(const char *module_name, const char *revision, sr_module_state_t state, void *private_ctx) {return;};
+ /** Wrapper for [sr_feature_enable_cb](@ref sr_feature_enable_cb) callback.*/
+ virtual void feature_enable(const char *module_name, const char *feature_name, bool enabled, void *private_ctx) {return;};
+ /** Wrapper for [sr_rpc_cb](@ref sr_rpc_cb) callback.*/
+ virtual int rpc(const char *xpath, const S_Vals input, S_Vals_Holder output, void *private_ctx) {return SR_ERR_OK;};
+ /** Wrapper for [sr_action_cb](@ref sr_action_cb) callback.*/
+ virtual int action(const char *xpath, const S_Vals input, S_Vals_Holder output, void *private_ctx) {return SR_ERR_OK;};
+ /** Wrapper for [sr_rpc_tree_cb](@ref sr_rpc_tree_cb) callback.*/
+ virtual int rpc_tree(const char *xpath, const S_Trees input, S_Trees_Holder output, void *private_ctx) {return SR_ERR_OK;};
+ /** Wrapper for [sr_action_tree_cb](@ref sr_action_tree_cb) callback.*/
+ virtual int action_tree(const char *xpath, const S_Trees input, S_Trees_Holder output, void *private_ctx) {return SR_ERR_OK;};
+ /** Wrapper for [sr_dp_get_items_cb](@ref sr_dp_get_items_cb) callback.*/
+ virtual int dp_get_items(const char *xpath, S_Vals_Holder vals, uint64_t request_id, void *private_ctx) {return SR_ERR_OK;};
+ /** Wrapper for [sr_event_notif_cb](@ref sr_event_notif_cb) callback.*/
+ virtual void event_notif(const sr_ev_notif_type_t notif_type, const char *xpath, S_Vals vals, time_t timestamp, void *private_ctx) {return;};
+ /** Wrapper for [sr_event_notif_tree_cb](@ref sr_event_notif_tree_cb) callback.*/
+ virtual void event_notif_tree(const sr_ev_notif_type_t notif_type, const char *xpath, S_Trees trees, time_t timestamp, void *private_ctx) {return;};
+ Callback *get() {return this;};
+
+ std::map<const char *, void*> private_ctx;
+};
+
+/**
+ * @brief Class for wrapping sr_subscription_ctx_t.
+ * @class Subscribe
+ */
+class Subscribe
+{
+
+public:
+ /** Wrapper for [sr_subscription_ctx_t](@ref sr_subscription_ctx_t), for internal use only.*/
+ Subscribe(S_Session sess);
+
+ /** Wrapper for [sr_module_change_subscribe](@ref sr_module_change_subscribe) */
+ void module_change_subscribe(const char *module_name, S_Callback callback, void *private_ctx = nullptr, uint32_t priority = 0, sr_subscr_options_t opts = SUBSCR_DEFAULT);
+ /** Wrapper for [sr_subtree_change_subscribe](@ref sr_subtree_change_subscribe) */
+ void subtree_change_subscribe(const char *xpath, S_Callback callback, void *private_ctx = nullptr, uint32_t priority = 0, sr_subscr_options_t opts = SUBSCR_DEFAULT);
+ /** Wrapper for [sr_module_install_subscribe](@ref sr_module_install_subscribe) */
+ void module_install_subscribe(S_Callback callback, void *private_ctx = nullptr, sr_subscr_options_t opts = SUBSCR_DEFAULT);
+ /** Wrapper for [sr_feature_enable_subscribe](@ref sr_feature_enable_subscribe) */
+ void feature_enable_subscribe(S_Callback callback, void *private_ctx = nullptr, sr_subscr_options_t opts = SUBSCR_DEFAULT);
+ /** Wrapper for [sr_rpc_subscribe](@ref sr_rpc_subscribe) */
+ void rpc_subscribe(const char *xpath, S_Callback callback, void *private_ctx = nullptr, sr_subscr_options_t opts = SUBSCR_DEFAULT);
+ /** Wrapper for [sr_action_subscribe](@ref sr_action_subscribe) */
+ void action_subscribe(const char *xpath, S_Callback callback, void *private_ctx = nullptr, sr_subscr_options_t opts = SUBSCR_DEFAULT);
+ /** Wrapper for [sr_event_notif_subscribe_tree](@ref sr_event_notif_subscribe_tree) */
+ void event_notif_subscribe_tree(const char *xpath, S_Callback callback, void *private_ctx = nullptr, sr_subscr_options_t opts = SUBSCR_DEFAULT);
+ /** Wrapper for [sr_event_notif_subscribe](@ref sr_event_notif_subscribe) */
+ void event_notif_subscribe(const char *xpath, S_Callback callback, void *private_ctx = nullptr, sr_subscr_options_t opts = SUBSCR_DEFAULT);
+ /** Wrapper for [sr_rpc_subscribe_tree](@ref sr_rpc_subscribe_tree) */
+ void rpc_subscribe_tree(const char *xpath, S_Callback callback, void *private_ctx = nullptr, sr_subscr_options_t opts = SUBSCR_DEFAULT);
+ /** Wrapper for [sr_action_subscribe_tree](@ref sr_action_subscribe_tree) */
+ void action_subscribe_tree(const char *xpath, S_Callback callback, void *private_ctx = nullptr, sr_subscr_options_t opts = SUBSCR_DEFAULT);
+ /** Wrapper for [sr_dp_get_items_subscribe](@ref sr_dp_get_items_subscribe) */
+ void dp_get_items_subscribe(const char *xpath, S_Callback callback, void *private_ctx = nullptr, sr_subscr_options_t opts = SUBSCR_DEFAULT);
+ std::vector<S_Callback > cb_list;
+
+ /** Wrapper for [sr_unsubscribe](@ref sr_unsubscribe) */
+ void unsubscribe();
+ ~Subscribe();
+
+ /** SWIG specific, internal use only.*/
+ sr_subscription_ctx_t **swig_sub() { return &_sub;};
+ /** SWIG specific, internal use only.*/
+ sr_session_ctx_t *swig_sess() {return _sess->_sess;};
+ /** SWIG specific, internal use only.*/
+ std::vector<void*> wrap_cb_l;
+ /** SWIG specific, internal use only.*/
+ void additional_cleanup(void *private_ctx) {return;};
+
+private:
+ sr_subscription_ctx_t *_sub;
+ S_Session _sess;
+ S_Deleter sess_deleter;
+};
+
+/**@} */
+}
+#endif
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Struct.hpp b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Struct.hpp
new file mode 100644
index 000000000..7f48d562d
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Struct.hpp
@@ -0,0 +1,514 @@
+/**
+ * @file Struct.h
+ * @author Mislav Novakovic <mislav.novakovic@sartura.hr>
+ * @brief Sysrepo class header for C struts.
+ *
+ * @copyright
+ * Copyright 2016 Deutsche Telekom AG.
+ *
+ * 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.
+ */
+
+#ifndef STRUCT_H
+#define STRUCT_H
+
+#include <iostream>
+#include <memory>
+
+#include "Sysrepo.hpp"
+#include "Internal.hpp"
+
+extern "C" {
+#include "../sysrepo.h"
+}
+
+namespace sysrepo {
+
+/**
+ * @defgroup classes C++/Python
+ * @{
+ */
+
+/**
+ * @brief Class for wrapping sr_data_t.
+ * @class Data
+ */
+class Data
+{
+public:
+ /** Wrapper for [sr_data_t](@ref sr_data_t), for internal use only.*/
+ Data(sr_data_t data, sr_type_t type, S_Deleter deleter);
+ ~Data();
+ /** Getter for binary data.*/
+ char *get_binary() const;
+ /** Getter for bits.*/
+ char *get_bits() const;
+ /** Getter for bool.*/
+ bool get_bool() const;
+ /** Getter for decimal64.*/
+ double get_decimal64() const;
+ /** Getter for enum.*/
+ char *get_enum() const;
+ /** Getter for identityref.*/
+ char *get_identityref() const;
+ /** Getter for instanceid.*/
+ char *get_instanceid() const;
+ /** Getter for int8.*/
+ int8_t get_int8() const;
+ /** Getter for int16.*/
+ int16_t get_int16() const;
+ /** Getter for int32.*/
+ int32_t get_int32() const;
+ /** Getter for int64.*/
+ int64_t get_int64() const;
+ /** Getter for string.*/
+ char *get_string() const;
+ /** Getter for uint8.*/
+ uint8_t get_uint8() const;
+ /** Getter for uint16.*/
+ uint16_t get_uint16() const;
+ /** Getter for uint32.*/
+ uint32_t get_uint32() const;
+ /** Getter for uint64.*/
+ uint64_t get_uint64() const;
+
+private:
+ sr_data_t _d;
+ sr_type_t _t;
+ S_Deleter _deleter;
+};
+
+/**
+ * @brief Class for wrapping sr_val_t.
+ * @class Val
+ */
+class Val
+{
+public:
+ /** Constructor for an empty value.*/
+ Val();
+ /** Wrapper for [sr_val_t](@ref sr_val_t).*/
+ Val(sr_val_t *val, S_Deleter deleter);
+ /** Constructor for string value, type can be SR_STRING_T, SR_BINARY_T, SR_BITS_T, SR_ENUM_T,
+ * SR_IDENTITYREF_T and SR_INSTANCEID_T.*/
+ Val(const char *val, sr_type_t type = SR_STRING_T);
+ /** Constructor for bool value.*/
+ Val(bool bool_val, sr_type_t type = SR_BOOL_T);
+ /** Constructor for decimal64 value.*/
+ Val(double decimal64_val);
+ /** Constructor for int8 value, C++ only.*/
+ Val(int8_t int8_val, sr_type_t type);
+ /** Constructor for int16 value, C++ only.*/
+ Val(int16_t int16_val, sr_type_t type);
+ /** Constructor for int32 value, C++ only.*/
+ Val(int32_t int32_val, sr_type_t type);
+ /** Constructor for int64 value, type can be SR_INT8_T, SR_INT16_T, SR_INT32_T,
+ * SR_INT64_T, SR_UINT8_T, SR_UINT16_T and SR_UINT32_T,*/
+ Val(int64_t int64_val, sr_type_t type);
+ /** Constructor for uint8 value, C++ only.*/
+ Val(uint8_t uint8_val, sr_type_t type);
+ /** Constructor for uint16 value, C++ only.*/
+ Val(uint16_t uint16_val, sr_type_t type);
+ /** Constructor for uint32 value, C++ only.*/
+ Val(uint32_t uint32_val, sr_type_t type);
+ /** Constructor for uint64 value, C++ only.*/
+ Val(uint64_t uint64_val, sr_type_t type);
+ ~Val();
+ /** Setter for string value, type can be SR_STRING_T, SR_BINARY_T, SR_BITS_T, SR_ENUM_T,
+ * SR_IDENTITYREF_T and SR_INSTANCEID_T.*/
+ void set(const char *xpath, const char *val, sr_type_t type = SR_STRING_T);
+ /** Setter for bool value.*/
+ void set(const char *xpath, bool bool_val, sr_type_t type = SR_BOOL_T);
+ /** Setter for decimal64 value.*/
+ void set(const char *xpath, double decimal64_val);
+ /** Setter for int8 value, C++ only.*/
+ void set(const char *xpath, int8_t int8_val, sr_type_t type);
+ /** Setter for int16 value, C++ only.*/
+ void set(const char *xpath, int16_t int16_val, sr_type_t type);
+ /** Setter for int32 value, C++ only.*/
+ void set(const char *xpath, int32_t int32_val, sr_type_t type);
+ /** Setter for int64 value, type can be SR_INT8_T, SR_INT16_T, SR_INT32_T,
+ * SR_INT64_T, SR_UINT8_T, SR_UINT16_T and SR_UINT32_T,*/
+ void set(const char *xpath, int64_t int64_val, sr_type_t type);
+ /** Setter for uint8 value, C++ only.*/
+ void set(const char *xpath, uint8_t uint8_val, sr_type_t type);
+ /** Setter for uint16 value, C++ only.*/
+ void set(const char *xpath, uint16_t uint16_val, sr_type_t type);
+ /** Setter for uint32 value, C++ only.*/
+ void set(const char *xpath, uint32_t uint32_val, sr_type_t type);
+ /** Setter for uint64 value, C++ only.*/
+ void set(const char *xpath, uint64_t uint64_val, sr_type_t type);
+ /** Getter for xpath.*/
+ char *xpath() {return _val->xpath;};
+ /** Setter for xpath.*/
+ void xpath_set(char *xpath);
+ /** Getter for type.*/
+ sr_type_t type() {return _val->type;};
+ /** Getter for dflt.*/
+ bool dflt() {return _val->dflt;};
+ /** Setter for dflt.*/
+ void dflt_set(bool data) {_val->dflt = data;};
+ /** Getter for data.*/
+ S_Data data() {S_Data data(new Data(_val->data, _val->type, _deleter)); return data;};
+ /** Wrapper for [sr_print_val_mem](@ref sr_print_val_mem) */
+ std::string to_string();
+ /** Wrapper for [sr_val_to_string](@ref sr_val_to_string) */
+ std::string val_to_string();
+ /** Wrapper for [sr_dup_val](@ref sr_dup_val) */
+ S_Val dup();
+
+ friend class Session;
+ friend class Subscribe;
+
+private:
+ sr_val_t *_val;
+ S_Deleter _deleter;
+};
+
+/**
+ * @brief Class for wrapping sr_val_t array.
+ * @class Vals
+ */
+class Vals
+{
+public:
+ /** Wrapper for [sr_val_t](@ref sr_val_t) array, internal use only.*/
+ Vals(const sr_val_t *vals, const size_t cnt, S_Deleter deleter = nullptr);
+ /** Wrapper for [sr_val_t](@ref sr_val_t) array, internal use only.*/
+ Vals(sr_val_t **vals, size_t *cnt, S_Deleter deleter = nullptr);
+ /** Wrapper for [sr_val_t](@ref sr_val_t) array, create n-array.*/
+ Vals(size_t cnt);
+ /** Constructor for an empty [sr_val_t](@ref sr_val_t) array.*/
+ Vals();
+ ~Vals();
+ /** Getter for [sr_val_t](@ref sr_val_t), get the n-th element in array.*/
+ S_Val val(size_t n);
+ /** Getter for array size */
+ size_t val_cnt() {return _cnt;};
+ /** Wrapper for [sr_dup_values](@ref sr_dup_values) */
+ S_Vals dup();
+
+ friend class Session;
+ friend class Subscribe;
+
+private:
+ size_t _cnt;
+ sr_val_t *_vals;
+ S_Deleter _deleter;
+};
+
+/**
+ * @brief Class for wrapping sr_val_t in callbacks.
+ * @class Vals_holder
+ */
+class Vals_Holder
+{
+public:
+ /** Wrapper for [sr_val_t](@ref sr_val_t) array, used only in callbacks.*/
+ Vals_Holder(sr_val_t **vals, size_t *cnt);
+ /** Create [sr_val_t](@ref sr_val_t) array of n size.*/
+ S_Vals allocate(size_t n);
+ ~Vals_Holder();
+
+private:
+ size_t *p_cnt;
+ sr_val_t **p_vals;
+ bool _allocate;
+};
+
+/**
+ * @brief Class for wrapping sr_val_iter_t.
+ * @class Val_Iter
+ */
+class Val_Iter
+{
+public:
+ /** Wrapper for [sr_val_iter_t](@ref sr_val_iter_t).*/
+ Val_Iter(sr_val_iter_t *iter = nullptr);
+ ~Val_Iter();
+ /** Getter for [sr_val_iter_t](@ref sr_val_iter_t).*/
+ sr_val_iter_t *iter() {return _iter;};
+
+private:
+ sr_val_iter_t *_iter;
+};
+
+/**
+ * @brief Class for wrapping sr_change_iter_t.
+ * @class Change_Iter
+ */
+class Change_Iter
+{
+public:
+ /** Wrapper for [sr_change_iter_t](@ref sr_change_iter_t).*/
+ Change_Iter(sr_change_iter_t *iter = nullptr);
+ ~Change_Iter();
+ /** Getter for [sr_change_iter_t](@ref sr_change_iter_t).*/
+ sr_change_iter_t *iter() {return _iter;};
+
+private:
+ sr_change_iter_t *_iter;
+};
+
+/**
+ * @brief Class for wrapping sr_error_info_t.
+ * @class Error
+ */
+class Error
+{
+public:
+ /** Constructor for an empty [sr_error_info_t](@ref sr_error_info_t).*/
+ Error();
+ /** Wrapper for [sr_error_info_t](@ref sr_error_info_t).*/
+ Error(const sr_error_info_t *info);
+ ~Error();
+ /** Getter for message.*/
+ const char *message() const {if (_info) return _info->message; else return nullptr;};
+ /** Getter for xpath.*/
+ const char *xpath() const {if (_info) return _info->xpath; else return nullptr;};
+
+ friend class Session;
+
+private:
+ const sr_error_info_t *_info;
+};
+
+/**
+ * @brief Class for wrapping sr_error_info_t array.
+ * @class Errors
+ */
+class Errors
+{
+public:
+ /** Constructor for an empty [sr_error_info_t](@ref sr_error_info_t) array.*/
+ Errors();
+ ~Errors();
+ /** Getter for [sr_error_info_t](@ref sr_error_info_t), get the n-th element in array.*/
+ S_Error error(size_t n);
+ /** Getter for array size.*/
+ size_t error_cnt() {return _cnt;};
+
+ friend class Session;
+
+private:
+ size_t _cnt;
+ const sr_error_info_t *_info;
+};
+
+/**
+ * @brief Class for wrapping sr_sch_revision_t array.
+ * @class Schema_Revision
+ */
+class Schema_Revision
+{
+public:
+ /** Wrapper for [sr_sch_revision_t](@ref sr_sch_revision_t).*/
+ Schema_Revision(sr_sch_revision_t rev);
+ ~Schema_Revision();
+ /** Getter for revision.*/
+ const char *revision() const {return _rev.revision;};
+ /** Getter for file_path_yang.*/
+ const char *file_path_yang() const {return _rev.file_path_yang;};
+ /** Getter for file_path_yin.*/
+ const char *file_path_yin() const {return _rev.file_path_yin;};
+
+private:
+ sr_sch_revision_t _rev;
+};
+
+/**
+ * @brief Class for wrapping sr_sch_submodule_t.
+ * @class Schema_Submodule
+ */
+class Schema_Submodule
+{
+public:
+ /** Wrapper for [sr_sch_submodule_t](@ref sr_sch_submodule_t).*/
+ Schema_Submodule(sr_sch_submodule_t sub, S_Deleter deleter);
+ ~Schema_Submodule();
+ /** Getter for submodule_name.*/
+ const char *submodule_name() const {return _sub.submodule_name;};
+ /** Getter for revision.*/
+ S_Schema_Revision revision();
+
+private:
+ sr_sch_submodule_t _sub;
+ S_Deleter _deleter;
+};
+
+/**
+ * @brief Class for wrapping sr_schema_t.
+ * @class Yang_Schema
+ */
+class Yang_Schema
+{
+public:
+ /** Wrapper for [sr_schema_t](@ref sr_schema_t).*/
+ Yang_Schema(sr_schema_t *sch, S_Deleter deleter);
+ ~Yang_Schema();
+ /** Getter for module_name.*/
+ const char *module_name() const {return _sch->module_name;};
+ /** Getter for ns.*/
+ const char *ns() const {return _sch->ns;};
+ /** Getter for prefix.*/
+ const char *prefix() const {return _sch->prefix;};
+ /** Getter for implemented.*/
+ bool implemented() const {return _sch->implemented;};
+ /** Getter for revision.*/
+ S_Schema_Revision revision();
+ /** Getter for submodule.*/
+ S_Schema_Submodule submodule(size_t n);
+ /** Getter for submodule_cnt.*/
+ size_t submodule_cnt() const {return _sch->submodule_count;};
+ /** Getter for enabled_features.*/
+ char *enabled_features(size_t n);
+ /** Getter for enabled_features_cnt.*/
+ size_t enabled_feature_cnt() const {return _sch->enabled_feature_cnt;};
+
+ friend class Session;
+
+private:
+ sr_schema_t *_sch;
+ S_Deleter _deleter;
+};
+
+/**
+ * @brief Class for wrapping sr_schema_t array.
+ * @class Yang_Schemas
+ */
+class Yang_Schemas
+{
+public:
+ /** Constructor for an empty [sr_schema_t](@ref sr_schema_t) array.*/
+ Yang_Schemas();
+ ~Yang_Schemas();
+ /** Getter for [sr_schema_t](@ref sr_schema_t) array, get the n-th element in array.*/
+ S_Yang_Schema schema(size_t n);
+ /** Getter for array size.*/
+ size_t schema_cnt() const {return _cnt;};
+
+ friend class Session;
+
+private:
+ size_t _cnt;
+ sr_schema_t *_sch;
+ S_Deleter _deleter;
+};
+
+/**
+ * @brief Class for wrapping sr_fd_change_t.
+ * @class Fd_Change
+ */
+class Fd_Change
+{
+public:
+ /** Wrapper for [sr_fd_change_t](@ref sr_fd_change_t).*/
+ Fd_Change(sr_fd_change_t *ch);
+ ~Fd_Change();
+ /** Getter for fd.*/
+ int fd() const {return _ch->fd;};
+ /** Getter for events.*/
+ int events() const {return _ch->events;};
+ /** Getter for action.*/
+ sr_fd_action_t action() const {return _ch->action;};
+
+private:
+ sr_fd_change_t *_ch;
+};
+
+/**
+ * @brief Class for wrapping sr_fd_change_t array.
+ * @class Fd_Changes
+ */
+class Fd_Changes
+{
+public:
+ /** Wrapper for [sr_fd_change_t](@ref sr_fd_change_t) array.*/
+ Fd_Changes(sr_fd_change_t *ch, size_t cnt);
+ ~Fd_Changes();
+ /** Getter for [sr_fd_change_t](@ref sr_fd_change_t) array, get the n-th element in array.*/
+ S_Fd_Change fd_change(size_t n);
+
+private:
+ sr_fd_change_t *_ch;
+ size_t _cnt;
+};
+
+/**
+ * @brief Class for wrapping sr_val_iter_t.
+ * @class Fd_Changes
+ */
+class Iter_Value
+{
+
+public:
+ /** Wrapper for [sr_val_iter_t](@ref sr_val_iter_t).*/
+ Iter_Value(sr_val_iter_t *iter = nullptr);
+ ~Iter_Value();
+ /** Setter for [sr_val_iter_t](@ref sr_val_iter_t).*/
+ void Set(sr_val_iter_t *iter);
+
+ friend class Session;
+
+private:
+ sr_val_iter_t *_iter;
+};
+
+/**
+ * @brief Class for wrapping sr_change_iter_t.
+ * @class Iter_Change
+ */
+class Iter_Change
+{
+
+public:
+ /** Wrapper for [sr_change_iter_t](@ref sr_change_iter_t).*/
+ Iter_Change(sr_change_iter_t *iter = nullptr);
+ ~Iter_Change();
+
+ friend class Session;
+
+private:
+ sr_change_iter_t *_iter;
+};
+
+/**
+ * @brief Class for wrapping sr_change_oper_t.
+ * @class Change
+ */
+class Change
+{
+public:
+ /** Constructor for an empty [sr_change_oper_t](@ref sr_change_oper_t).*/
+ Change();
+ ~Change();
+ /** Getter for sr_change_oper_t.*/
+ sr_change_oper_t oper() {return _oper;};
+ /** Getter for new sr_val_t.*/
+ S_Val new_val();
+ /** Getter for old sr_val_t.*/
+ S_Val old_val();
+
+ friend class Session;
+
+private:
+ sr_change_oper_t _oper;
+ sr_val_t *_new;
+ sr_val_t *_old;
+ S_Deleter _deleter_new;
+ S_Deleter _deleter_old;
+};
+
+/**@} */
+}
+#endif
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Sysrepo.hpp b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Sysrepo.hpp
new file mode 100644
index 000000000..d3b76483f
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Sysrepo.hpp
@@ -0,0 +1,177 @@
+/**
+ * @file Sysrepo.h
+ * @author Mislav Novakovic <mislav.novakovic@sartura.hr>
+ * @brief Sysrepo Sysrepo class header.
+ *
+ * @copyright
+ * Copyright 2016 Deutsche Telekom AG.
+ *
+ * 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.
+ */
+
+#ifndef SYSREPO_H
+#define SYSREPO_H
+
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+
+#include "Internal.hpp"
+
+extern "C" {
+#include "../sysrepo.h"
+}
+
+namespace sysrepo {
+
+/**
+ * @defgroup classes C++/Python
+ * @{
+ */
+
+class Iter_Value;
+class Iter_Change;
+class Session;
+class Subscribe;
+class Connection;
+class Operation;
+class Schema_Content;
+class Error;
+class Errors;
+class Data;
+class Schema_Revision;
+class Schema_Submodule;
+class Yang_Schema;
+class Yang_Schemas;
+class Fd_Change;
+class Fd_Changes;
+class Val;
+class Vals_Holder;
+class Vals;
+class Tree;
+class Trees;
+class Trees_Holder;
+class Xpath_Ctx;
+class Logs;
+class Change;
+class Counter;
+class Callback;
+class Deleter;
+
+#ifdef SWIGLUA
+using S_Iter_Value = Iter_Value*;
+using S_Iter_Change = Iter_Change*;
+using S_Session = Session*;
+using S_Subscribe = Subscribe*;
+using S_Connection = Connection*;
+using S_Operation = Operation*;
+using S_Schema_Content = Schema_Content*;
+using S_Error = Error*;
+using S_Errors = Errors*;
+using S_Data = Data*;
+using S_Schema_Revision = Schema_Revision*;
+using S_Schema_Submodule = Schema_Submodule*;
+using S_Yang_Schema = Yang_Schema*;
+using S_Yang_Schemas = Yang_Schemas*;
+using S_Fd_Change = Fd_Change*;
+using S_Fd_Changes = Fd_Changes*;
+using S_Val = Val*;
+using S_Vals_Holder = Vals_Holder*;
+using S_Vals = Vals*;
+using S_Tree = Tree*;
+using S_Trees = Trees*;
+using S_Trees_Holder = Trees_Holder*;
+using S_Xpath_Ctx = Xpath_Ctx*;
+using S_Logs = Logs*;
+using S_Change = Change*;
+using S_Counter = Counter*;
+using S_Callback = Callback*;
+#else
+using S_Iter_Value = std::shared_ptr<Iter_Value>;
+using S_Iter_Change = std::shared_ptr<Iter_Change>;
+using S_Session = std::shared_ptr<Session>;
+using S_Subscribe = std::shared_ptr<Subscribe>;
+using S_Connection = std::shared_ptr<Connection>;
+using S_Operation = std::shared_ptr<Operation>;
+using S_Schema_Content = std::shared_ptr<Schema_Content>;
+using S_Error = std::shared_ptr<Error>;
+using S_Errors = std::shared_ptr<Errors>;
+using S_Data = std::shared_ptr<Data>;
+using S_Schema_Revision = std::shared_ptr<Schema_Revision>;
+using S_Schema_Submodule = std::shared_ptr<Schema_Submodule>;
+using S_Yang_Schema = std::shared_ptr<Yang_Schema>;
+using S_Yang_Schemas = std::shared_ptr<Yang_Schemas>;
+using S_Fd_Change = std::shared_ptr<Fd_Change>;
+using S_Fd_Changes = std::shared_ptr<Fd_Changes>;
+using S_Val = std::shared_ptr<Val>;
+using S_Vals_Holder = std::shared_ptr<Vals_Holder>;
+using S_Vals = std::shared_ptr<Vals>;
+using S_Tree = std::shared_ptr<Tree>;
+using S_Trees = std::shared_ptr<Trees>;
+using S_Trees_Holder = std::shared_ptr<Trees_Holder>;
+using S_Xpath_Ctx = std::shared_ptr<Xpath_Ctx>;
+using S_Logs = std::shared_ptr<Logs>;
+using S_Change = std::shared_ptr<Change>;
+using S_Counter = std::shared_ptr<Counter>;
+using S_Callback = std::shared_ptr<Callback>;
+using S_Deleter = std::shared_ptr<Deleter>;
+#endif
+
+/* this is a workaround for python not recognizing
+ * enum's in function default values */
+static const int SESS_DEFAULT = SR_SESS_DEFAULT;
+static const int DS_RUNNING = SR_DS_RUNNING;
+static const int EDIT_DEFAULT = SR_EDIT_DEFAULT;
+static const int CONN_DEFAULT = SR_CONN_DEFAULT;
+static const int GET_SUBTREE_DEFAULT = SR_GET_SUBTREE_DEFAULT;
+static const int SUBSCR_DEFAULT = SR_SUBSCR_DEFAULT;
+
+#ifdef SWIG
+// https://github.com/swig/swig/issues/1158
+void throw_exception (int error);
+#else
+void throw_exception [[noreturn]] (int error);
+#endif
+
+/**
+ * @brief Class for wrapping sr_error_t.
+ * @class sysrepo_exception
+ */
+class sysrepo_exception : public std::runtime_error
+{
+public:
+ explicit sysrepo_exception(const sr_error_t error_code);
+ virtual ~sysrepo_exception() override;
+ sr_error_t error_code() const;
+private:
+ sr_error_t m_error_code;
+};
+
+/**
+ * @brief Class for wrapping ref sr_log_level_t.
+ * @class Logs
+ */
+class Logs
+{
+public:
+ Logs();
+ ~Logs();
+ /** Wrapper for [sr_log_stderr](@ref sr_log_stderr) */
+ void set_stderr(sr_log_level_t log_level);
+ /** Wrapper for [sr_log_syslog](@ref sr_log_syslog) */
+ void set_syslog(sr_log_level_t log_level);
+};
+
+/**@} */
+}
+#endif
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Tree.hpp b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Tree.hpp
new file mode 100644
index 000000000..31f3abd47
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Tree.hpp
@@ -0,0 +1,176 @@
+/**
+ * @file Trees.h
+ * @author Mislav Novakovic <mislav.novakovic@sartura.hr>
+ * @brief Sysrepo class header for C header trees.h.
+ *
+ * @copyright
+ * Copyright 2016 Deutsche Telekom AG.
+ *
+ * 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.
+ */
+
+#ifndef TREE_H
+#define TREE_H
+
+#include "Sysrepo.hpp"
+#include "Struct.hpp"
+
+extern "C" {
+#include "../sysrepo.h"
+#include "../sysrepo/trees.h"
+}
+
+namespace sysrepo {
+
+/**
+ * @defgroup classes C++/Python
+ * @{
+ */
+
+/**
+ * @brief Class for wrapping sr_node_t.
+ * @class Tree
+ */
+class Tree
+{
+public:
+ /** Constructor for an empty [sr_node_t](@ref sr_node_t).*/
+ Tree();
+ /** Wrapper for [sr_new_tree](@ref sr_new_tree).*/
+ Tree(const char *root_name, const char *root_module_name);
+ /** Wrapper for [sr_node_t](@ref sr_node_t).*/
+ Tree(sr_node_t *tree, S_Deleter deleter);
+ /** Wrapper for [sr_dup_tree](@ref sr_dup_tree).*/
+ S_Tree dup();
+ /** Get the node value.*/
+ S_Tree node();
+ /** Getter for name.*/
+ char *name() {return _node->name;};
+ /** Getter for type.*/
+ sr_type_t type() {return _node->type;};
+ /** Getter for dflt.*/
+ bool dflt() {return _node->dflt;};
+ /** Getter for data.*/
+ S_Data data() {S_Data data(new Data(_node->data, _node->type, _deleter)); return data;};
+ /** Getter for module_name.*/
+ char *module_name() {return _node->module_name;};
+ /** Getter for parent.*/
+ S_Tree parent();
+ /** Getter for next.*/
+ S_Tree next();
+ /** Getter for prev.*/
+ S_Tree prev();
+ /** Getter for first_child.*/
+ S_Tree first_child();
+ /** Getter for last_child.*/
+ S_Tree last_child();
+ /** Wrapper for [sr_print_tree_mem](@ref sr_print_tree_mem).*/
+ std::string to_string(int depth_limit);
+ /** Wrapper for [sr_print_val_mem](@ref sr_print_val_mem).*/
+ std::string value_to_string();
+ /** Wrapper for [sr_node_set_name](@ref sr_node_set_name).*/
+ void set_name(const char *name);
+ /** Wrapper for [sr_node_set_module](@ref sr_node_set_module).*/
+ void set_module(const char *module_name);
+ /** Wrapper for [sr_node_set_str_data](@ref sr_node_set_str_data).*/
+ void set_str_data(sr_type_t type, const char *string_val);
+ /** Wrapper for [sr_node_add_child](@ref sr_node_add_child).*/
+ void add_child(const char *child_name, const char *child_module_name, S_Tree child);
+ /** Setter for string value, type can be SR_STRING_T, SR_BINARY_T, SR_BITS_T, SR_ENUM_T,
+ * SR_IDENTITYREF_T and SR_INSTANCEID_T.*/
+ void set(const char *val, sr_type_t type = SR_STRING_T);
+ /** Setter for bool value.*/
+ void set(bool bool_val, sr_type_t type = SR_BOOL_T);
+ /** Setter for decimal64 value.*/
+ void set(double decimal64_val);
+ /** Setter for int8 value, C++ only.*/
+ void set(int8_t int8_val, sr_type_t type);
+ /** Setter for int16 value, C++ only.*/
+ void set(int16_t int16_val, sr_type_t type);
+ /** Setter for int32 value, C++ only.*/
+ void set(int32_t int32_val, sr_type_t type);
+ /** Setter for int64 value, type can be SR_INT8_T, SR_INT16_T, SR_INT32_T,
+ * SR_INT64_T, SR_UINT8_T, SR_UINT16_T and SR_UINT32_T,*/
+ void set(int64_t int64_val, sr_type_t type);
+ /** Setter for uint8 value, C++ only.*/
+ void set(uint8_t uint8_val, sr_type_t type);
+ /** Setter for uint16 value, C++ only.*/
+ void set(uint16_t uint16_val, sr_type_t type);
+ /** Setter for uint32 value, C++ only.*/
+ void set(uint32_t uint32_val, sr_type_t type);
+ /** Setter for uint64 value, C++ only.*/
+ void set(uint64_t uint64_val, sr_type_t type);
+ ~Tree();
+
+ friend class Session;
+ friend class Subscribe;
+
+private:
+ sr_node_t *_node;
+ S_Deleter _deleter;
+};
+
+/**
+ * @brief Class for wrapping sr_node_t array.
+ * @class Trees
+ */
+class Trees
+{
+public:
+ /** Constructor for an empty [sr_node_t](@ref sr_node_t) array.*/
+ Trees();
+ /** Wrapper for [sr_node_t](@ref sr_node_t) array, create n-array.*/
+ Trees(size_t n);
+ /** Wrapper for [sr_node_t](@ref sr_node_t) array, internal use only.*/
+ Trees(sr_node_t **trees, size_t *cnt, S_Deleter deleter = nullptr);
+ /** Wrapper for [sr_node_t](@ref sr_node_t) array, internal use only.*/
+ Trees(const sr_node_t *trees, const size_t n, S_Deleter deleter = nullptr);
+ /** Getter for [sr_node_t](@ref sr_node_t), get the n-th element in array.*/
+ S_Tree tree(size_t n);
+ /** Wrapper for [sr_dup_trees](@ref sr_dup_trees) */
+ S_Trees dup();
+ /** Getter for array size */
+ size_t tree_cnt() {return _cnt;};
+ ~Trees();
+
+ friend class Session;
+ friend class Subscribe;
+
+private:
+ size_t _cnt;
+ sr_node_t *_trees;
+ S_Deleter _deleter;
+};
+
+/**
+ * @brief Class for wrapping sr_node_t in callbacks.
+ * @class Trees_Holder
+ */
+class Trees_Holder
+{
+public:
+ /** Wrapper for [sr_node_t](@ref sr_node_t) array, used only in callbacks.*/
+ Trees_Holder(sr_node_t **trees, size_t *cnt);
+ /** Create [sr_node_t](@ref sr_node_t) array of n size.*/
+ S_Trees allocate(size_t n);
+ ~Trees_Holder();
+
+private:
+ size_t *p_cnt;
+ sr_node_t **p_trees;
+ bool _allocate;
+};
+
+/**@} */
+}
+#endif
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Xpath.hpp b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Xpath.hpp
new file mode 100644
index 000000000..4406134da
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/Xpath.hpp
@@ -0,0 +1,97 @@
+/**
+ * @file Xpath.h
+ * @author Mislav Novakovic <mislav.novakovic@sartura.hr>
+ * @brief Sysrepo class header for C header xpath_utils.h.
+ *
+ * @copyright
+ * Copyright 2016 Deutsche Telekom AG.
+ *
+ * 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.
+ */
+
+#ifndef XPATH_H
+#define XPATH_H
+
+#include <iostream>
+
+extern "C" {
+#include "../sysrepo/xpath.h"
+}
+
+namespace sysrepo {
+
+/**
+ * @defgroup classes C++/Python
+ * @{
+ */
+
+/**
+ * @brief Class for wrapping sr_xpath_ctx_t.
+ * @class Xpath_Ctx
+ */
+class Xpath_Ctx
+{
+public:
+ /** Constructor for an empty [sr_xpath_ctx_t](@ref sr_xpath_ctx_t).*/
+ Xpath_Ctx();
+ /** Getter for begining.*/
+ char *begining() {if (_state != nullptr) return _state->begining; return nullptr;};
+ /** Getter for current_node.*/
+ char *current_node() {if (_state != nullptr) return _state->current_node; return nullptr;};
+ /** Getter for replaced_position.*/
+ char *replaced_position() {if (_state != nullptr) return _state->replaced_position; return nullptr;};
+ /** Getter for replaced_char.*/
+ char replaced_char() {if (_state != nullptr) return _state->replaced_char; return (char) 0;};
+ ~Xpath_Ctx();
+ /** Wrapper for [sr_xpath_next_node](@ref sr_xpath_next_node).*/
+ char *next_node(char *xpath) {return sr_xpath_next_node(xpath, _state);};
+ /** Wrapper for [sr_xpath_next_node_with_ns](@ref sr_xpath_next_node_with_ns).*/
+ char *next_node_with_ns(char *xpath) {return sr_xpath_next_node_with_ns(xpath, _state);};
+ /** Wrapper for [sr_xpath_next_key_name](@ref sr_xpath_next_key_name).*/
+ char *next_key_name(char *xpath) {return sr_xpath_next_key_name(xpath, _state);};
+ /** Wrapper for [sr_xpath_next_key_value](@ref sr_xpath_next_key_value).*/
+ char *next_key_value(char *xpath) {return sr_xpath_next_key_value(xpath, _state);};
+ /** Wrapper for [sr_xpath_node](@ref sr_xpath_node).*/
+ char *node(char *xpath, const char *node_name) {return sr_xpath_node(xpath, node_name, _state);};
+ /** Wrapper for [sr_xpath_node_rel](@ref sr_xpath_node_rel).*/
+ char *node_rel(char *xpath, const char *node_name) {return sr_xpath_node_rel(xpath, node_name, _state);};
+ /** Wrapper for [sr_xpath_node_idx](@ref sr_xpath_node_idx).*/
+ char *node_idx(char *xpath, size_t index) {return sr_xpath_node_idx(xpath, index, _state);};
+ /** Wrapper for [sr_xpath_node_idx_rel](@ref sr_xpath_node_idx_rel).*/
+ char *node_idx_rel(char *xpath, size_t index) {return sr_xpath_node_idx_rel(xpath, index, _state);};
+ /** Wrapper for [sr_xpath_node_key_value](@ref sr_xpath_node_key_value).*/
+ char *node_key_value(char *xpath, const char *key) {return sr_xpath_node_key_value(xpath, key, _state);};
+ /** Wrapper for [sr_xpath_node_key_value_idx](@ref sr_xpath_node_key_value_idx).*/
+ char *node_key_value_idx(char *xpath, size_t index) {return sr_xpath_node_key_value_idx(xpath, index, _state);};
+ /** Wrapper for [sr_xpath_key_value](@ref sr_xpath_key_value).*/
+ char *key_value(char *xpath, const char *node_name, const char *key_name) {
+ return sr_xpath_key_value(xpath, node_name, key_name, _state);};
+ /** Wrapper for [sr_xpath_key_value_idx](@ref sr_xpath_key_value_idx).*/
+ char *key_value_idx(char *xpath, size_t node_index, size_t key_index) {
+ return sr_xpath_key_value_idx(xpath, node_index, key_index, _state);};
+ /** Wrapper for [sr_xpath_last_node](@ref sr_xpath_last_node).*/
+ char *last_node(char *xpath) {return sr_xpath_last_node(xpath, _state);};
+ /** Wrapper for [sr_xpath_node_name](@ref sr_xpath_node_name).*/
+ char *node_name(const char *xpath) {return sr_xpath_node_name(xpath);};
+ /** Wrapper for [sr_xpath_node_name_eq](@ref sr_xpath_node_name_eq).*/
+ bool node_name_eq(const char *xpath, const char *node_str) {return sr_xpath_node_name_eq(xpath, node_str);};
+ /** Wrapper for [sr_xpath_recover](@ref sr_xpath_recover).*/
+ void recover() {return sr_xpath_recover(_state);};
+
+private:
+ sr_xpath_ctx_t *_state;
+};
+
+/**@} */
+}
+#endif
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/plugins.h b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/plugins.h
new file mode 100644
index 000000000..3c4efc9a9
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/plugins.h
@@ -0,0 +1,139 @@
+/**
+ * @file plugins.h
+ * @author Rastislav Szabo <raszabo@cisco.com>, Lukas Macko <lmacko@cisco.com>
+ * @brief Sysrepo helpers for plugin integrations.
+ *
+ * @copyright
+ * Copyright 2016 Cisco Systems, Inc.
+ *
+ * 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.
+ */
+
+#ifndef SYSREPO_PLUGINS_H_
+#define SYSREPO_PLUGINS_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <sysrepo.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup plugin_utils Plugin Utilities
+ * @{
+ *
+ * @brief Utilities that expand sysrepo API aimed for sysrepo plugins.
+ *
+ * The provided features are: logging macros.
+ */
+
+/** Prints an error message (with format specifiers). */
+#define SRP_LOG_ERR(MSG, ...) SRP_LOG__INTERNAL(SR_LL_ERR, MSG, __VA_ARGS__)
+/** Prints an error message. */
+#define SRP_LOG_ERR_MSG(MSG) SRP_LOG__INTERNAL(SR_LL_ERR, MSG "%s", "")
+
+/** Prints a warning message (with format specifiers). */
+#define SRP_LOG_WRN(MSG, ...) SRP_LOG__INTERNAL(SR_LL_WRN, MSG, __VA_ARGS__)
+/** Prints a warning message. */
+#define SRP_LOG_WRN_MSG(MSG) SRP_LOG__INTERNAL(SR_LL_WRN, MSG "%s", "")
+
+/** Prints an informational message (with format specifiers). */
+#define SRP_LOG_INF(MSG, ...) SRP_LOG__INTERNAL(SR_LL_INF, MSG, __VA_ARGS__)
+/** Prints an informational message. */
+#define SRP_LOG_INF_MSG(MSG) SRP_LOG__INTERNAL(SR_LL_INF, MSG "%s", "")
+
+/** Prints a development debug message (with format specifiers). */
+#define SRP_LOG_DBG(MSG, ...) SRP_LOG__INTERNAL(SR_LL_DBG, MSG, __VA_ARGS__)
+/** Prints a development debug message. */
+#define SRP_LOG_DBG_MSG(MSG) SRP_LOG__INTERNAL(SR_LL_DBG, MSG "%s", "")
+
+/**@} plugin_utils */
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Internal macros (not intended to be used directly)
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef NDEBUG
+ #define SRP_LOG_PRINT_FUNCTION_NAMES (0) /**< Do not print function names in messages */
+#else
+ #define SRP_LOG_PRINT_FUNCTION_NAMES (1) /**< Every message will include the function that generated the output */
+#endif
+
+extern volatile uint8_t sr_ll_stderr; /**< Holds current level of stderr debugs. */
+extern volatile uint8_t sr_ll_syslog; /**< Holds current level of syslog debugs. */
+
+/**
+ * @brief Matching log level to message beginning
+ */
+#define SRP_LOG__LL_STR(LL) \
+ ((SR_LL_DBG == LL) ? "DBG" : \
+ (SR_LL_INF == LL) ? "INF" : \
+ (SR_LL_WRN == LL) ? "WRN" : \
+ "ERR")
+
+/**
+ * @brief Matching log level to message macros
+ */
+#define SRP_LOG__LL_FACILITY(LL) \
+ ((SR_LL_DBG == LL) ? LOG_DEBUG : \
+ (SR_LL_INF == LL) ? LOG_INFO : \
+ (SR_LL_WRN == LL) ? LOG_WARNING : \
+ LOG_ERR)
+
+#if SRP_LOG_PRINT_FUNCTION_NAMES
+/**
+ * @brief Syslog output macro with function names.
+ */
+#define SRP_LOG__SYSLOG(LL, MSG, ...) \
+ syslog(SRP_LOG__LL_FACILITY(LL), "[%s] (%s:%d) " MSG, SRP_LOG__LL_STR(LL), __func__, __LINE__, __VA_ARGS__);
+/**
+ * @brief Stderr output macro with function names.
+ */
+#define SRP_LOG__STDERR(LL, MSG, ...) \
+ fprintf(stderr, "[%s] (%s:%d) " MSG "\n", SRP_LOG__LL_STR(LL), __func__, __LINE__, __VA_ARGS__);
+#else
+/**
+ * @brief Syslog output macro without function names.
+ */
+#define SRP_LOG__SYSLOG(LL, MSG, ...) \
+ syslog(SRP_LOG__LL_FACILITY(LL), "[%s] " MSG, SRP_LOG__LL_STR(LL), __VA_ARGS__);
+/**
+ * @brief Stderr output macro without function names.
+ */
+#define SRP_LOG__STDERR(LL, MSG, ...) \
+ fprintf(stderr, "[%s] " MSG "\n", SRP_LOG__LL_STR(LL), __VA_ARGS__);
+#endif
+
+/**
+ * @brief Internal outptu macro.
+ */
+#define SRP_LOG__INTERNAL(LL, MSG, ...) \
+ do { \
+ if (sr_ll_stderr >= LL) \
+ SRP_LOG__STDERR(LL, MSG, __VA_ARGS__) \
+ if (sr_ll_syslog >= LL) \
+ SRP_LOG__SYSLOG(LL, MSG, __VA_ARGS__) \
+ } while(0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SYSREPO_PLUGINS_H_ */
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/trees.h b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/trees.h
new file mode 100644
index 000000000..8db1602e6
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/trees.h
@@ -0,0 +1,226 @@
+/**
+ * @file trees.h
+ * @author Rastislav Szabo <raszabo@cisco.com>, Lukas Macko <lmacko@cisco.com>,
+ * Milan Lenco <milan.lenco@pantheon.tech>
+ * @brief Functions for simplified manipulation with Sysrepo trees.
+ *
+ * @copyright
+ * Copyright 2016 Cisco Systems, Inc.
+ *
+ * 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.
+ */
+
+#ifndef SYSREPO_TREES_H_
+#define SYSREPO_TREES_H_
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup trees Tree Manipulation Utilities
+ * @{
+ *
+ * @brief Set of functions facilitating simplified manipulation and traversal
+ * of Sysrepo trees. As there are many connections between the tree nodes
+ * and also some internal attributes associated with each node, it is actually
+ * recommended to use these function rather than to allocate and initialize trees
+ * manually, which is very likely to lead to time-wasting and hard-to-debug programming
+ * errors.
+ * Iterative tree loading (@see SR_GET_SUBTREE_ITERATIVE) even requires to use
+ * designated functions for tree traversal -- ::sr_node_get_child and ::sr_node_get_next_sibling.
+ *
+ * Another added benefit of using these function is that the trees created using
+ * ::sr_new_tree and ::sr_new_trees will be allocated using the Sysrepo's own memory management
+ * (if enabled) which was proven to be more efficient for larger data sets
+ * (far less copying, quicker conversion to/from google protocol buffer messages,
+ * stable memory footprint, etc.).
+ */
+
+/**
+ * @brief Allocate an instance of Sysrepo tree. The newly allocated tree has only
+ * one node -- the tree root -- and can be expanded to its full desired size
+ * through a repeated use of the function ::sr_node_add_child.
+ *
+ * @param [in] root_name Name for the newly allocated tree root. Can be NULL.
+ * @param [in] root_module_name Name of the module that defines scheme of the tree root.
+ * Can be NULL.
+ * @param [out] tree Returned newly allocated Sysrepo tree.
+ */
+int sr_new_tree(const char *root_name, const char *root_module_name, sr_node_t **tree);
+
+/**
+ * @brief Allocate an array of sysrepo trees (uninitialized tree roots).
+ *
+ * @param [in] tree_cnt Length of the array to allocate.
+ * @param [out] trees Returned newly allocated array of trees.
+ */
+int sr_new_trees(size_t tree_cnt, sr_node_t **trees);
+
+/**
+ * @brief Reallocate an array of sysrepo trees (uninitialized tree roots).
+ *
+ * @param [in] old_tree_cnt Current length of the tree array.
+ * @param [in] new_tree_cnt Desired length of the tree array.
+ * @param [in,out] trees Returned newly allocated/enlarged array of trees.
+ */
+int sr_realloc_trees(size_t old_tree_cnt, size_t new_tree_cnt, sr_node_t **trees);
+
+/**
+ * @brief Set/change name of a Sysrepo node.
+ *
+ * @param [in] node Sysrepo node to change the name of.
+ * @param [in] name Name to set.
+ */
+int sr_node_set_name(sr_node_t *node, const char *name);
+
+/**
+ * @brief Set/change module of a Sysrepo node.
+ *
+ * @param [in] node Sysrepo node to change the module of.
+ * @param [in] module_name Module name to set.
+ */
+int sr_node_set_module(sr_node_t *node, const char *module_name);
+
+/**
+ * @brief Store data of string type into the Sysrepo node data.
+ *
+ * @param [in] node Sysrepo node to edit.
+ * @param [in] type Exact type of the data.
+ * @param [in] string_val String value to set.
+ */
+int sr_node_set_str_data(sr_node_t *node, sr_type_t type, const char *string_val);
+
+/**
+ * @brief Store data of string type into the Sysrepo node data. The actual data
+ * will be built from the a format string and a variable arguments list.
+ *
+ * @param [in] node Sysrepo node to edit.
+ * @param [in] type Exact type of the data.
+ * @param [in] format Format string used to build the data.
+ */
+int sr_node_build_str_data(sr_node_t *node, sr_type_t type, const char *format, ...);
+
+/**
+ * @brief Create a new child for a given Sysrepo node.
+ *
+ * @param [in] parent Sysrepo node that should be parent of the newly created node.
+ * @param [in] child_name Name of the newly created child node. Can be NULL.
+ * @param [in] child_module_name Name of the module that defines scheme of the newly created
+ * child node. Can be NULL.
+ * @param [out] child Returned newly allocated child node.
+ */
+int sr_node_add_child(sr_node_t *parent, const char *child_name, const char *child_module_name,
+ sr_node_t **child);
+
+/**
+ * @brief Duplicate node and all its descendants (with or without Sysrepo memory context)
+ * into a new instance of Sysrepo tree with memory context.
+ *
+ * @param [in] tree Sysrepo tree to duplicate.
+ * @param [out] tree_dup Returned duplicate of the input tree.
+ */
+int sr_dup_tree(const sr_node_t *tree, sr_node_t **tree_dup);
+
+/**
+ * @brief Duplicate an array of trees (with or without Sysrepo memory context) into a new
+ * array of trees with memory context.
+ *
+ * @param [in] trees Array of sysrepo trees to duplicate.
+ * @param [in] count Size of the array to duplicate.
+ * @param [out] trees_dup Returned duplicate of the input array.
+ */
+int sr_dup_trees(const sr_node_t *trees, size_t count, sr_node_t **trees_dup);
+
+/**
+ * @brief Print sysrepo tree to STDOUT.
+ *
+ * @param [in] tree Sysrepo tree to print.
+ * @param [in] depth_limit Maximum number of tree levels to print.
+ */
+int sr_print_tree(const sr_node_t *tree, int depth_limit);
+
+/**
+ * @brief Print sysrepo tree to the specified file descriptor.
+ *
+ * @param [in] fd File descriptor to print the tree into.
+ * @param [in] tree Sysrepo tree to print.
+ * @param [in] depth_limit Maximum number of tree levels to print.
+ */
+int sr_print_tree_fd(int fd, const sr_node_t *tree, int depth_limit);
+
+/**
+ * @brief Print sysrepo tree to the specified output file stream.
+ *
+ * @param [in] stream Output file stream to print the tree into.
+ * @param [in] tree Sysrepo tree to print.
+ * @param [in] depth_limit Maximum number of tree levels to print.
+ */
+int sr_print_tree_stream(FILE *stream, const sr_node_t *tree, int depth_limit);
+
+/**
+ * @brief Print sysrepo tree into a newly allocated memory buffer.
+ * The caller is expected to eventually free the returned string.
+ *
+ * @param [in] mem_p Pointer to store the resulting dump.
+ * @param [in] tree Sysrepo tree to print.
+ * @param [in] depth_limit Maximum number of tree levels to print.
+ */
+int sr_print_tree_mem(char **mem_p, const sr_node_t *tree, int depth_limit);
+
+/**
+ * @brief Returns pointer to the first child (based on the schema) of a given node.
+ * For a fully loaded tree it is equivalent to "node->first_child". For a partially
+ * loaded tree (@see SR_GET_SUBTREE_ITERATIVE) it may internally issue a sysrepo
+ * get-subtree-chunk request in order to obtain the data of the child
+ * (and the data of some surrounding nodes with it for efficiency).
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] node Node to get the child of.
+ * @return Pointer to a child node. NULL if there is none or an error occured.
+ */
+sr_node_t *sr_node_get_child(sr_session_ctx_t *session, sr_node_t *node);
+
+/**
+ * @brief Returns pointer to the next sibling (based on the schema) of a given node.
+ * For a fully loaded tree it is equivalent to "node->next". For a partially
+ * loaded tree (@see SR_GET_SUBTREE_ITERATIVE) it may internally issue a sysrepo
+ * get-subtree-chunk request in order to obtain the data of the next sibling
+ * (and the data of some surrounding nodes with it for efficiency).
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] node Node to get the next sibling of.
+ * @return Pointer to the next sibling. NULL if this is the last sibling or an error occured.
+ */
+sr_node_t *sr_node_get_next_sibling(sr_session_ctx_t *session, sr_node_t *node);
+
+/**
+ * @brief Get the parent of a given node. It is equivalent to "node->parent", but for
+ * a partially loaded tree it is preferred to use this function rather than to access
+ * the pointer directly just for the sake of code cleanliness.
+ *
+ * @param[in] session Session context acquired with ::sr_session_start call.
+ * @param[in] node Node to get the parent of.
+ * @return Pointer to the node's parent or NULL if the node is a root of a (sub)tree.
+ */
+sr_node_t *sr_node_get_parent(sr_session_ctx_t *session, sr_node_t *node);
+
+/**@} trees */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SYSREPO_TREES_H_ */
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/values.h b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/values.h
new file mode 100644
index 000000000..049c82f19
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/values.h
@@ -0,0 +1,196 @@
+/**
+ * @file values.h
+ * @author Rastislav Szabo <raszabo@cisco.com>, Lukas Macko <lmacko@cisco.com>,
+ * Milan Lenco <milan.lenco@pantheon.tech>
+ * @brief Functions for simplified manipulation with Sysrepo values.
+ *
+ * @copyright
+ * Copyright 2016 Cisco Systems, Inc.
+ *
+ * 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.
+ */
+
+#ifndef SYSREPO_VALUES_H_
+#define SYSREPO_VALUES_H_
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup values Value Manipulation Utilities
+ * @{
+ *
+ * @brief Set of functions facilitating simplified manipulation with sysrepo
+ * values. It is not necessary to use these functions in any scenario, values
+ * can be allocated and initialized manually (just remember to set all uninitialized
+ * members to zero!).
+ *
+ * Using these utilities, however, has several benefits. Firstly, all the memory
+ * allocations associated with creating values and setting their attributes get
+ * hidden behind these functions. The "old-way" was (and still is) to set xpath
+ * and string values using strdup, which may repeat in applications communicating
+ * with sysrepo very often and becomes very annoying to write.
+ * Secondly, the programmer may actually forget to copy or give-up on the ownership
+ * of a string passed to sysrepo value which will then get unexpectedly deallocated
+ * in ::sr_free_val or ::sr_free_values.
+ * The third benefit is that the values created using ::sr_new_val
+ * and ::sr_new_values will be allocated using the Sysrepo's own memory management
+ * (if enabled) which was proven to be more efficient for larger data sets
+ * (far less copying, quicker conversion to/from google protocol buffer messages,
+ * stable memory footprint, etc.).
+ */
+
+/**
+ * @brief Allocate an instance of Sysrepo value.
+ *
+ * @param [in] xpath Xpath to set for the newly allocated value. Can be NULL.
+ * @param [out] value Returned newly allocated value.
+ */
+int sr_new_val(const char *xpath, sr_val_t **value);
+
+/**
+ * @brief Allocate an array of sysrepo values.
+ *
+ * @param [in] value_cnt Length of the array to allocate.
+ * @param [out] values Returned newly allocated array of values.
+ */
+int sr_new_values(size_t value_cnt, sr_val_t **values);
+
+/**
+ * @brief Reallocate an array of sysrepo values.
+ *
+ * @param [in] old_value_cnt Current length of the value array.
+ * @param [in] new_value_cnt Desired length of the value array.
+ * @param [in,out] values Returned newly allocated/enlarged array of values.
+ */
+int sr_realloc_values(size_t old_value_cnt, size_t new_value_cnt, sr_val_t **values);
+
+/**
+ * @brief Set/change xpath of a Sysrepo value.
+ *
+ * @param [in] value Sysrepo value to change the xpath of.
+ * @param [in] xpath XPath to set.
+ */
+int sr_val_set_xpath(sr_val_t *value, const char *xpath);
+
+/**
+ * @brief Set/change xpath of a Sysrepo value to a new one, built from
+ * a format string and a variable arguments list.
+ *
+ * @param [in] value Sysrepo value to change the xpath of.
+ * @param [in] format Format string used to build XPath.
+ */
+int sr_val_build_xpath(sr_val_t *value, const char *format, ...);
+
+/**
+ * @brief Store data of string type into the Sysrepo value data.
+ *
+ * @param [in] value Sysrepo value to edit.
+ * @param [in] type Exact type of the data.
+ * @param [in] string_val String value to set.
+ */
+int sr_val_set_str_data(sr_val_t *value, sr_type_t type, const char *string_val);
+
+/**
+ * @brief Store data of string type into the Sysrepo value data. The actual data
+ * will be built from the a format string and a variable arguments list.
+ *
+ * @param [in] value Sysrepo value to edit.
+ * @param [in] type Exact type of the data.
+ * @param [in] format Format string used to build the data.
+ */
+int sr_val_build_str_data(sr_val_t *value, sr_type_t type, const char *format, ...);
+
+/**
+ * @brief Duplicate value (with or without Sysrepo memory context) into a new
+ * instance with memory context.
+ *
+ * @param [in] value Sysrepo value to duplicate
+ * @param [out] value_dup Returned duplicate of the input value.
+ */
+int sr_dup_val(const sr_val_t *value, sr_val_t **value_dup);
+
+/**
+ * @brief Duplicate values (with or without Sysrepo memory context) into a new
+ * array with memory context.
+ *
+ * @param [in] values Array of sysrepo values to duplicate
+ * @param [in] count Size of the array to duplicate.
+ * @param [out] values_dup Returned duplicate of the input array.
+ */
+int sr_dup_values(const sr_val_t *values, size_t count, sr_val_t **values_dup);
+
+/**
+ * @brief Print sysrepo value to STDOUT.
+ *
+ * @param [in] value Sysrepo value to print.
+ */
+int sr_print_val(const sr_val_t *value);
+
+/**
+ * @brief Print sysrepo value to the specified file descriptor.
+ *
+ * @param [in] fd File descriptor to print the value into.
+ * @param [in] value Sysrepo value to print.
+ */
+int sr_print_val_fd(int fd, const sr_val_t *value);
+
+/**
+ * @brief Print sysrepo value to the specified output file stream.
+ *
+ * @param [in] stream Output file stream to print the value into.
+ * @param [in] value Sysrepo value to print.
+ */
+int sr_print_val_stream(FILE *stream, const sr_val_t *value);
+
+/**
+ * @brief Print sysrepo value into a newly allocated memory buffer.
+ * The caller is expected to eventually free the returned string.
+ *
+ * @param [in] mem_p Pointer to store the resulting dump.
+ * @param [in] value Sysrepo value to print.
+ */
+int sr_print_val_mem(char **mem_p, const sr_val_t *value);
+
+/**
+ * @brief Converts value to string representation
+ * @param [in] value
+ * @return allocated string representation of value (must be freed by caller), NULL in case of error
+ * @note In case of SR_DECIMAL64_T type, number of fraction digits doesn't have to
+ * correspond to schema.
+ */
+char *sr_val_to_str(const sr_val_t *value);
+
+/**
+ * @brief Converts value to string and prints it to the provided buffer including
+ * terminating NULL byte
+ * @param [in] value
+ * @param [in] buffer - buffer provided by caller where the data will be printed
+ * @param [in] size - the size of the buffer
+ * @return number of characters that was written in case of success, otherwise number of characters which would have been
+ * written if enough space had been available (excluding terminating NULL byte)
+ * @note In case of SR_DECIMAL64_T type, number of fraction digits doesn't have to
+ * correspond to schema.
+ */
+int sr_val_to_buff(const sr_val_t *value, char buffer[], size_t size);
+
+/**@} values */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SYSREPO_VALUES_H_ */
diff --git a/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/xpath.h b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/xpath.h
new file mode 100644
index 000000000..7eca41e57
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/sysrepo/xpath.h
@@ -0,0 +1,232 @@
+/**
+ * @file xpath.h
+ * @author Rastislav Szabo <raszabo@cisco.com>, Lukas Macko <lmacko@cisco.com>
+ * @brief Sysrepo helpers for node's address manipulation.
+ *
+ * @copyright
+ * Copyright 2015 Cisco Systems, Inc.
+ *
+ * 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.
+ */
+
+#ifndef SYSREPO_XPATH_H_
+#define SYSREPO_XPATH_H_
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup xpath_utils XPath Processing Utilities
+ * @{
+ *
+ * @brief Set of helpers working on a subset of xpath expressions used of node identification
+ * Functions modify inputs arguments by placing termination zero at appropriate places to save up
+ * string duplication. The state of processing is stored in ::sr_xpath_ctx_t opaque for user.
+ * It allows to continue in processing where the processing stopped or recover processed input.
+ *
+ * Similarly to strtok function in all subsequent calls that is supposed to work with the same
+ * input xpath must be NULL.
+ */
+
+/**
+ * @brief State of xpath parsing. User must not modify nor rely on the content
+ * of the structure.
+ */
+typedef struct sr_xpath_ctx_s {
+ char *begining; /**< Pointer to the begining of the processed string */
+ char *current_node; /**< Pointer to the currently processed node, used as a context for key search */
+ char *replaced_position; /**< Pointer to the posistion where the last terminating 0 by was written */
+ char replaced_char; /**< Character that was overwritten by the last termination 0 */
+} sr_xpath_ctx_t;
+
+/**
+ * @brief The function returns a pointer to the following node. If xpath is
+ * not NULL returns the first node name, otherwise returns the subsequent node
+ * according to the state.
+ *
+ * The state is modified upon function successful return from function, so the subsequent
+ * calls can continue in processing or xpath can be recovered by calling ::sr_xpath_recover.
+ *
+ * @note It writes terminating zero at the and of the node name.
+ *
+ * @note Skips the namespace if it is present to get node name qualified by namespace use ::sr_xpath_next_node_with_ns
+ *
+ * @param [in] xpath - xpath to be processed, can be NULL
+ * @param [in] state
+ * @return Pointer to the node name, NULL if there are no more node names
+ */
+char *sr_xpath_next_node(char *xpath, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Returns pointer to the last node.
+ * @param [in] xpath
+ * @param [in] state
+ * @return Pointer to the last node
+ */
+char *sr_xpath_last_node(char *xpath, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Same as ::sr_xpath_next_node with the difference that namespace is included in result if present in xpath
+ *
+ * @param [in] xpath - xpath to be processed, can be NULL if the user wants to continue in processing of previous input
+ * @param [in] state
+ * @return Pointer to the node name including namespace, NULL if there are no more node names
+ */
+char *sr_xpath_next_node_with_ns(char *xpath, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Returns the name of the next key at the current level in processed xpath.
+ *
+ * @param [in] xpath
+ * @param [in] state
+ * @return Pointer to the key name, NULL if there are no more keys at the current level
+ */
+char *sr_xpath_next_key_name(char *xpath, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Returns the value of the next key at the current level in processed xpath.
+ *
+ * @param [in] xpath
+ * @param [in] state
+ * @return Pointer to the key value, NULL if there are no more keys at the current level
+ */
+char *sr_xpath_next_key_value(char *xpath, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Returns a pointer to the node specified by name. It searches from the beginning of the xpath, returns first match.
+ * Can be used to jump at the desired node xpath and subsequent analysis of key values.
+ *
+ * @param [in] xpath
+ * @param [in] node_name
+ * @param [in] state
+ * @return Pointer to the node, NULL if the node with the specified name is not found
+ */
+char *sr_xpath_node(char *xpath, const char *node_name, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Similar to ::sr_xpath_node. The difference is that search start at current node
+ * according to the state.
+ *
+ * @param [in] xpath
+ * @param [in] node_name
+ * @param [in] state
+ * @return Pointer to the node, NULL if the node with the specified name is not found
+ */
+char *sr_xpath_node_rel(char *xpath, const char *node_name, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Returns node specified by index starting at the begin of expression.
+ * First node has index 0.
+ *
+ * @param [in] xpath
+ * @param [in] index
+ * @param [in] state
+ * @return Pointer to the specified node, NULL if the index is out of bounds
+ */
+char *sr_xpath_node_idx(char *xpath, size_t index, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Return node specified by index. Following node has index zero.
+ *
+ * @param [in] xpath
+ * @param [in] index
+ * @param [in] state
+ * @return Pointer to the specified node, NULL if the index is out of bounds
+ */
+char *sr_xpath_node_idx_rel(char *xpath, size_t index, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Looks up the value for the key at the current level in xpath.
+ *
+ * @param [in] xpath
+ * @param [in] key - key name to be looked up
+ * @param [in] state
+ * @return Key value, NULL if the key with the specified name is not found
+ */
+char *sr_xpath_node_key_value(char *xpath, const char *key, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Looks up the value for the key at the current level in xpath specified by index.
+ * First key has index zero.
+ *
+ * @param [in] xpath
+ * @param [in] index
+ * @param [in] state
+ * @return Key value, NULL if the index is out of bound
+ */
+char *sr_xpath_node_key_value_idx(char *xpath, size_t index, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Looks up the value of the key in a node specified by name.
+ *
+ * @param [in] xpath
+ * @param [in] node_name
+ * @param [in] key_name
+ * @param [in] state
+ * @return Pointer to the key value, NULL if not found
+ */
+char *sr_xpath_key_value(char *xpath, const char *node_name, const char *key_name, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Looks up the value of the key in a node specified by index. First node has index zero.
+ *
+ * @param [in] xpath
+ * @param [in] node_index
+ * @param [in] key_index
+ * @param [in] state
+ * @return Pointer to the key value, NULL if not found or index out of bound
+ */
+char *sr_xpath_key_value_idx(char *xpath, size_t node_index, size_t key_index, sr_xpath_ctx_t *state);
+
+/**
+ * @brief Returns pointer to the string after the last slash in xpath (node name).
+ *
+ * @note The returned string can also contain namespace and/or key values
+ * if they were specified for the last node in xpath.
+ *
+ * @param [in] xpath
+ * @return Result, NULL in case of the slash was not found
+ */
+char *sr_xpath_node_name(const char *xpath);
+
+/**
+ * @brief Compares string after the last slash in xpath (node name) with provided string.
+ *
+ * @note The returned string can also contain namespace and/or key values
+ * if they were specified for the last node in xpath.
+ *
+ * @param [in] xpath
+ * @param [in] node_str String to test for equality.
+ * @return true in case that the Node names are equal, false otherwise
+ */
+bool sr_xpath_node_name_eq(const char *xpath, const char *node_str);
+
+/**
+ * @brief Recovers the xpath string to the original state (puts back the character
+ * that was replaced by termination zero).
+ *
+ * @param [in] state
+ */
+void sr_xpath_recover(sr_xpath_ctx_t *state);
+
+/**@} xpath_utils */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SYSREPO_XPATH_H_ */
+
diff --git a/test/mocks/pnfsimulator/netconfsimulator/pom.xml b/test/mocks/pnfsimulator/netconfsimulator/pom.xml
new file mode 100644
index 000000000..7c8d5c78e
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/pom.xml
@@ -0,0 +1,278 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============LICENSE_START=======================================================
+ Simulator
+ ================================================================================
+ Copyright (C) 2019 Nokia. All rights reserved.
+ ================================================================================
+ 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.
+ ============LICENSE_END=========================================================
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onap.simulator</groupId>
+ <artifactId>simulator-parent</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>netconfsimulator</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <maven.build.timestamp.format>yyyyMMdd'T'HHmmss</maven.build.timestamp.format>
+ <docker.registry>nexus3.onap.org:10003</docker.registry>
+ <docker.image.tag>latest</docker.image.tag>
+ <docker.image.name>onap/${project.artifactId}</docker.image.name>
+ <spring.boot.version>2.1.6.RELEASE</spring.boot.version>
+ <spring.kafka.version>2.2.7.RELEASE</spring.kafka.version>
+ <apache.httpclient.version>4.5.6</apache.httpclient.version>
+ <dependency.directory.name>libs</dependency.directory.name>
+ <dependency.directory.location>${project.build.directory}/${dependency.directory.name}
+ </dependency.directory.location>
+ <netopeer-saver-project-name>netopeer-change-saver</netopeer-saver-project-name>
+ <netopeer-saver-source-dir>${project.basedir}/netopeer-change-saver-native</netopeer-saver-source-dir>
+ <netopeer-saver-build-dir>${project.build.directory}/cmake</netopeer-saver-build-dir>
+ <netopeer-saver-executable-dir>${netopeer-saver-build-dir}/bin</netopeer-saver-executable-dir>
+ <skipITs>true</skipITs>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter</artifactId>
+ <version>${spring.boot.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ <version>${spring.boot.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ <version>1.18.2</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>jnc</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-websocket</artifactId>
+ <version>${spring.boot.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.websocket</groupId>
+ <artifactId>javax.websocket-api</artifactId>
+ <version>1.1</version>
+ </dependency>
+
+ <!-- Kafka -->
+
+ <dependency>
+ <groupId>org.springframework.kafka</groupId>
+ <artifactId>spring-kafka</artifactId>
+ <version>${spring.kafka.version}</version>
+ </dependency>
+
+ <!-- TEST DEPENDENCIES -->
+
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.9.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>2.18.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>5.3.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.vintage</groupId>
+ <artifactId>junit-vintage-engine</artifactId>
+ <version>5.3.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <version>${spring.boot.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.kafka</groupId>
+ <artifactId>spring-kafka-test</artifactId>
+ <version>${spring.kafka.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.bitbucket.radistao.test</groupId>
+ <artifactId>before-after-spring-test-runner</artifactId>
+ <version>0.1.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.palantir.docker.compose</groupId>
+ <artifactId>docker-compose-rule-junit4</artifactId>
+ <version>0.29.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.5.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <version>4.5.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>${apache.httpclient.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <version>${apache.httpclient.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger2</artifactId>
+ <version>2.9.2</version>
+ </dependency>
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger-ui</artifactId>
+ <version>2.9.2</version>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <configuration>
+ <outputDirectory>${dependency.directory.location}</outputDirectory>
+ <includeScope>runtime</includeScope>
+ <silent>true</silent>
+ </configuration>
+ <executions>
+ <execution>
+ <id>copy-external-dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-dependencies</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.19</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-surefire-provider</artifactId>
+ <version>1.1.1</version>
+ </dependency>
+ </dependencies>
+ <configuration>
+ <detail>true</detail>
+ <printSummary>true</printSummary>
+ <useSystemClassLoader>false</useSystemClassLoader>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <version>2.19.1</version>
+ <configuration>
+ <skipITs>${skipITs}</skipITs>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>com.spotify</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <version>1.1.1</version>
+ <configuration>
+ <imageName>${docker.registry}/${docker.image.name}</imageName>
+ <dockerDirectory>${project.basedir}/docker</dockerDirectory>
+ <forceTags>true</forceTags>
+ <registryUrl>${docker.registry}</registryUrl>
+ <imageTags>
+ <tag>latest</tag>
+ <tag>${project.version}</tag>
+ <tag>${project.version}-${maven.build.timestamp}</tag>
+ </imageTags>
+ <resources>
+ <resource>
+ <targetPath>${dependency.directory.name}</targetPath>
+ <directory>${dependency.directory.location}</directory>
+ </resource>
+ <resource>
+ <targetPath>/</targetPath>
+ <directory>${project.build.directory}</directory>
+ <include>${project.build.finalName}.jar</include>
+ </resource>
+ </resources>
+ <forceTags>true</forceTags>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <repositories>
+ <repository>
+ <id>Palantir</id>
+ <url>https://dl.bintray.com/palantir/releases/</url>
+ </repository>
+ </repositories>
+</project>
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/Configuration.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/Configuration.java
new file mode 100644
index 000000000..92e5b2327
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/Configuration.java
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.springframework.context.annotation.Bean;
+
+@org.springframework.context.annotation.Configuration
+public class Configuration {
+
+ @Bean
+ public HttpClient httpClient() {
+ return HttpClientBuilder.create().build();
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/Main.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/Main.java
new file mode 100644
index 000000000..e2a0ed0c0
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/Main.java
@@ -0,0 +1,31 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.netconfsimulator;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Main {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Main.class, args);
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/SwaggerConfig.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/SwaggerConfig.java
new file mode 100644
index 000000000..2e9df997e
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/SwaggerConfig.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@EnableSwagger2
+@Configuration
+class SwaggerConfig {
+
+ @Bean
+ Docket api() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .select()
+ .apis(RequestHandlerSelectors.basePackage("org.onap.netconfsimulator"))
+ .paths(PathSelectors.any())
+ .build();
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/Config.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/Config.java
new file mode 100644
index 000000000..9ae564103
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/Config.java
@@ -0,0 +1,70 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.kafka;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.common.serialization.StringDeserializer;
+import org.onap.netconfsimulator.kafka.listener.KafkaListenerHandler;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.kafka.annotation.EnableKafka;
+import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
+import org.springframework.kafka.core.ConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
+
+@Configuration
+@EnableKafka
+class Config {
+
+ @Value("${spring.kafka.bootstrap-servers}")
+ private String bootstrapServer;
+
+ @Value("${spring.kafka.consumer.auto-offset-reset}")
+ private String offsetReset;
+
+ @Bean
+ ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(ConsumerFactory<String, String> consumerFactory) {
+ ConcurrentKafkaListenerContainerFactory<String, String> containerFactory = new ConcurrentKafkaListenerContainerFactory<>();
+ containerFactory.setConsumerFactory(consumerFactory);
+ return containerFactory;
+ }
+
+ @Bean
+ ConsumerFactory<String, String> consumerFactory() {
+ Map<String, Object> props = new HashMap<>();
+ props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServer);
+ props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
+ props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
+ props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, offsetReset);
+ return new DefaultKafkaConsumerFactory<>(props);
+ }
+
+
+ @Bean
+ KafkaListenerHandler kafkaListenerHandler(ConsumerFactory<String, String> consumerFactory) {
+ return new KafkaListenerHandler(consumerFactory);
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/MessageDTO.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/MessageDTO.java
new file mode 100644
index 000000000..4311cd61f
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/MessageDTO.java
@@ -0,0 +1,31 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.kafka;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+class MessageDTO {
+ private long timestamp;
+ private String configuration;
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreController.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreController.java
new file mode 100644
index 000000000..33bbdf7cf
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreController.java
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.kafka;
+
+import java.util.List;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@Slf4j
+@RequestMapping("/store")
+public class StoreController {
+
+ private StoreService service;
+
+ @Autowired
+ public StoreController(StoreService service) {
+ this.service = service;
+ }
+
+ @GetMapping("/ping")
+ String ping() {
+ return "pong";
+ }
+
+ @GetMapping("cm-history")
+ List<MessageDTO> getAllConfigurationChanges() {
+ return service.getAllMessages();
+ }
+
+ @GetMapping("/less")
+ List<MessageDTO> less(@RequestParam(value = "offset", required = false, defaultValue = "${spring.kafka.default-offset}") long offset) {
+ return service.getLastMessages(offset);
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreService.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreService.java
new file mode 100644
index 000000000..5fddff5a2
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreService.java
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.kafka;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.consumer.Consumer;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.apache.kafka.common.TopicPartition;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.kafka.core.ConsumerFactory;
+import org.springframework.stereotype.Service;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@Slf4j
+@Service
+public class StoreService {
+
+ private static final String CONFIG_TOPIC = "config";
+ private static final long CONSUMING_DURATION_IN_MS = 1000;
+
+ private ConsumerFactory<String, String> consumerFactory;
+ static final List<String> TOPICS_TO_SUBSCRIBE = Collections.singletonList(CONFIG_TOPIC);
+
+ @Autowired
+ StoreService(ConsumerFactory<String, String> consumerFactory) {
+ this.consumerFactory = consumerFactory;
+ }
+
+ List<MessageDTO> getAllMessages() {
+ List<MessageDTO> messages = new ArrayList<>();
+ String clientID = Long.toString(Instant.now().getEpochSecond());
+ try (Consumer<String, String> consumer = consumerFactory.createConsumer(clientID, clientID)) {
+ consumer.subscribe(TOPICS_TO_SUBSCRIBE);
+ ConsumerRecords<String, String> consumerRecords = consumer.poll(CONSUMING_DURATION_IN_MS);
+ consumerRecords.forEach(
+ consumerRecord ->
+ messages.add(new MessageDTO(consumerRecord.timestamp(), consumerRecord.value())));
+ log.debug(String.format("consumed %d messages", consumerRecords.count()));
+ }
+ return messages;
+ }
+
+ List<MessageDTO> getLastMessages(long offset) {
+ List<MessageDTO> messages = new ArrayList<>();
+ try (Consumer<String, String> consumer = createConsumer(offset)) {
+ ConsumerRecords<String, String> consumerRecords = consumer.poll(CONSUMING_DURATION_IN_MS);
+ consumerRecords.forEach(consumerRecord ->
+ messages.add(new MessageDTO(consumerRecord.timestamp(), consumerRecord.value())));
+ }
+ return messages;
+ }
+
+ private Consumer<String, String> createConsumer(long offsetFromLastIndex) {
+ String clientID = Long.toString(Instant.now().getEpochSecond());
+ Consumer<String, String> consumer = consumerFactory.createConsumer(clientID, clientID);
+ consumer.subscribe(TOPICS_TO_SUBSCRIBE);
+ seekConsumerTo(consumer, offsetFromLastIndex);
+ return consumer;
+ }
+
+ private void seekConsumerTo(Consumer<String, String> consumer, long offsetFromLastIndex) {
+ consumer.seekToEnd(consumer.assignment());
+ consumer.poll(CONSUMING_DURATION_IN_MS);
+ TopicPartition topicPartition = consumer.assignment().iterator().next();
+ long topicCurrentSize = consumer.position(topicPartition);
+ long indexToSeek = offsetFromLastIndex > topicCurrentSize ? 0 : topicCurrentSize - offsetFromLastIndex;
+ consumer.seek(topicPartition, indexToSeek);
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerEntry.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerEntry.java
new file mode 100644
index 000000000..e3c04c9fc
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerEntry.java
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.kafka.listener;
+
+import lombok.Getter;
+import org.springframework.kafka.listener.AbstractMessageListenerContainer;
+
+@Getter
+public class KafkaListenerEntry {
+
+ private String clientId;
+ private AbstractMessageListenerContainer listenerContainer;
+
+ public KafkaListenerEntry(String clientId, AbstractMessageListenerContainer listenerContainer) {
+ this.clientId = clientId;
+ this.listenerContainer = listenerContainer;
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandler.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandler.java
new file mode 100644
index 000000000..604315d5f
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandler.java
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.kafka.listener;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.kafka.core.ConsumerFactory;
+
+import org.springframework.kafka.listener.ContainerProperties;
+import org.springframework.kafka.listener.KafkaMessageListenerContainer;
+import org.springframework.kafka.listener.MessageListener;
+
+
+import org.springframework.kafka.support.TopicPartitionInitialOffset;
+
+import java.time.Instant;
+
+public class KafkaListenerHandler {
+
+ private static final int PARTITION = 0;
+ private static final long NUMBER_OF_HISTORICAL_MESSAGES_TO_SHOW = -10L;
+ private static final boolean RELATIVE_TO_CURRENT = false;
+ private ConsumerFactory<String, String> consumerFactory;
+
+
+ @Autowired
+ public KafkaListenerHandler(ConsumerFactory<String, String> consumerFactory) {
+ this.consumerFactory = consumerFactory;
+ }
+
+
+ public KafkaListenerEntry createKafkaListener(MessageListener messageListener, String topicName) {
+ String clientId = Long.toString(Instant.now().getEpochSecond());
+ ContainerProperties containerProperties = new ContainerProperties(topicName);
+ containerProperties.setGroupId(clientId);
+ KafkaMessageListenerContainer<String, String> listenerContainer = createListenerContainer(containerProperties,
+ topicName);
+
+ listenerContainer.setupMessageListener(messageListener);
+ return new KafkaListenerEntry(clientId, listenerContainer);
+ }
+
+
+ KafkaMessageListenerContainer<String, String> createListenerContainer(ContainerProperties containerProperties,
+ String topicName) {
+ TopicPartitionInitialOffset config = new TopicPartitionInitialOffset(topicName, PARTITION,
+ NUMBER_OF_HISTORICAL_MESSAGES_TO_SHOW, RELATIVE_TO_CURRENT);
+ return new KafkaMessageListenerContainer<>(consumerFactory, containerProperties, config);
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/model/KafkaMessage.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/model/KafkaMessage.java
new file mode 100644
index 000000000..90f283acf
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/model/KafkaMessage.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.kafka.model;
+
+import lombok.Getter;
+
+@Getter
+public class KafkaMessage {
+ private long timestamp;
+ private String configuration;
+
+ public KafkaMessage(long timestamp, String configuration) {
+ this.timestamp = timestamp;
+ this.configuration = configuration;
+ }
+
+ KafkaMessage() {
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/NetconfController.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/NetconfController.java
new file mode 100644
index 000000000..cdb4a8f97
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/NetconfController.java
@@ -0,0 +1,111 @@
+/*
+ * ============LICENSE_START=======================================================
+ * NETCONF-CONTROLLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore;
+
+import com.tailf.jnc.JNCException;
+
+import java.io.IOException;
+
+import lombok.extern.slf4j.Slf4j;
+import org.onap.netconfsimulator.netconfcore.configuration.NetconfConfigurationService;
+import org.onap.netconfsimulator.netconfcore.model.LoadModelResponse;
+import org.onap.netconfsimulator.netconfcore.model.NetconfModelLoaderService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+@Slf4j
+@RestController
+@RequestMapping("netconf")
+class NetconfController {
+
+ private final NetconfConfigurationService netconfService;
+ private final NetconfModelLoaderService netconfModelLoaderService;
+
+ @Autowired
+ NetconfController(NetconfConfigurationService netconfService,
+ NetconfModelLoaderService netconfModelLoaderService) {
+ this.netconfService = netconfService;
+ this.netconfModelLoaderService = netconfModelLoaderService;
+ }
+
+ @GetMapping(value = "get", produces = "application/xml")
+ ResponseEntity<String> getNetconfConfiguration() throws IOException, JNCException {
+ return ResponseEntity.ok(netconfService.getCurrentConfiguration());
+ }
+
+ @GetMapping(value = "get/{model}/{container}", produces = "application/xml")
+ ResponseEntity<String> getNetconfConfiguration(@PathVariable String model,
+ @PathVariable String container)
+ throws IOException {
+ ResponseEntity<String> entity;
+ try {
+ entity = ResponseEntity.ok(netconfService.getCurrentConfiguration(model, container));
+ } catch (JNCException exception) {
+ log.error("Get configuration for model {} and container {} failed.", model, container,
+ exception);
+ entity = ResponseEntity.badRequest().body(exception.toString());
+ }
+ return entity;
+ }
+
+ @PostMapping(value = "edit-config", produces = "application/xml")
+ @ResponseStatus(HttpStatus.ACCEPTED)
+ ResponseEntity<String> editConfig(@RequestPart("editConfigXml") MultipartFile editConfig)
+ throws IOException, JNCException {
+ log.info("Loading updated configuration");
+ if (editConfig == null || editConfig.isEmpty()) {
+ throw new IllegalArgumentException("No XML file with proper name: editConfigXml found.");
+ }
+ return ResponseEntity
+ .status(HttpStatus.ACCEPTED)
+ .body(netconfService.editCurrentConfiguration(editConfig));
+ }
+
+ @PostMapping("model/{moduleName}")
+ ResponseEntity<String> loadNewYangModel(@RequestBody MultipartFile yangModel,
+ @RequestBody MultipartFile initialConfig, @PathVariable String moduleName)
+ throws IOException {
+ LoadModelResponse response = netconfModelLoaderService.loadYangModel(yangModel, initialConfig, moduleName);
+ return ResponseEntity
+ .status(response.getStatusCode())
+ .body(response.getMessage());
+ }
+
+ @DeleteMapping("model/{modelName}")
+ ResponseEntity<String> deleteYangModel(@PathVariable String modelName)
+ throws IOException {
+ LoadModelResponse response = netconfModelLoaderService.deleteYangModel(modelName);
+ return ResponseEntity
+ .status(response.getStatusCode())
+ .body(response.getMessage());
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfBeanConfiguration.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfBeanConfiguration.java
new file mode 100644
index 000000000..d90c60d58
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfBeanConfiguration.java
@@ -0,0 +1,60 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.configuration;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+class NetconfBeanConfiguration {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NetconfBeanConfiguration.class);
+
+ @Value("${netconf.port}")
+ private Integer netconfPort;
+
+ @Value("${netconf.address}")
+ private String netconfAddress;
+
+ @Value("${netconf.user}")
+ private String netconfUser;
+
+ @Value("${netconf.password}")
+ private String netconfPassword;
+
+ @Bean
+ NetconfConfigurationReader configurationReader() {
+ NetconfConnectionParams params = new NetconfConnectionParams(netconfAddress, netconfPort, netconfUser, netconfPassword);
+ LOGGER.info("Configuration params are : {}", params);
+ return new NetconfConfigurationReader(params, new NetconfSessionHelper());
+ }
+
+ @Bean
+ NetconfConfigurationEditor configurationEditor() {
+ NetconfConnectionParams params =
+ new NetconfConnectionParams(netconfAddress, netconfPort, netconfUser, netconfPassword);
+ return new NetconfConfigurationEditor(params, new NetconfSessionHelper());
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditor.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditor.java
new file mode 100644
index 000000000..992c88d5a
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditor.java
@@ -0,0 +1,50 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.configuration;
+
+import com.tailf.jnc.Element;
+import com.tailf.jnc.JNCException;
+import com.tailf.jnc.NetconfSession;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+
+@Slf4j
+public class NetconfConfigurationEditor {
+
+ private NetconfConnectionParams params;
+ private NetconfSessionHelper netconfSessionHelper;
+
+ public NetconfConfigurationEditor(NetconfConnectionParams params, NetconfSessionHelper netconfSessionHelper) {
+ this.params = params;
+ this.netconfSessionHelper = netconfSessionHelper;
+ }
+
+ void editConfig(Element configurationXmlElement) throws JNCException, IOException {
+ log.debug("New configuration passed to simulator: {}", configurationXmlElement.toXMLString());
+ NetconfSession session = netconfSessionHelper.createNetconfSession(params);
+ session.editConfig(configurationXmlElement);
+ session.closeSession();
+
+ log.info("Successfully updated configuration");
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReader.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReader.java
new file mode 100644
index 000000000..10fe40e2f
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReader.java
@@ -0,0 +1,57 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.configuration;
+
+import com.tailf.jnc.JNCException;
+import com.tailf.jnc.NetconfSession;
+import com.tailf.jnc.NodeSet;
+import java.io.IOException;
+import java.util.Objects;
+
+class NetconfConfigurationReader {
+
+ private NetconfConnectionParams params;
+ private NetconfSessionHelper netconfSessionHelper;
+
+ NetconfConfigurationReader(NetconfConnectionParams params, NetconfSessionHelper netconfSessionHelper) {
+ this.params = params;
+ this.netconfSessionHelper = netconfSessionHelper;
+ }
+
+ String getRunningConfig() throws IOException, JNCException {
+ NetconfSession session = netconfSessionHelper.createNetconfSession(params);
+ String config = session.getConfig().toXMLString();
+ session.closeSession();
+ return config;
+ }
+
+ String getRunningConfig(String modelPath) throws IOException, JNCException {
+ NetconfSession session = netconfSessionHelper.createNetconfSession(params);
+ NodeSet config = session.getConfig(modelPath);
+ if (Objects.isNull(config) || Objects.isNull(config.first())) {
+ throw new JNCException(JNCException.ELEMENT_MISSING, modelPath);
+ }
+ session.closeSession();
+ return config.first().toXMLString();
+ }
+
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationService.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationService.java
new file mode 100644
index 000000000..248aec46a
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationService.java
@@ -0,0 +1,76 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.configuration;
+
+import com.tailf.jnc.Element;
+import com.tailf.jnc.JNCException;
+import com.tailf.jnc.XMLParser;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+import org.xml.sax.InputSource;
+
+@Service
+public class NetconfConfigurationService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NetconfConfigurationService.class);
+ private static final String CONFIGURATION_HAS_BEEN_ACTIVATED = "New configuration has been activated";
+
+ private final NetconfConfigurationReader netconfConfigurationReader;
+ private NetconfConfigurationEditor configurationEditor;
+ private XMLParser parser;
+
+ @Autowired
+ public NetconfConfigurationService(NetconfConfigurationReader netconfConfigurationReader,
+ NetconfConfigurationEditor netconfConfigurationEditor) throws JNCException {
+ this.netconfConfigurationReader = netconfConfigurationReader;
+ this.configurationEditor = netconfConfigurationEditor;
+ this.parser = new XMLParser();
+ }
+
+ public String getCurrentConfiguration() throws IOException, JNCException {
+ return netconfConfigurationReader.getRunningConfig();
+ }
+
+ public String getCurrentConfiguration(String model, String container) throws IOException, JNCException {
+ String path = String.format("/%s:%s", model, container);
+ return netconfConfigurationReader.getRunningConfig(path);
+ }
+
+ public String editCurrentConfiguration(MultipartFile newConfiguration) throws IOException, JNCException {
+ Element configurationElement = convertMultipartToXmlElement(newConfiguration);
+ configurationEditor.editConfig(configurationElement);
+
+ LOGGER.debug("Loading new configuration: \n{}", configurationElement.toXMLString());
+ return CONFIGURATION_HAS_BEEN_ACTIVATED;
+ }
+
+ private Element convertMultipartToXmlElement(MultipartFile editConfig) throws IOException, JNCException {
+ InputSource inputSourceUpdateConfig = new InputSource(new ByteArrayInputStream(editConfig.getBytes()));
+ return parser.parse(inputSourceUpdateConfig);
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationTO.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationTO.java
new file mode 100644
index 000000000..e43ff690e
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationTO.java
@@ -0,0 +1,32 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.configuration;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class NetconfConfigurationTO {
+
+ private String configuration;
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConnectionParams.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConnectionParams.java
new file mode 100644
index 000000000..ace0ee04c
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConnectionParams.java
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.configuration;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.ToString;
+
+@AllArgsConstructor
+@ToString
+@Getter
+class NetconfConnectionParams {
+
+ private final String address;
+ private final int port;
+ private final String user;
+ private final String password;
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfSessionHelper.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfSessionHelper.java
new file mode 100644
index 000000000..69fda7d63
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfSessionHelper.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.configuration;
+
+import com.tailf.jnc.JNCException;
+import com.tailf.jnc.NetconfSession;
+import com.tailf.jnc.SSHConnection;
+import com.tailf.jnc.SSHSession;
+import java.io.IOException;
+
+class NetconfSessionHelper {
+
+ NetconfSession createNetconfSession(NetconfConnectionParams params) throws IOException, JNCException {
+ SSHConnection sshConnection = new SSHConnection(params.getAddress(), params.getPort());
+ sshConnection.authenticateWithPassword(params.getUser(), params.getPassword());
+ return new NetconfSession(new SSHSession(sshConnection));
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/LoadModelResponse.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/LoadModelResponse.java
new file mode 100644
index 000000000..a6e292f62
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/LoadModelResponse.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.model;
+
+public class LoadModelResponse {
+
+ private Integer statusCode;
+ private String message;
+
+ public LoadModelResponse(Integer statusCode, String message) {
+ this.statusCode = statusCode;
+ this.message = message;
+ }
+
+ public Integer getStatusCode() {
+ return this.statusCode;
+ }
+
+ public String getMessage() {
+ return this.message;
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderService.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderService.java
new file mode 100644
index 000000000..7e0739579
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderService.java
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.model;
+
+import java.io.IOException;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+@Service
+public class NetconfModelLoaderService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NetconfModelLoaderService.class);
+
+ @Value("${netconf.address}")
+ private String netconfIp;
+
+ @Value("${netconf.model-loader.port}")
+ private String modelLoaderPort;
+
+ private final HttpClient httpClient;
+
+ @Autowired
+ public NetconfModelLoaderService(HttpClient httpClient) {
+ this.httpClient = httpClient;
+ }
+
+ public LoadModelResponse deleteYangModel(String yangModelName) throws IOException {
+ String uri = getDeleteAddress(yangModelName);
+ HttpDelete httpDelete = new HttpDelete(uri);
+ HttpResponse httpResponse = httpClient.execute(httpDelete);
+ return parseResponse(httpResponse);
+ }
+
+ public LoadModelResponse loadYangModel(MultipartFile yangModel, MultipartFile initialConfig, String moduleName)
+ throws IOException {
+ HttpPost httpPost = new HttpPost(getBackendAddress());
+ HttpEntity httpEntity = MultipartEntityBuilder.create()
+ .addBinaryBody("yangModel", yangModel.getInputStream(), ContentType.MULTIPART_FORM_DATA,
+ yangModel.getOriginalFilename())
+ .addBinaryBody("initialConfig", initialConfig.getInputStream(), ContentType.MULTIPART_FORM_DATA,
+ initialConfig.getOriginalFilename())
+ .addTextBody("moduleName", moduleName)
+ .build();
+ httpPost.setEntity(httpEntity);
+ HttpResponse response = httpClient.execute(httpPost);
+ return parseResponse(response);
+ }
+
+ String getBackendAddress() {
+ return String.format("http://%s:%s/model", netconfIp, modelLoaderPort);
+ }
+
+ String getDeleteAddress(String yangModelName) {
+ return String.format("%s?yangModelName=%s", getBackendAddress(), yangModelName);
+ }
+
+
+ private LoadModelResponse parseResponse(HttpResponse response) throws IOException {
+ int statusCode = response.getStatusLine().getStatusCode();
+ String responseBody = EntityUtils.toString(response.getEntity());
+
+ logResponse(statusCode, responseBody);
+ return new LoadModelResponse(statusCode, responseBody);
+ }
+
+ private void logResponse(int statusCode, String responseBody) {
+ if (statusCode >= HttpStatus.BAD_REQUEST.value()) {
+ LOGGER.error(responseBody);
+ } else {
+ LOGGER.info(responseBody);
+ }
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/EndpointConfig.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/EndpointConfig.java
new file mode 100644
index 000000000..4eaa85010
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/EndpointConfig.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.websocket;
+
+import java.util.Collections;
+import org.onap.netconfsimulator.websocket.message.NetconfMessageEncoder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+import org.springframework.web.socket.server.standard.ServerEndpointRegistration;
+
+@Configuration
+class EndpointConfig {
+
+ @Bean
+ ServerEndpointRegistration endpointRegistration() {
+ ServerEndpointRegistration serverEndpointRegistration = new ServerEndpointRegistration("/netconf",
+ NetconfEndpoint.class);
+ serverEndpointRegistration.setEncoders(Collections.singletonList(NetconfMessageEncoder.class));
+ return serverEndpointRegistration;
+ }
+
+ @Bean
+ ServerEndpointExporter endpointExporter() {
+ return new ServerEndpointExporter();
+ }
+}
+
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/NetconfEndpoint.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/NetconfEndpoint.java
new file mode 100644
index 000000000..5870ee1e4
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/NetconfEndpoint.java
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.websocket;
+
+
+import java.util.Optional;
+import javax.websocket.CloseReason;
+import javax.websocket.Endpoint;
+import javax.websocket.EndpointConfig;
+import javax.websocket.RemoteEndpoint;
+import javax.websocket.Session;
+
+import org.onap.netconfsimulator.kafka.listener.KafkaListenerEntry;
+import org.onap.netconfsimulator.kafka.listener.KafkaListenerHandler;
+import org.onap.netconfsimulator.websocket.message.NetconfMessageListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.kafka.listener.AbstractMessageListenerContainer;
+import org.springframework.kafka.listener.MessageListener;
+import org.springframework.stereotype.Component;
+
+//instance of this class is created every each websocket request
+@Component
+class NetconfEndpoint extends Endpoint {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NetconfEndpoint.class);
+ private static final String TOPIC_NAME = "config";
+
+ private KafkaListenerHandler kafkaListenerHandler;
+
+ public Optional<KafkaListenerEntry> getEntry() {
+ return entry;
+ }
+
+ public void setEntry(Optional<KafkaListenerEntry> entry) {
+ this.entry = entry;
+ }
+
+ private Optional<KafkaListenerEntry> entry = Optional.empty();
+
+
+ @Autowired
+ NetconfEndpoint(KafkaListenerHandler listenerHandler) {
+ this.kafkaListenerHandler = listenerHandler;
+ }
+
+ @Override
+ public void onOpen(Session session, EndpointConfig endpointConfig) {
+ RemoteEndpoint.Basic basicRemote = session.getBasicRemote();
+
+ addKafkaListener(basicRemote);
+ entry.ifPresent(x -> LOGGER.info("Session with client: {} established", x.getClientId()));
+ }
+
+ @Override
+ public void onError(Session session, Throwable throwable) {
+ LOGGER.error("Unexpected error occurred", throwable);
+ }
+
+ @Override
+ public void onClose(Session session, CloseReason closeReason) {
+ entry.ifPresent(x -> x.getListenerContainer().stop());
+ entry.ifPresent(x -> LOGGER.info("Closing connection for client: {}", x.getClientId()));
+ }
+
+
+ private void addKafkaListener(RemoteEndpoint.Basic remoteEndpoint) {
+ MessageListener messageListener = new NetconfMessageListener(remoteEndpoint);
+
+ KafkaListenerEntry kafkaListener = kafkaListenerHandler.createKafkaListener(messageListener, TOPIC_NAME);
+
+ AbstractMessageListenerContainer listenerContainer = kafkaListener.getListenerContainer();
+ listenerContainer.start();
+ entry = Optional.of(kafkaListener);
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageEncoder.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageEncoder.java
new file mode 100644
index 000000000..349b7e2d9
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageEncoder.java
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.websocket.message;
+
+import org.onap.netconfsimulator.kafka.model.KafkaMessage;
+import org.springframework.web.socket.adapter.standard.ConvertingEncoderDecoderSupport;
+
+public class NetconfMessageEncoder extends ConvertingEncoderDecoderSupport.TextEncoder<KafkaMessage> {
+
+ private static final String MESSAGE_FORMAT = "%s: %s";
+
+ @Override
+ public String encode(KafkaMessage netconfMessage) {
+ return String.format(MESSAGE_FORMAT, netconfMessage.getTimestamp(), netconfMessage.getConfiguration());
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListener.java b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListener.java
new file mode 100644
index 000000000..61610dea0
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListener.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.websocket.message;
+
+import java.io.IOException;
+import javax.websocket.EncodeException;
+import javax.websocket.RemoteEndpoint;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.onap.netconfsimulator.kafka.model.KafkaMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.kafka.listener.MessageListener;
+
+public class NetconfMessageListener implements MessageListener<String, String> {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NetconfMessageListener.class);
+ private RemoteEndpoint.Basic remoteEndpoint;
+
+ public NetconfMessageListener(RemoteEndpoint.Basic remoteEndpoint) {
+ this.remoteEndpoint = remoteEndpoint;
+ }
+
+ @Override
+ public void onMessage(ConsumerRecord<String, String> message) {
+ LOGGER.debug("Attempting to send message to {}", remoteEndpoint);
+ try {
+ remoteEndpoint
+ .sendObject(new KafkaMessage(message.timestamp(), message.value()));
+ } catch (IOException | EncodeException exception) {
+ LOGGER.error("Error during sending message to remote", exception);
+ }
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/main/resources/application.properties b/test/mocks/pnfsimulator/netconfsimulator/src/main/resources/application.properties
new file mode 100644
index 000000000..3947cf358
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/main/resources/application.properties
@@ -0,0 +1,8 @@
+netconf.port=830
+netconf.address=netopeer
+netconf.user=netconf
+netconf.password=netconf
+netconf.model-loader.port=5002
+spring.kafka.bootstrap-servers=kafka1:9092
+spring.kafka.default-offset=100
+spring.kafka.consumer.auto-offset-reset=earliest
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/integration/NetconfFunctionsIT.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/integration/NetconfFunctionsIT.java
new file mode 100644
index 000000000..95ef58696
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/integration/NetconfFunctionsIT.java
@@ -0,0 +1,211 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package integration;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.palantir.docker.compose.connection.DockerMachine;
+import com.palantir.docker.compose.connection.waiting.HealthChecks;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.bitbucket.radistao.test.annotation.BeforeAllMethods;
+import org.bitbucket.radistao.test.runner.BeforeAfterSpringTestRunner;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import com.palantir.docker.compose.DockerComposeRule;
+import org.onap.netconfsimulator.kafka.model.KafkaMessage;
+import org.springframework.http.HttpStatus;
+
+import java.io.IOException;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static junit.framework.TestCase.fail;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(BeforeAfterSpringTestRunner.class)
+public class NetconfFunctionsIT {
+
+ private static NetconfSimulatorClient client;
+ private static ObjectMapper objectMapper;
+
+ private static final DockerMachine dockerMachine = DockerMachine
+ .localMachine()
+ .build();
+
+ private static DockerComposeRule docker = DockerComposeRule.builder()
+ .file("docker-compose.yml")
+ .machine(dockerMachine)
+ .removeConflictingContainersOnStartup(true)
+ .waitingForService("sftp-server", HealthChecks.toHaveAllPortsOpen())
+ .waitingForService("ftpes-server", HealthChecks.toHaveAllPortsOpen())
+ .waitingForService("zookeeper", HealthChecks.toHaveAllPortsOpen())
+ .waitingForService("netopeer", HealthChecks.toHaveAllPortsOpen())
+ .waitingForService("kafka1", HealthChecks.toHaveAllPortsOpen())
+ .waitingForService("netconf-simulator", HealthChecks.toHaveAllPortsOpen())
+ .build();
+
+ @ClassRule
+ public static TestRule exposePortMappings = docker;
+
+ @BeforeClass
+ public static void setUpClass() {
+ objectMapper = new ObjectMapper();
+ client = new NetconfSimulatorClient(String.format("http://%s:%d", docker.containers().ip(), 9000));
+ }
+
+ @BeforeAllMethods
+ public void setupBeforeAll() throws InterruptedException {
+ if (client.isServiceAvailable(Instant.now(), Duration.ofSeconds(45))) {
+ Thread.sleep(60000);
+ return;
+ }
+ fail("Application failed to start within established timeout: 45 seconds. Exiting.");
+ }
+
+ @Before
+ public void setUp() {
+ client.reinitializeClient();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ client.releaseClient();
+ }
+
+ @Test
+ public void testShouldLoadModelEditConfigurationAndDeleteModule() throws IOException {
+ // do load
+ try (CloseableHttpResponse response = client
+ .loadModel("newyangmodel", "newYangModel.yang", "initialConfig.xml")) {
+ assertResponseStatusCode(response, HttpStatus.OK);
+ String original = client.getResponseContentAsString(response);
+ assertThat(original).isEqualTo("\"Successfully started\"\n");
+ }
+ // do edit-config
+ try (CloseableHttpResponse updateResponse = client.updateConfig()) {
+ String afterUpdateConfigContent = client.getResponseContentAsString(updateResponse);
+ assertResponseStatusCode(updateResponse, HttpStatus.ACCEPTED);
+ assertThat(afterUpdateConfigContent).isEqualTo("New configuration has been activated");
+ }
+ // do delete
+ try (CloseableHttpResponse deleteResponse = client.deleteModel("newyangmodel")) {
+ assertResponseStatusCode(deleteResponse, HttpStatus.OK);
+ String original = client.getResponseContentAsString(deleteResponse);
+ assertThat(original).isEqualTo("\"Successfully deleted\"\n");
+ }
+ }
+
+ @Test
+ public void testShouldGetCurrentConfigurationAndEditItSuccessfully() throws IOException {
+ try (CloseableHttpResponse updateResponse = client.updateConfig();
+ CloseableHttpResponse newCurrentConfigResponse = client.getCurrentConfig()) {
+ String afterUpdateConfigContent = client.getResponseContentAsString(updateResponse);
+
+ assertResponseStatusCode(updateResponse, HttpStatus.ACCEPTED);
+ assertResponseStatusCode(newCurrentConfigResponse, HttpStatus.OK);
+
+ assertThat(afterUpdateConfigContent).isEqualTo("New configuration has been activated");
+ }
+ }
+
+ @Test
+ public void testShouldPersistConfigChangesAndGetAllWhenRequested() throws IOException {
+ client.updateConfig();
+
+ try (CloseableHttpResponse newAllConfigChangesResponse = client.getAllConfigChanges()) {
+ String newAllConfigChangesString = client.getResponseContentAsString(newAllConfigChangesResponse);
+ assertResponseStatusCode(newAllConfigChangesResponse, HttpStatus.OK);
+
+ List<KafkaMessage> kafkaMessages = objectMapper
+ .readValue(newAllConfigChangesString, new TypeReference<List<KafkaMessage>>() {
+ });
+
+ assertThat(kafkaMessages.size()).isGreaterThanOrEqualTo(1);
+ Set<String> configChangeContent = kafkaMessages.stream().map(KafkaMessage::getConfiguration)
+ .collect(Collectors.toSet());
+ assertThat(configChangeContent)
+ .anyMatch(el -> el.contains("new value: /pnf-simulator:config/itemValue1 = 100"));
+ assertThat(configChangeContent)
+ .anyMatch(el -> el.contains("new value: /pnf-simulator:config/itemValue2 = 200"));
+ }
+ }
+
+ @Test
+ public void testShouldGetLastMessage() throws IOException {
+ client.updateConfig();
+
+ try (CloseableHttpResponse lastConfigChangesResponse = client.getLastConfigChanges(2)) {
+ String newAllConfigChangesString = client.getResponseContentAsString(lastConfigChangesResponse);
+ List<KafkaMessage> kafkaMessages = objectMapper
+ .readValue(newAllConfigChangesString, new TypeReference<List<KafkaMessage>>() {
+ });
+
+ assertThat(kafkaMessages).hasSize(2);
+ assertThat(kafkaMessages.get(0).getConfiguration())
+ .contains("new value: /pnf-simulator:config/itemValue1 = 100");
+ assertThat(kafkaMessages.get(1).getConfiguration())
+ .contains("new value: /pnf-simulator:config/itemValue2 = 200");
+ }
+ }
+
+ @Test
+ public void testShouldLoadNewYangModelAndReconfigure() throws IOException {
+ try (CloseableHttpResponse response = client
+ .loadModel("newyangmodel", "newYangModel.yang", "initialConfig.xml")) {
+ assertResponseStatusCode(response, HttpStatus.OK);
+
+ String original = client.getResponseContentAsString(response);
+
+ assertThat(original).isEqualTo("\"Successfully started\"\n");
+ }
+ }
+
+ @Test
+ public void shouldGetLoadedModelByName() throws IOException {
+ testShouldLoadNewYangModelAndReconfigure();
+
+ try (CloseableHttpResponse response = client.getConfigByModelAndContainerNames("newyangmodel", "config2")) {
+ assertResponseStatusCode(response, HttpStatus.OK);
+ String config = client.getResponseContentAsString(response);
+
+ assertThat(config).isEqualTo(
+ "<config2 xmlns=\"http://onap.org/newyangmodel\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ + " <item1>100</item1>\n"
+ + "</config2>\n");
+ }
+
+ }
+
+ private void assertResponseStatusCode(HttpResponse response, HttpStatus expectedStatus) {
+ assertThat(response.getStatusLine().getStatusCode()).isEqualTo(expectedStatus.value());
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/integration/NetconfSimulatorClient.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/integration/NetconfSimulatorClient.java
new file mode 100644
index 000000000..61f2ef1d8
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/integration/NetconfSimulatorClient.java
@@ -0,0 +1,150 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package integration;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.junit.platform.commons.logging.Logger;
+import org.junit.platform.commons.logging.LoggerFactory;
+import org.springframework.util.ResourceUtils;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+
+class NetconfSimulatorClient {
+
+ private CloseableHttpClient netconfClient;
+ private String simulatorBaseUrl;
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfSimulatorClient.class);
+
+ NetconfSimulatorClient(String simulatorBaseUrl) {
+ this.netconfClient = HttpClients.createDefault();
+ this.simulatorBaseUrl = simulatorBaseUrl;
+ }
+
+ CloseableHttpResponse loadModel(String moduleName, String yangModelFileName, String initialiConfigFileName) throws IOException {
+ String updateConfigUrl = String.format("%s/netconf/model/%s", simulatorBaseUrl, moduleName);
+ HttpPost httpPost = new HttpPost(updateConfigUrl);
+ HttpEntity updatedConfig = MultipartEntityBuilder
+ .create()
+ .addBinaryBody("yangModel", ResourceUtils.getFile(String.format("classpath:%s", yangModelFileName)))
+ .addBinaryBody("initialConfig", ResourceUtils.getFile(String.format("classpath:%s",initialiConfigFileName)))
+ .addTextBody("moduleName", moduleName)
+ .build();
+ httpPost.setEntity(updatedConfig);
+ return netconfClient.execute(httpPost);
+ }
+
+ CloseableHttpResponse deleteModel(String moduleName) throws IOException {
+ String deleteModuleUrl = String.format("%s/netconf/model/%s", simulatorBaseUrl, moduleName);
+ HttpDelete httpDelete = new HttpDelete(deleteModuleUrl);
+ return netconfClient.execute(httpDelete);
+ }
+
+ boolean isServiceAvailable(Instant startTime, Duration maxWaitingDuration) throws InterruptedException {
+ boolean isServiceReady = false;
+ while (Duration.between(startTime, Instant.now()).compareTo(maxWaitingDuration) < 1){
+ if(checkIfSimResponds()){
+ return true;
+ }
+ else {
+ LOG.info(() -> "Simulator not ready yet, retrying in 5s...");
+ Thread.sleep(5000);
+ }
+ }
+ return isServiceReady;
+ }
+
+ private boolean checkIfSimResponds() throws InterruptedException {
+ try(CloseableHttpResponse pingResponse = getCurrentConfig()){
+ String responseString = getResponseContentAsString(pingResponse);
+ if(pingResponse.getStatusLine().getStatusCode() == 200 && !responseString.trim().isEmpty()){
+ return true;
+ }
+ }
+ catch(IOException ex){
+ LOG.error(ex, () -> "EXCEPTION");
+ Thread.sleep(5000);
+ }
+ return false;
+ }
+
+ CloseableHttpResponse getCurrentConfig() throws IOException {
+ String netconfAddress = String.format("%s/netconf/get", simulatorBaseUrl);
+ HttpGet get = new HttpGet(netconfAddress);
+ return netconfClient.execute(get);
+ }
+
+ CloseableHttpResponse getConfigByModelAndContainerNames(String model, String container) throws IOException {
+ String netconfAddress = String
+ .format("%s/netconf/get/%s/%s", simulatorBaseUrl, model, container);
+ HttpGet get = new HttpGet(netconfAddress);
+ return netconfClient.execute(get);
+ }
+
+ CloseableHttpResponse updateConfig() throws IOException {
+ String updateConfigUrl = String.format("%s/netconf/edit-config", simulatorBaseUrl);
+ HttpPost httpPost = new HttpPost(updateConfigUrl);
+ HttpEntity updatedConfig = MultipartEntityBuilder
+ .create()
+ .addBinaryBody("editConfigXml", ResourceUtils.getFile("classpath:updatedConfig.xml"))
+ .build();
+ httpPost.setEntity(updatedConfig);
+ return netconfClient.execute(httpPost);
+ }
+
+ CloseableHttpResponse getAllConfigChanges() throws IOException {
+ String netconfStoreCmHistoryAddress = String.format("%s/store/cm-history", simulatorBaseUrl);
+ HttpGet configurationChangesResponse = new HttpGet(netconfStoreCmHistoryAddress);
+ return netconfClient.execute(configurationChangesResponse);
+ }
+
+ CloseableHttpResponse getLastConfigChanges(int howManyLastChanges) throws IOException {
+ String netconfStoreCmHistoryAddress = String.format("%s/store/less?offset=%d", simulatorBaseUrl, howManyLastChanges);
+ HttpGet configurationChangesResponse = new HttpGet(netconfStoreCmHistoryAddress);
+ return netconfClient.execute(configurationChangesResponse);
+ }
+
+ void releaseClient() throws IOException {
+ netconfClient.close();
+ }
+
+ void reinitializeClient(){
+ netconfClient = HttpClients.createDefault();
+ }
+
+ String getResponseContentAsString(HttpResponse response) throws IOException {
+ HttpEntity entity = response.getEntity();
+ String entityStringRepr = EntityUtils.toString(entity);
+ EntityUtils.consume(entity);
+ return entityStringRepr;
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/EmbeddedKafkaConfig.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/EmbeddedKafkaConfig.java
new file mode 100644
index 000000000..5ddf2b2a6
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/EmbeddedKafkaConfig.java
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.kafka;
+
+
+import java.util.Map;
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
+import org.springframework.kafka.core.ConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
+import org.springframework.kafka.core.DefaultKafkaProducerFactory;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.kafka.core.ProducerFactory;
+import org.springframework.kafka.test.utils.KafkaTestUtils;
+
+import static org.onap.netconfsimulator.kafka.StoreServiceTest.embeddedKafka;
+
+@Configuration
+class EmbeddedKafkaConfig {
+
+ @Bean
+ KafkaTemplate<String, String> kafkaTemplate(){
+ return new KafkaTemplate<>(producerFactory());
+ }
+
+ @Bean
+ @Autowired
+ ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(ConsumerFactory<String, String> consumerFactory){
+ ConcurrentKafkaListenerContainerFactory<String, String> containerFactory = new ConcurrentKafkaListenerContainerFactory<>();
+ containerFactory.setConsumerFactory(consumerFactory);
+ return containerFactory;
+ }
+
+ @Bean
+ ConsumerFactory<String, String> consumerFactory(){
+ Map<String, Object> consumerProperties =
+ KafkaTestUtils.consumerProps("sender", "false", embeddedKafka.getEmbeddedKafka());
+ consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
+ return new DefaultKafkaConsumerFactory<>(consumerProperties);
+ }
+
+ private ProducerFactory<String, String> producerFactory() {
+ Map<String, Object> senderProperties =
+ KafkaTestUtils.senderProps(embeddedKafka.getEmbeddedKafka().getBrokersAsString());
+ return new DefaultKafkaProducerFactory<>(senderProperties);
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreControllerTest.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreControllerTest.java
new file mode 100644
index 000000000..02eec12ac
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreControllerTest.java
@@ -0,0 +1,86 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.kafka;
+
+import java.time.Instant;
+import java.util.List;
+import org.assertj.core.api.Assertions;
+import org.assertj.core.util.Lists;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.mockito.Mockito.when;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+public class StoreControllerTest {
+
+ private static final String MESSAGE_3 = "message 3";
+ private static final String MESSAGE_2 = "message 2";
+ private static final String MESSAGE_1 = "message 1";
+
+ private static final List<MessageDTO> ALL_MESSAGES = Lists.newArrayList(new MessageDTO(Instant.now().getEpochSecond(), MESSAGE_1),
+ new MessageDTO(Instant.now().getEpochSecond(), MESSAGE_2),
+ new MessageDTO(Instant.now().getEpochSecond(), MESSAGE_3));
+
+ @Mock
+ private StoreService service;
+
+ @InjectMocks
+ private StoreController storeController;
+
+
+ @Test
+ public void lessShouldTakeAllMessagesTest() {
+ when(service.getLastMessages(3)).thenReturn(ALL_MESSAGES);
+
+ List<MessageDTO> lessResponse = storeController.less(3);
+
+ assertResponseContainsExpectedMessages(lessResponse, 3, MESSAGE_1, MESSAGE_2, MESSAGE_3);
+ }
+
+ @Test
+ public void lessShouldTakeTwoMessagesTest() {
+ when(service.getLastMessages(2)).thenReturn(Lists.newArrayList(new MessageDTO(Instant.now().getEpochSecond(), MESSAGE_1)));
+
+ List<MessageDTO> lessResult = storeController.less(2);
+
+ assertResponseContainsExpectedMessages(lessResult, 1, MESSAGE_1);
+ }
+
+ @Test
+ public void shouldGetAllMessages(){
+ when(service.getAllMessages()).thenReturn(ALL_MESSAGES);
+
+ List<MessageDTO> allMsgResult = storeController.getAllConfigurationChanges();
+
+ assertResponseContainsExpectedMessages(allMsgResult, 3, MESSAGE_1, MESSAGE_2, MESSAGE_3);
+ }
+
+ private void assertResponseContainsExpectedMessages(List<MessageDTO> actualMessages, int expectedMessageCount, String... expectedMessages){
+ Assertions.assertThat(actualMessages.stream().map(MessageDTO::getConfiguration))
+ .hasSize(expectedMessageCount)
+ .containsExactly(expectedMessages);
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreServiceTest.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreServiceTest.java
new file mode 100644
index 000000000..fd36116a8
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreServiceTest.java
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.kafka;
+
+import org.bitbucket.radistao.test.annotation.BeforeAllMethods;
+import org.bitbucket.radistao.test.runner.BeforeAfterSpringTestRunner;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.kafka.test.context.EmbeddedKafka;
+import org.springframework.kafka.test.rule.EmbeddedKafkaRule;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(BeforeAfterSpringTestRunner.class)
+@SpringBootTest(classes = {StoreService.class, EmbeddedKafkaConfig.class})
+@EmbeddedKafka
+public class StoreServiceTest {
+
+ private static final String MESSAGE_1 = "message1";
+ private static final String MESSAGE_2 = "message2";
+ private static final String MESSAGE_3 = "message3";
+
+ @ClassRule
+ public static EmbeddedKafkaRule embeddedKafka = new EmbeddedKafkaRule(1, true, 1, "config");
+
+ @Autowired
+ StoreService service;
+
+ @Autowired
+ KafkaTemplate<String, String> kafkaTemplate;
+
+ @BeforeAllMethods
+ public void setupBeforeAll() {
+ prepareProducer();
+ }
+
+ @Test
+ public void testShouldReturnAllAvailableMessages(){
+
+ List<MessageDTO> actualMessages = service.getAllMessages();
+
+ assertResponseContainsExpectedMessages(actualMessages, 3, MESSAGE_1, MESSAGE_2, MESSAGE_3);
+ }
+
+ @Test
+ public void testShouldGetLastMessagesRespectingOffset(){
+
+ List<MessageDTO> wantedLastMsg = service.getLastMessages(1L);
+
+ assertResponseContainsExpectedMessages(wantedLastMsg, 1, MESSAGE_3);
+ }
+
+ @Test
+ public void testShouldGetAll3Messages() {
+ List<MessageDTO> wantedLastMsgs = service.getLastMessages(3L);
+
+ assertResponseContainsExpectedMessages(wantedLastMsgs, 3, MESSAGE_1, MESSAGE_2, MESSAGE_3);
+ }
+
+ private void prepareProducer(){
+ kafkaTemplate.send("config", "message1");
+ kafkaTemplate.send("config", "message2");
+ kafkaTemplate.send("config", "message3");
+ }
+
+ private void assertResponseContainsExpectedMessages(List<MessageDTO> actualMessages, int expectedMessageCount, String... expectedMessages){
+ assertThat(actualMessages.stream().map(MessageDTO::getConfiguration))
+ .hasSize(expectedMessageCount)
+ .containsExactly(expectedMessages);
+ }
+
+}
+
+
+
+
+
+
+
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandlerTest.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandlerTest.java
new file mode 100644
index 000000000..fcb72666a
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandlerTest.java
@@ -0,0 +1,87 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.kafka.listener;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.springframework.kafka.core.ConsumerFactory;
+import org.springframework.kafka.listener.ContainerProperties;
+import org.springframework.kafka.listener.KafkaMessageListenerContainer;
+import org.springframework.kafka.listener.MessageListener;
+
+class KafkaListenerHandlerTest {
+
+ private static final String CLIENT_ID_REGEX = "[0-9]{10,}";
+ private static final String SAMPLE_TOPIC = "sampleTopic";
+
+ @Mock
+ private ConsumerFactory<String, String> consumerFactory;
+
+ @Mock
+ private KafkaMessageListenerContainer<String, String> kafkaMessageListenerContainer;
+
+ @Mock
+ private MessageListener messageListener;
+
+ @BeforeEach
+ void setUp() {
+ initMocks(this);
+ }
+
+
+ @Test
+ void shouldProperlyCreateKafkaListener() {
+ KafkaListenerHandler kafkaListenerHandler = spy(new KafkaListenerHandler(consumerFactory));
+ doReturn(kafkaMessageListenerContainer).when(kafkaListenerHandler)
+ .createListenerContainer(any(ContainerProperties.class), eq(SAMPLE_TOPIC));
+
+ KafkaListenerEntry kafkaListenerEntry = kafkaListenerHandler
+ .createKafkaListener(messageListener, SAMPLE_TOPIC);
+
+ assertThat(kafkaListenerEntry.getListenerContainer()).isEqualTo(kafkaMessageListenerContainer);
+ assertThat(kafkaListenerEntry.getClientId()).matches(CLIENT_ID_REGEX);
+ }
+
+ @Test
+ void shouldProperlyCreateContainer() {
+ KafkaListenerHandler kafkaListenerHandler = spy(new KafkaListenerHandler(consumerFactory));
+ ContainerProperties containerProperties = new ContainerProperties(SAMPLE_TOPIC);
+ containerProperties.setMessageListener(mock(MessageListener.class));
+
+ KafkaMessageListenerContainer<String, String> listenerContainer = kafkaListenerHandler
+ .createListenerContainer(containerProperties, SAMPLE_TOPIC);
+
+ ContainerProperties actualProperties = listenerContainer.getContainerProperties();
+ assertThat(actualProperties.getTopics()).isEqualTo(containerProperties.getTopics());
+ assertThat(actualProperties.getMessageListener()).isEqualTo(containerProperties.getMessageListener());
+ }
+
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/NetconfControllerTest.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/NetconfControllerTest.java
new file mode 100644
index 000000000..73fb627ea
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/NetconfControllerTest.java
@@ -0,0 +1,172 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import com.tailf.jnc.JNCException;
+import java.io.IOException;
+import java.nio.file.Files;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.onap.netconfsimulator.netconfcore.configuration.NetconfConfigurationService;
+import org.onap.netconfsimulator.netconfcore.model.LoadModelResponse;
+import org.onap.netconfsimulator.netconfcore.model.NetconfModelLoaderService;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.util.ResourceUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+class NetconfControllerTest {
+
+ private MockMvc mockMvc;
+
+ @Mock
+ private NetconfConfigurationService netconfService;
+
+ @Mock
+ private NetconfModelLoaderService netconfModelLoaderService;
+
+ @InjectMocks
+ private NetconfController controller;
+
+ private static final String SAMPLE_CONFIGURATION = "<config xmlns=\"http://onap.org/pnf-simulator\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><itemValue1>11</itemValue1><itemValue2>22</itemValue2></config>";
+
+ @BeforeEach
+ void setUp() {
+ initMocks(this);
+ mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
+ }
+
+ @Test
+ void testShouldDigestMultipartFile() throws Exception {
+ byte[] bytes =
+ Files.readAllBytes(ResourceUtils.getFile("classpath:updatedConfig.xml").toPath());
+ MockMultipartFile file = new MockMultipartFile("editConfigXml", bytes);
+
+ mockMvc
+ .perform(MockMvcRequestBuilders.multipart("/netconf/edit-config").file(file))
+ .andExpect(status().isAccepted());
+
+ verify(netconfService).editCurrentConfiguration(any(MultipartFile.class));
+ }
+
+ @Test
+ void testShouldThrowExceptionWhenEditConfigFileWithIncorrectNameProvided() throws Exception {
+ MockMultipartFile file = new MockMultipartFile("wrongName", new byte[0]);
+
+ mockMvc
+ .perform(MockMvcRequestBuilders.multipart("/netconf/edit-config").file(file))
+ .andExpect(status().isBadRequest());
+
+ verify(netconfService, never()).editCurrentConfiguration(any(MultipartFile.class));
+ }
+
+ @Test
+ void testShouldReturnCurrentConfiguration() throws Exception {
+ when(netconfService.getCurrentConfiguration()).thenReturn(SAMPLE_CONFIGURATION);
+
+ String contentAsString =
+ mockMvc
+ .perform(get("/netconf/get"))
+ .andExpect(status().isOk())
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ verify(netconfService).getCurrentConfiguration();
+ assertThat(contentAsString).isEqualTo(SAMPLE_CONFIGURATION);
+ }
+
+ @Test
+ void testShouldReturnConfigurationForGivenPath() throws Exception {
+ when(netconfService.getCurrentConfiguration("sampleModel", "sampleContainer"))
+ .thenReturn(SAMPLE_CONFIGURATION);
+
+ String contentAsString =
+ mockMvc
+ .perform(get("/netconf/get/sampleModel/sampleContainer"))
+ .andExpect(status().isOk())
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ verify(netconfService).getCurrentConfiguration("sampleModel", "sampleContainer");
+ assertThat(contentAsString).isEqualTo(SAMPLE_CONFIGURATION);
+ }
+
+ @Test
+ void testShouldRaiseBadRequestWhenConfigurationIsNotPresent() throws Exception {
+ when(netconfService.getCurrentConfiguration("sampleModel", "sampleContainer2"))
+ .thenThrow(new JNCException(JNCException.ELEMENT_MISSING, "/sampleModel:sampleContainer2"));
+
+ String contentAsString =
+ mockMvc
+ .perform(get("/netconf/get/sampleModel/sampleContainer2"))
+ .andExpect(status().isBadRequest())
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ assertThat(contentAsString).isEqualTo("Element does not exists: /sampleModel:sampleContainer2");
+ }
+
+ @Test
+ void shouldThrowExceptionWhenNoConfigurationPresent() throws IOException, JNCException {
+ when(netconfService.getCurrentConfiguration()).thenThrow(JNCException.class);
+
+ assertThatThrownBy(() -> mockMvc.perform(get("/netconf/get")))
+ .hasRootCauseExactlyInstanceOf(JNCException.class);
+ }
+
+ @Test
+ void testShouldDeleteYangModel() throws Exception {
+ String responseOkString = "Alles klar";
+ String yangModelName = "someModel";
+ LoadModelResponse loadModelResponse = new LoadModelResponse(200, responseOkString);
+ String uri = String.format("/netconf/model/%s", yangModelName);
+ when(netconfModelLoaderService.deleteYangModel(yangModelName)).thenReturn(loadModelResponse);
+
+ String contentAsString =
+ mockMvc
+ .perform(delete(uri))
+ .andExpect(status().isOk())
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ verify(netconfModelLoaderService).deleteYangModel(yangModelName);
+ assertThat(contentAsString).isEqualTo(responseOkString);
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditorTest.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditorTest.java
new file mode 100644
index 000000000..371bdd84b
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditorTest.java
@@ -0,0 +1,69 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.configuration;
+
+import com.tailf.jnc.Element;
+import com.tailf.jnc.JNCException;
+import com.tailf.jnc.NetconfSession;
+import com.tailf.jnc.XMLParser;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.onap.netconfsimulator.netconfcore.configuration.NetconfConfigurationEditor;
+import org.springframework.util.ResourceUtils;
+import org.xml.sax.InputSource;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+class NetconfConfigurationEditorTest {
+
+ @Mock
+ private NetconfSession session;
+ @Mock
+ private NetconfSessionHelper netconfSessionHelper;
+
+ private NetconfConfigurationEditor editor;
+
+ @BeforeEach
+ void setUp() throws IOException, JNCException {
+ initMocks(this);
+ NetconfConnectionParams params = null;
+ Mockito.when(netconfSessionHelper.createNetconfSession(params)).thenReturn(session);
+ editor = new NetconfConfigurationEditor(params, netconfSessionHelper);
+ }
+
+ @Test
+ void testShouldEditConfigSuccessfully() throws IOException, JNCException {
+ byte[] bytes =
+ Files.readAllBytes(ResourceUtils.getFile("classpath:updatedConfig.xml").toPath());
+ Element editConfigXml = new XMLParser().parse(new InputSource(new ByteArrayInputStream(bytes)));
+
+ editor.editConfig(editConfigXml);
+
+ verify(session).editConfig(editConfigXml);
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReaderTest.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReaderTest.java
new file mode 100644
index 000000000..a0a15b993
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReaderTest.java
@@ -0,0 +1,94 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.configuration;
+
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.tailf.jnc.Element;
+import com.tailf.jnc.JNCException;
+import com.tailf.jnc.NetconfSession;
+import com.tailf.jnc.NodeSet;
+import java.io.IOException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+class NetconfConfigurationReaderTest {
+
+ private static final String NETCONF_MODEL_PATH = "";
+ private static final String EXPECTED_STRING_XML = "<?xml version=\"1.0\"?>";
+ private NetconfConfigurationReader reader;
+
+ @Mock
+ private NetconfSession netconfSession;
+
+ @Mock
+ private NetconfSessionHelper netconfSessionHelper;
+
+ @Mock
+ private NodeSet nodeSet;
+
+ @Mock
+ private Element element;
+
+ @BeforeEach
+ void setUp() throws IOException, JNCException {
+ MockitoAnnotations.initMocks(this);
+ NetconfConnectionParams params = null;
+ Mockito.when(netconfSessionHelper.createNetconfSession(params)).thenReturn(netconfSession);
+ reader = new NetconfConfigurationReader(params, netconfSessionHelper);
+ }
+
+ @Test
+ void properlyReadXML() throws IOException, JNCException {
+ when(netconfSession.getConfig()).thenReturn(nodeSet);
+ when(nodeSet.toXMLString()).thenReturn(EXPECTED_STRING_XML);
+
+ String result = reader.getRunningConfig();
+
+ verify(netconfSession).getConfig();
+ verify(nodeSet).toXMLString();
+ assertThat(result).isEqualTo(EXPECTED_STRING_XML);
+ }
+
+ @Test
+ void shouldProperlyReadXmlByName() throws IOException, JNCException {
+ when(netconfSession.getConfig("/sample:test")).thenReturn(nodeSet);
+ when(nodeSet.first()).thenReturn(element);
+ when(element.toXMLString()).thenReturn(EXPECTED_STRING_XML);
+
+ String result = reader.getRunningConfig("/sample:test");
+
+ verify(netconfSession).getConfig("/sample:test");
+ verify(nodeSet, times(2)).first();
+ verify(element).toXMLString();
+
+ assertThat(result).isEqualTo(EXPECTED_STRING_XML);
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationServiceTest.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationServiceTest.java
new file mode 100644
index 000000000..6da65728e
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationServiceTest.java
@@ -0,0 +1,102 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.tailf.jnc.Element;
+import com.tailf.jnc.JNCException;
+import java.io.IOException;
+import java.nio.file.Files;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.util.ResourceUtils;
+
+class NetconfConfigurationServiceTest {
+
+ @Mock
+ NetconfConfigurationReader reader;
+
+ @Mock
+ NetconfConfigurationEditor editor;
+
+ @InjectMocks
+ NetconfConfigurationService service;
+
+ private static String CURRENT_CONFIG_XML_STRING =
+ "<config xmlns=\"http://onap.org/pnf-simulator\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ + " <itemValue1>100</itemValue1>\n"
+ + " <itemValue2>200</itemValue2>\n"
+ + "</config>\n";
+
+ @BeforeEach
+ void setUp() {
+ initMocks(this);
+ }
+
+ @Test
+ void testShouldReturnCorrectCurrentConfiguration() throws IOException, JNCException {
+ String expectedConfiguration = CURRENT_CONFIG_XML_STRING;
+ when(reader.getRunningConfig()).thenReturn(CURRENT_CONFIG_XML_STRING);
+
+ String actualCurrentConfiguration = service.getCurrentConfiguration();
+
+ assertThat(actualCurrentConfiguration).isEqualToIgnoringCase(expectedConfiguration);
+ }
+
+ @Test
+ void testShouldThrowExceptionWhenCurrentConfigurationDoesNotExists() throws IOException, JNCException{
+ when(reader.getRunningConfig()).thenThrow(JNCException.class);
+
+ assertThatThrownBy(() -> service.getCurrentConfiguration()).isInstanceOf(JNCException.class);
+ }
+
+ @Test
+ void testShouldEditConfigurationSuccessfully() throws IOException, JNCException{
+ byte[] bytes =
+ Files.readAllBytes(ResourceUtils.getFile("classpath:updatedConfig.xml").toPath());
+ MockMultipartFile editConfigXmlContent = new MockMultipartFile("editConfigXml", bytes);
+ ArgumentCaptor<Element> elementCaptor = ArgumentCaptor.forClass(Element.class);
+ doNothing().when(editor).editConfig(elementCaptor.capture());
+
+ service.editCurrentConfiguration(editConfigXmlContent);
+
+ assertThat(elementCaptor.getValue().toXMLString()).isEqualTo(CURRENT_CONFIG_XML_STRING);
+ }
+
+ @Test
+ void testShouldRaiseExceptionWhenMultipartFileIsInvalidXmlFile() throws IOException {
+ byte[] bytes =
+ Files.readAllBytes(ResourceUtils.getFile("classpath:invalidXmlFile.xml").toPath());
+ MockMultipartFile editConfigXmlContent = new MockMultipartFile("editConfigXml", bytes);
+
+ assertThatThrownBy(() -> service.editCurrentConfiguration(editConfigXmlContent)).isInstanceOf(JNCException.class);
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderServiceTest.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderServiceTest.java
new file mode 100644
index 000000000..a10876b98
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderServiceTest.java
@@ -0,0 +1,121 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.netconfcore.model;
+
+
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.web.multipart.MultipartFile;
+
+class NetconfModelLoaderServiceTest {
+
+ @Mock
+ private HttpClient httpClient;
+
+ private NetconfModelLoaderService modelLoaderService;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.initMocks(this);
+ modelLoaderService = new NetconfModelLoaderService(httpClient);
+ }
+
+
+ @Test
+ void shouldSendMultipartToServer() throws IOException {
+ //given
+ String loadModelAddress = modelLoaderService.getBackendAddress();
+ makeMockClientReturnStatusOk(httpClient, HttpPost.class);
+ ArgumentCaptor<HttpPost> postArgumentCaptor = ArgumentCaptor.forClass(HttpPost.class);
+ MultipartFile yangMmodel = mock(MultipartFile.class);
+ MultipartFile initialConfig = mock(MultipartFile.class);
+ String moduleName = "moduleName";
+ when(yangMmodel.getInputStream()).thenReturn(getEmptyImputStream());
+ when(initialConfig.getInputStream()).thenReturn(getEmptyImputStream());
+
+ //when
+ LoadModelResponse response = modelLoaderService.loadYangModel(yangMmodel, initialConfig, moduleName);
+
+ //then
+ verify(httpClient).execute(postArgumentCaptor.capture());
+ HttpPost sentPost = postArgumentCaptor.getValue();
+ assertThat(response.getStatusCode()).isEqualTo(200);
+ assertThat(response.getMessage()).isEqualTo("");
+ assertThat(sentPost.getURI().toString()).isEqualTo(loadModelAddress);
+ assertThat(sentPost.getEntity().getContentType().getElements()[0].getName()).isEqualTo("multipart/form-data");
+ }
+
+ @Test
+ void shouldSendDeleteRequestToServer() throws IOException {
+ //given
+ String yangModelName = "sampleModel";
+ String deleteModelAddress = modelLoaderService.getDeleteAddress(yangModelName);
+ makeMockClientReturnStatusOk(httpClient, HttpDelete.class);
+ ArgumentCaptor<HttpDelete> deleteArgumentCaptor = ArgumentCaptor.forClass(HttpDelete.class);
+
+ //when
+ LoadModelResponse response = modelLoaderService.deleteYangModel(yangModelName);
+
+ //then
+ verify(httpClient).execute(deleteArgumentCaptor.capture());
+ HttpDelete sendDelete = deleteArgumentCaptor.getValue();
+ assertThat(response.getStatusCode()).isEqualTo(200);
+ assertThat(response.getMessage()).isEqualTo("");
+ assertThat(sendDelete.getURI().toString()).isEqualTo(deleteModelAddress);
+ }
+
+ private void makeMockClientReturnStatusOk(HttpClient client,
+ Class<? extends HttpRequestBase> httpMethodClass) throws IOException {
+ HttpResponse httpResponse = mock(HttpResponse.class);
+ StatusLine mockStatus = mock(StatusLine.class);
+ HttpEntity mockEntity = mock(HttpEntity.class);
+
+ when(client.execute(any(httpMethodClass))).thenReturn(httpResponse);
+ when(httpResponse.getStatusLine()).thenReturn(mockStatus);
+ when(mockStatus.getStatusCode()).thenReturn(200);
+ when(httpResponse.getEntity()).thenReturn(mockEntity);
+ when(mockEntity.getContent()).thenReturn(getEmptyImputStream());
+ }
+
+ private InputStream getEmptyImputStream() {
+ return new ByteArrayInputStream("".getBytes());
+ }
+
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/NetconfEndpointTest.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/NetconfEndpointTest.java
new file mode 100644
index 000000000..c1484d4b2
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/NetconfEndpointTest.java
@@ -0,0 +1,135 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.websocket;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import java.util.Map;
+import java.util.Optional;
+import javax.websocket.CloseReason;
+import javax.websocket.EndpointConfig;
+import javax.websocket.RemoteEndpoint;
+import javax.websocket.Session;
+import org.apache.kafka.common.Metric;
+import org.apache.kafka.common.MetricName;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.onap.netconfsimulator.kafka.listener.KafkaListenerEntry;
+import org.onap.netconfsimulator.kafka.listener.KafkaListenerHandler;
+import org.onap.netconfsimulator.websocket.message.NetconfMessageListener;
+import org.springframework.kafka.core.ConsumerFactory;
+import org.springframework.kafka.listener.AbstractMessageListenerContainer;
+
+import org.springframework.kafka.listener.ContainerProperties;
+import org.springframework.kafka.listener.GenericMessageListener;
+
+class NetconfEndpointTest {
+
+
+ @Mock
+ private KafkaListenerHandler kafkaListenerHandler;
+
+ @Mock
+ private Session session;
+
+ @Mock
+ private EndpointConfig endpointConfig;
+
+ @Mock
+ private RemoteEndpoint.Basic remoteEndpoint;
+
+
+ @BeforeEach
+ void setUp() {
+ initMocks(this);
+ }
+
+
+ @Test
+ void shouldCreateKafkaListenerWhenClientInitializeConnection() {
+ NetconfEndpoint netconfEndpoint = new NetconfEndpoint(kafkaListenerHandler);
+ AbstractMessageListenerContainer abstractMessageListenerContainer = getListenerContainer();
+ when(session.getBasicRemote()).thenReturn(remoteEndpoint);
+ KafkaListenerEntry kafkaListenerEntry = new KafkaListenerEntry("sampleGroupId",
+ abstractMessageListenerContainer);
+ when(kafkaListenerHandler.createKafkaListener(any(NetconfMessageListener.class), eq("config")))
+ .thenReturn(kafkaListenerEntry);
+
+ netconfEndpoint.onOpen(session, endpointConfig);
+
+ assertThat(netconfEndpoint.getEntry().get().getClientId()).isEqualTo("sampleGroupId");
+ assertThat(netconfEndpoint.getEntry().get().getListenerContainer()).isEqualTo(abstractMessageListenerContainer);
+
+ verify(abstractMessageListenerContainer).start();
+ }
+
+
+ @Test
+ void shouldCloseListenerWhenClientDisconnects() {
+ NetconfEndpoint netconfEndpoint = new NetconfEndpoint(kafkaListenerHandler);
+ AbstractMessageListenerContainer abstractMessageListenerContainer = getListenerContainer();
+ netconfEndpoint.setEntry( Optional.of(new KafkaListenerEntry("sampleGroupId", abstractMessageListenerContainer)) );
+
+ netconfEndpoint.onClose(session, mock(CloseReason.class));
+
+ verify(abstractMessageListenerContainer).stop();
+ }
+
+ class TestAbstractMessageListenerContainer extends AbstractMessageListenerContainer {
+
+
+ TestAbstractMessageListenerContainer(ContainerProperties containerProperties) {
+ super(mock(ConsumerFactory.class),containerProperties);
+ }
+
+ @Override
+ protected void doStart() {
+
+ }
+
+ @Override
+ protected void doStop(Runnable callback) {
+
+ }
+
+ @Override
+ public Map<String, Map<MetricName, ? extends Metric>> metrics() {
+ return null;
+ }
+ }
+
+ private AbstractMessageListenerContainer getListenerContainer() {
+ ContainerProperties containerProperties = new ContainerProperties("config");
+ containerProperties.setGroupId("sample");
+ containerProperties.setMessageListener(mock(GenericMessageListener.class));
+ TestAbstractMessageListenerContainer testAbstractMessageListenerContainer = new TestAbstractMessageListenerContainer(
+ containerProperties);
+ return spy(testAbstractMessageListenerContainer);
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListenerTest.java b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListenerTest.java
new file mode 100644
index 000000000..bb040d1e3
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListenerTest.java
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Simulator
+ * ================================================================================
+ * Copyright (C) 2019 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.netconfsimulator.websocket.message;
+
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import java.io.IOException;
+import javax.websocket.EncodeException;
+import javax.websocket.RemoteEndpoint;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.onap.netconfsimulator.kafka.model.KafkaMessage;
+
+
+class NetconfMessageListenerTest {
+
+ private static final ConsumerRecord<String, String> KAFKA_RECORD = new ConsumerRecord<>("sampleTopic", 0, 0,
+ "sampleKey", "sampleValue");
+
+ @Mock
+ private RemoteEndpoint.Basic remoteEndpoint;
+
+ @InjectMocks
+ private NetconfMessageListener netconfMessageListener;
+
+
+ @BeforeEach
+ void setUp() {
+ initMocks(this);
+ }
+
+
+ @Test
+ void shouldProperlyParseAndSendConsumerRecord() throws IOException, EncodeException {
+ netconfMessageListener.onMessage(KAFKA_RECORD);
+
+ verify(remoteEndpoint).sendObject(any(KafkaMessage.class));
+ }
+
+
+
+ @Test
+ void shouldNotPropagateEncodeException() throws IOException, EncodeException {
+ doThrow(new EncodeException("","")).when(remoteEndpoint).sendObject(any(KafkaMessage.class));
+
+ netconfMessageListener.onMessage(KAFKA_RECORD);
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/application-it.properties b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/application-it.properties
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/application-it.properties
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/initialConfig.xml b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/initialConfig.xml
new file mode 100644
index 000000000..f28a1a0a8
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/initialConfig.xml
@@ -0,0 +1,23 @@
+<!--
+ ============LICENSE_START=======================================================
+ Simulator
+ ================================================================================
+ Copyright (C) 2019 Nokia. All rights reserved.
+ ================================================================================
+ 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.
+ ============LICENSE_END=========================================================
+ -->
+
+<config2 xmlns="http://onap.org/newyangmodel">
+ <item1>100</item1>
+</config2>
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/invalidXmlFile.xml b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/invalidXmlFile.xml
new file mode 100644
index 000000000..3debd8c26
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/invalidXmlFile.xml
@@ -0,0 +1,23 @@
+<!--
+ ============LICENSE_START=======================================================
+ Simulator
+ ================================================================================
+ Copyright (C) 2019 Nokia. All rights reserved.
+ ================================================================================
+ 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.
+ ============LICENSE_END=========================================================
+ -->
+
+<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>100</itemValue1>
+<config>
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/newYangModel.yang b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/newYangModel.yang
new file mode 100644
index 000000000..bbe66c3ae
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/newYangModel.yang
@@ -0,0 +1,8 @@
+module newyangmodel {
+ namespace "http://onap.org/newyangmodel";
+ prefix config2;
+ container config2 {
+ config true;
+ leaf item1 {type uint32;}
+ }
+}
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/updatedConfig.xml b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/updatedConfig.xml
new file mode 100644
index 000000000..628a710fd
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/updatedConfig.xml
@@ -0,0 +1,24 @@
+<!--
+ ============LICENSE_START=======================================================
+ Simulator
+ ================================================================================
+ Copyright (C) 2019 Nokia. All rights reserved.
+ ================================================================================
+ 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.
+ ============LICENSE_END=========================================================
+ -->
+
+<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>100</itemValue1>
+ <itemValue2>200</itemValue2>
+</config>
diff --git a/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/updatedConfigForCmHistory.xml b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/updatedConfigForCmHistory.xml
new file mode 100644
index 000000000..5bc0e4285
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/src/test/resources/updatedConfigForCmHistory.xml
@@ -0,0 +1,24 @@
+<!--
+ ============LICENSE_START=======================================================
+ Simulator
+ ================================================================================
+ Copyright (C) 2019 Nokia. All rights reserved.
+ ================================================================================
+ 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.
+ ============LICENSE_END=========================================================
+ -->
+
+<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>500</itemValue1>
+ <itemValue2>1000</itemValue2>
+</config>
diff --git a/test/mocks/pnfsimulator/netconfsimulator/ssh/ssh_host_rsa_key b/test/mocks/pnfsimulator/netconfsimulator/ssh/ssh_host_rsa_key
new file mode 100644
index 000000000..dbf8d7635
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/ssh/ssh_host_rsa_key
@@ -0,0 +1,49 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
+NhAAAAAwEAAQAAAgEA0Eve1v58bkLc4/6FvcBYWO8q6rOTJok7YyOHnc6QBJFuChSTbAaV
+nn+E8nPgyqN0/hH740+qjtWpaJdKh0+hJMw0vUeX6SQ1OpRgyXFbDSbFrjsBjB7N2eusrR
+M5FweDxVW/mfR7AHemrLulbNYHt/HmY+Hee09D/awICpuViLKLOsPp8ifmbHG1mXStLNSg
+OJgBvsrMvbsKYgJq0HdWPyfkzFf4SVdjKxeegQbkSogWGqRG7zkfu8bsYooUpIFRFMiJvW
+dr9tlegESAu1fmUx2Wz7EtN4Qq7xqHfAbT+Ruzr4rtCRRCsdEou49kpTSM75FaKlXmchRV
+tBQKndFhcPQCO+m9OkQ4+45VSeLL236MTvAFGnqfLks97TO7VLGinhtMvJA1gZI93bwVBS
+P1uaVuiUdzT8OgUq6xsLx2qg2aoau5Ivj65Hdj8tlIjeFHeGgUd1GZa434X3p8WCDFVrQK
+GpReaDTFttVFw7F2+biFmn68TO0bb4GE84r8ouaYVSGzWG1kEy8UtriXnx6GPOzncp8HKT
+YFn2etzs5EE9Ae64+1mIJTUEnWlpxwrjCqCIreibMz4/7AkSWrwc0YeAUERso+ESQkCU8R
+zpcYSRz50UTCaerS8K5cPE9N2XB3WYbsziNTpR568onQIkL+ZlTIbNNYBgdVNDLlQeabKS
+kAAAdILF3O/Sxdzv0AAAAHc3NoLXJzYQAAAgEA0Eve1v58bkLc4/6FvcBYWO8q6rOTJok7
+YyOHnc6QBJFuChSTbAaVnn+E8nPgyqN0/hH740+qjtWpaJdKh0+hJMw0vUeX6SQ1OpRgyX
+FbDSbFrjsBjB7N2eusrRM5FweDxVW/mfR7AHemrLulbNYHt/HmY+Hee09D/awICpuViLKL
+OsPp8ifmbHG1mXStLNSgOJgBvsrMvbsKYgJq0HdWPyfkzFf4SVdjKxeegQbkSogWGqRG7z
+kfu8bsYooUpIFRFMiJvWdr9tlegESAu1fmUx2Wz7EtN4Qq7xqHfAbT+Ruzr4rtCRRCsdEo
+u49kpTSM75FaKlXmchRVtBQKndFhcPQCO+m9OkQ4+45VSeLL236MTvAFGnqfLks97TO7VL
+GinhtMvJA1gZI93bwVBSP1uaVuiUdzT8OgUq6xsLx2qg2aoau5Ivj65Hdj8tlIjeFHeGgU
+d1GZa434X3p8WCDFVrQKGpReaDTFttVFw7F2+biFmn68TO0bb4GE84r8ouaYVSGzWG1kEy
+8UtriXnx6GPOzncp8HKTYFn2etzs5EE9Ae64+1mIJTUEnWlpxwrjCqCIreibMz4/7AkSWr
+wc0YeAUERso+ESQkCU8RzpcYSRz50UTCaerS8K5cPE9N2XB3WYbsziNTpR568onQIkL+Zl
+TIbNNYBgdVNDLlQeabKSkAAAADAQABAAACAQDFUyq+14T/W34wytzd/opzbddlUksTlavZ
+5j3CZH4Qpcjt6cIi8zXoWfuTR+1ramAZlOXf2IfGGmkLeU+UUf5hgsZvjZQ+vBtk7E2oaC
+eOlO1ued2kZUYzrMz/hRdvVqIhXnNNoMqpjbArMPSs3zGes53Df6UpgdTySnevvOZzAlld
+iV1mFyB2GV6lCmBH+QHzuyTkHvDIyJk0cf/Ij1T4LY3Ve3zt1chPeWeh6ep5JORzxq6gT6
+hdVjx3uUGG+i7aloPOF1yzFAcvUjX1xHagxIYrKTihwCaALsys1TcYZYLayKx3DmeEVpXU
+4SnCS7878KHPO2M9LUBngRjxmvpHtnaIyp4LugY48y+KtywjR39hOsKW3QawVp6CtTceNE
+QMlosaVIQuMJkeW14poYBclva0B/lCn3r9/3OrCI9qZPdD1RrCQqUyom56EU4kwRddSwHi
+LDj4xKoyzH022lfaTt+PwbXtVJGAOVTS86ZovJaIJwyKCE1T0p66qqIM/mo1GVMTfTKwUt
+A9v0AwTYFXmZ/9HJjhMNhCNclPilZHzuPI9VqiZ3tkKhS6kxZQA2cB1VdcFPIEeVOpZcJd
+yPzGfFuKYOh9CLQMxfMC46MeT1XoQi5bMBygy8ZajV9VM2xUMKft1IqyN65nA7d+covoxL
+PLjC6n3hUJJtSMTAohAQAAAQBf4lenniOgFycb1REtnUhyaCsopBmkuxlCOknkmFiMNW+B
+v/aeFUp03WluKGOVureJoooU3Z32Om2+YTxTOnCRL5Sn4gL7cAp7JfDSZpZPqynUAKFvKq
+QVbYyiEmFkUDFWes9Q0r0LAx2rvPrDIGhqx2ZgrzINlhrhxaQU2+fGbNRdI86PcWSYtc5u
+Oucd6nJM7eI3QL7/pVNlK3GMJ68eviKmhxP5HHnanNes2ORa15S5rRSOAZ5653pA1J1KxB
+J5vwwMvIXbEnBn8NilqWK50DHJMFXEoLaKlb0OoYMKbiLt4CjcCCIUUT1kAu/SFUpFdYI1
+DNXfrieu3ZTEwnU9AAABAQDrf9b8VnARNimzLqI15hp0Z5B8Sxf8Xw85KRRxg7cu6TJLoF
+K6VRK1jOzrKCrIzvBwuS2Er06ogEE/3N8eXC2ipzNOtDh7CQqoAq8IKNUt+2cvThNzfOFe
+BZ6lP9pQ60RGEPoeQyhlxHOUbV80K/ksiFqnAixOmOV7Uc7KZ+8clFvhOCm76vo80GaYYk
+NQtvMa1qxIsyUrOdhmIxF9dYN/sQMUr909o80kN69L7d+D1hG6WNskEJphrHSkPWTell5g
+4ekFna1+MjNQoCWhp6KPDKK9Y8AMrqWU/bFYw9CYwXo67p486qt487ZN+0cNItmSLMR3Ke
+MWmCmq37+v3viJAAABAQDibc4fU/xePaCjQa4VlAwk9ISd8Ufz/LcJWDiseKSzQnIdCaCG
+gO/SWTUuAzGEdrNnfJRcEUtrJ0UJAo2ntYZ3AlJLQvFF9KII3yuUn/RHMrkwslAIoqLhLO
+QX38nJeWR+hEWSFbpWP5N5biLRi2Qnwtv55hYgNqTLHRURzvin5/YeuwigBax1SwtN+V2D
+JSDhMiaFV85ZQggSoIjLIsunLK5XIDzTC82gJ9aaazvKBXwkf4Yfv5t1BgPATzqwjmXztQ
+T8WbTeqEO95cIu0zhfKIGo7Wvx7S9NPrNjyNO+JK9/qqdYGhErPiZPKOcHHywF77Yyv2cm
+gOrMYVCubUOhAAAAD21hcmNpbkBtYW5kaW5nbwECAw==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/test/mocks/pnfsimulator/netconfsimulator/ssh/ssh_host_rsa_key.pub b/test/mocks/pnfsimulator/netconfsimulator/ssh/ssh_host_rsa_key.pub
new file mode 100644
index 000000000..8f739114b
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/ssh/ssh_host_rsa_key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDQS97W/nxuQtzj/oW9wFhY7yrqs5MmiTtjI4edzpAEkW4KFJNsBpWef4Tyc+DKo3T+EfvjT6qO1alol0qHT6EkzDS9R5fpJDU6lGDJcVsNJsWuOwGMHs3Z66ytEzkXB4PFVb+Z9HsAd6asu6Vs1ge38eZj4d57T0P9rAgKm5WIsos6w+nyJ+ZscbWZdK0s1KA4mAG+ysy9uwpiAmrQd1Y/J+TMV/hJV2MrF56BBuRKiBYapEbvOR+7xuxiihSkgVEUyIm9Z2v22V6ARIC7V+ZTHZbPsS03hCrvGod8BtP5G7Oviu0JFEKx0Si7j2SlNIzvkVoqVeZyFFW0FAqd0WFw9AI76b06RDj7jlVJ4svbfoxO8AUaep8uSz3tM7tUsaKeG0y8kDWBkj3dvBUFI/W5pW6JR3NPw6BSrrGwvHaqDZqhq7ki+Prkd2Py2UiN4Ud4aBR3UZlrjfhfenxYIMVWtAoalF5oNMW21UXDsXb5uIWafrxM7RtvgYTzivyi5phVIbNYbWQTLxS2uJefHoY87OdynwcpNgWfZ63OzkQT0B7rj7WYglNQSdaWnHCuMKoIit6JszPj/sCRJavBzRh4BQRGyj4RJCQJTxHOlxhJHPnRRMJp6tLwrlw8T03ZcHdZhuzOI1OlHnryidAiQv5mVMhs01gGB1U0MuVB5pspKQ== marcin@mandingo
diff --git a/test/mocks/pnfsimulator/netconfsimulator/tls/ca.crt b/test/mocks/pnfsimulator/netconfsimulator/tls/ca.crt
new file mode 100644
index 000000000..62593ab7c
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/tls/ca.crt
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID7TCCAtWgAwIBAgIJAMtE1NGAR5KoMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD
+VQQGEwJDWjEWMBQGA1UECAwNU291dGggTW9yYXZpYTENMAsGA1UEBwwEQnJubzEP
+MA0GA1UECgwGQ0VTTkVUMQwwCgYDVQQLDANUTUMxEzARBgNVBAMMCmV4YW1wbGUg
+Q0ExIjAgBgkqhkiG9w0BCQEWE2V4YW1wbGVjYUBsb2NhbGhvc3QwHhcNMTQwNzI0
+MTQxOTAyWhcNMjQwNzIxMTQxOTAyWjCBjDELMAkGA1UEBhMCQ1oxFjAUBgNVBAgM
+DVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xDzANBgNVBAoMBkNFU05FVDEM
+MAoGA1UECwwDVE1DMRMwEQYDVQQDDApleGFtcGxlIENBMSIwIAYJKoZIhvcNAQkB
+FhNleGFtcGxlY2FAbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEArD3TDHPAMT2Z84orK4lMlarbgooIUCcRZyLe+QM+8KY8Hn+mGaxPEOTS
+L3ywszqefB/Utm2hPKLHX684iRC14ID9WDGHxPjvoPArhgFhfV+qnPfxKTgxZC12
+uOj4u1V9y+SkTCocFbRfXVBGpojrBuDHXkDMDEWNvr8/52YCv7bGaiBwUHolcLCU
+bmtKILCG0RNJyTaJpXQdAeq5Z1SJotpbfYFFtAXB32hVoLug1dzl2tjG9sb1wq3Q
+aDExcbC5w6P65qOkNoyym9ne6QlQagCqVDyFn3vcqkRaTjvZmxauCeUxXgJoXkyW
+cm0lM1KMHdoTArmchw2Dz0yHHSyDAQIDAQABo1AwTjAdBgNVHQ4EFgQUc1YQIqjZ
+sHVwlea0AB4N+ilNI2gwHwYDVR0jBBgwFoAUc1YQIqjZsHVwlea0AB4N+ilNI2gw
+DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAI/1KH60qnw9Xs2RGfi0/
+IKf5EynXt4bQX8EIyVKwSkYKe04zZxYfLIl/Q2HOPYoFmm3daj5ddr0ZS1i4p4fT
+UhstjsYWvXs3W/HhVmFUslakkn3PrswhP77fCk6eEJLxdfyJ1C7Uudq2m1isZbKi
+h+XF0mG1LxJaDMocSz4eAya7M5brwjy8DoOmA1TnLQFCVcpn+sCr7VC4wE/JqxyV
+hBCk/MuGqqM3B1j90bGFZ112ZOecyE0EDSr6IbiRBtmeNbEwOFjKXhNLYdxpBZ9D
+8A/368OckZkCrVLGuJNxK9UwCVTe8IhotHUqU9EqFDmxdV8oIdU/OzUwwNPA/Bd/
+9g==
+-----END CERTIFICATE-----
diff --git a/test/mocks/pnfsimulator/netconfsimulator/tls/server_cert.crt b/test/mocks/pnfsimulator/netconfsimulator/tls/server_cert.crt
new file mode 100644
index 000000000..c0e03a3f0
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/tls/server_cert.crt
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCQ1ox
+FjAUBgNVBAgMDVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xDzANBgNVBAoM
+BkNFU05FVDEMMAoGA1UECwwDVE1DMRMwEQYDVQQDDApleGFtcGxlIENBMSIwIAYJ
+KoZIhvcNAQkBFhNleGFtcGxlY2FAbG9jYWxob3N0MB4XDTE1MDczMDA3MjU1MFoX
+DTM1MDcyNTA3MjU1MFowgYUxCzAJBgNVBAYTAkNaMRYwFAYDVQQIDA1Tb3V0aCBN
+b3JhdmlhMQ8wDQYDVQQKDAZDRVNORVQxDDAKBgNVBAsMA1RNQzEXMBUGA1UEAwwO
+ZXhhbXBsZSBzZXJ2ZXIxJjAkBgkqhkiG9w0BCQEWF2V4YW1wbGVzZXJ2ZXJAbG9j
+YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdI1TBjzX1Pg
+QXFuPCw5/kQwU7qkrhirMcFAXhI8EoXepPa9fKAVuMjHW32P6nNzDpnhFe0YGdNl
+oIEN3hJJ87cVOqj4o7zZMbq3zVG2L8As7MTA8tYXm2fSC/0rIxxRRemcGUXM0q+4
+LEACjZj2pOKonaivF5VbhgNjPCO1Jj/TamUc0aViE577C9L9EiObGM+bGbabWk/K
+WKLsvxUc+sKZXaJ7psTVgpggJAkUszlmwOQgFiMSR53E9/CAkQYhzGVCmH44Vs6H
+zs3RZjOTbce4wr4ongiA5LbPeSNSCFjy9loKpaE1rtOjkNBVdiNPCQTmLuODXUTK
+gkeL+9v/OwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu
+U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU83qEtQDFzDvLoaII
+vqiU6k7j1uswHwYDVR0jBBgwFoAUc1YQIqjZsHVwlea0AB4N+ilNI2gwDQYJKoZI
+hvcNAQELBQADggEBAJ+QOLi4gPWGofMkLTqSsbv5xRvTw0xa/sJnEeiejtygAu3o
+McAsyevSH9EYVPCANxzISPzd9SFaO56HxWgcxLn9vi8ZNvo2wIp9zucNu285ced1
+K/2nDZfBmvBxXnj/n7spwqOyuoIc8sR7P7YyI806Qsfhk3ybNZE5UHJFZKDRQKvR
+J1t4nk9saeo87kIuNEDfYNdwYZzRfXoGJ5qIJQK+uJJv9noaIhfFowDW/G14Ji5p
+Vh/YtvnOPh7aBjOj8jmzk8MqzK+TZgT7GWu48Nd/NaV8g/DNg9hlN047LaNsJly3
+NX3+VBlpMnA4rKwl1OnmYSirIVh9RJqNwqe6k/k=
+-----END CERTIFICATE-----
diff --git a/test/mocks/pnfsimulator/netconfsimulator/tls/server_key.pem b/test/mocks/pnfsimulator/netconfsimulator/tls/server_key.pem
new file mode 100644
index 000000000..d61c77bdf
--- /dev/null
+++ b/test/mocks/pnfsimulator/netconfsimulator/tls/server_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAsdI1TBjzX1PgQXFuPCw5/kQwU7qkrhirMcFAXhI8EoXepPa9
+fKAVuMjHW32P6nNzDpnhFe0YGdNloIEN3hJJ87cVOqj4o7zZMbq3zVG2L8As7MTA
+8tYXm2fSC/0rIxxRRemcGUXM0q+4LEACjZj2pOKonaivF5VbhgNjPCO1Jj/TamUc
+0aViE577C9L9EiObGM+bGbabWk/KWKLsvxUc+sKZXaJ7psTVgpggJAkUszlmwOQg
+FiMSR53E9/CAkQYhzGVCmH44Vs6Hzs3RZjOTbce4wr4ongiA5LbPeSNSCFjy9loK
+paE1rtOjkNBVdiNPCQTmLuODXUTKgkeL+9v/OwIDAQABAoIBAG/4MG1JbL4C/7vV
+pBcpth7Aaznd1eJ2UB4VVOWnT8JOH2L6p1h5KRRhAP9AMkXsCnAQPyZiVAG3FlAZ
+01SZaY2YJDr6uQ3JVW4155TWtgSdWux//Ass+lJ17lJ0SRxjsV13ez6CsDWeRjc+
+2xy0S+KJgqk71XzhJG9fZLYyuddp3U/i3xFPUAcQM9xXKxcaD7g6LJf+a9pt6rim
+Eqq/pjJxDgTsRLARsazYuxrlOB445mvnLiYhOf2/MvI80jIUKaj8BeAhg49UIg/k
+mIh0xdevkcxBFer/BjBjscWaFjx14D6nkFMw7vtCum5KfalLN2edZKAzByOudGD4
+5KnRp3ECgYEA6vnSoNGg9Do80JOpXRGYWhcR1lIDO5yRW5rVagncCcW5Pn/GMtNd
+x2q6k1ks8mXKR9CxZrxZGqeYObZ9a/5SLih7ZkpiVWXG8ZiBIPhP6lnwm5OeIqLa
+hr0BYWcRfrGg1phj5uySZgsVBE+D8jH42O9ccdvrWv1OiryAHfKIcwMCgYEAwbs+
+HfQtvHOQXSYNhtOeA7IetkGy3cKVg2oILNcROvI96hS0MZKt1Rko0UAapx96eCIr
+el7vfdT0eUzNqt2wTKp1zmiG+SnX3fMDJNzMwu/jb/b4wQ20IHWNDnqcqTUVRUnL
+iksLFoHbTxsN5NpEQExcSt/zzP4qi1W2Bmo18WkCgYEAnhrk16LVux9ohiulHONW
+8N9u+BeM51JtGAcxrDzgGo85Gs2czdwc0K6GxdiN/rfxCKtqgqcfCWlVaxfYgo7I
+OxiwF17blXx7BVrJICcUlqpX1Ebac5HCmkCYqjJQuj/I6jv1lI7/3rt8M79RF+j5
++PXt7Qq97SZd78nwJrZni4MCgYAiPjZ8lOyAouyhilhZvI3xmUpUbMhw6jQDRnqr
+clhZUvgeqAoxuPuA7zGHywzq/WVoVqHYv28Vjs6noiu4R/chlf+8vD0fTYYadRnZ
+Ki4HRt+sqrrNZN6x3hVQudt3DSr1VFXl293Z3JonIWETUoE93EFz+qHdWg+rETtb
+ZuqiAQKBgD+HI/syLECyO8UynuEaDD7qPl87PJ/CmZLMxa2/ZZUjhaXAW7CJMaS6
+9PIzsLk33y3O4Qer0wx/tEdfnxMTBJrgGt/lFFdAKhSJroZ45l5apiavg1oZYp89
+jSd0lVxWSmrBjBZLnqOl336gzaBVkBD5ND+XUPdR1UuVQExJlem4
+-----END RSA PRIVATE KEY-----