diff options
Diffstat (limited to 'test/mocks/pnfsimulator/netconfsimulator')
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 Binary files differnew file mode 100755 index 000000000..efdcc135d --- /dev/null +++ b/test/mocks/pnfsimulator/netconfsimulator/netopeer-change-saver-native/libsysrepo-cpp.so 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----- |