aboutsummaryrefslogtreecommitdiffstats
path: root/test/mocks/netconf-pnp-simulator
diff options
context:
space:
mode:
authorebo <eliezio.oliveira@est.tech>2020-02-27 14:04:23 +0000
committerebo <eliezio.oliveira@est.tech>2020-02-28 11:28:56 +0000
commit3ecb7b39473c5ea6835fcbdd57b91acb74781a52 (patch)
tree29ca00e16f35cf418a95ce0b3644b79164432b83 /test/mocks/netconf-pnp-simulator
parent9babb59ffd0e65b1e4acdb13f4b25205ae1bfdda (diff)
Add NETCONF PNF Simulator Engine
Issue-ID: INT-1124 Signed-off-by: ebo <eliezio.oliveira@est.tech> Change-Id: Ifb50a749992cbd662d579e1cb861bd8f55b3f808
Diffstat (limited to 'test/mocks/netconf-pnp-simulator')
-rw-r--r--test/mocks/netconf-pnp-simulator/README.md9
-rw-r--r--test/mocks/netconf-pnp-simulator/docs/README.md63
-rw-r--r--test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/data.json10
-rw-r--r--test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml12
-rw-r--r--test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/model.yang29
-rwxr-xr-xtest/mocks/netconf-pnp-simulator/docs/examples/mynetconf/subscriber.py136
-rw-r--r--test/mocks/netconf-pnp-simulator/docs/images/Architecture.pngbin0 -> 58061 bytes
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/Dockerfile179
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/LICENSE13
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/config/modules/.gitkeep0
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/config/tls/load_server_certs.xml62
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/config/tls/server_key.pem27
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/config/tls/server_key.pem.pub9
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/config/tls/tls_listen.xml27
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/container-tag.yaml1
-rwxr-xr-xtest/mocks/netconf-pnp-simulator/engine/entrypoint.sh132
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/patches/Netopeer2/01-fix-grep-count.patch35
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/01-configurable-PYTHON_MODULE_PATH.patch14
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/02-fix-missing-include-dir.patch11
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/03-fix-missing-pthread_rwlockattr_setkind_np.patch20
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/patches/libyang/01-configurable-PYTHON_MODULE_PATH.patch17
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/patches/sysrepo/01-configurable-PYTHON_MODULE_PATH.patch21
-rw-r--r--test/mocks/netconf-pnp-simulator/engine/supervisord.conf45
23 files changed, 872 insertions, 0 deletions
diff --git a/test/mocks/netconf-pnp-simulator/README.md b/test/mocks/netconf-pnp-simulator/README.md
new file mode 100644
index 000000000..df3211844
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/README.md
@@ -0,0 +1,9 @@
+# NETCONF Plug-and-Play Simulator
+
+Instead of a single docker image aggregating all Yang models and simulation logic, this simulator uses a modular
+approach that is reflected on this directory structure:
+
+- engine: Contains only the core NETCONF engine and files required to build the
+ docker image;
+- modules: The modules containing the Yang models and its corresponding
+ applications goes here.
diff --git a/test/mocks/netconf-pnp-simulator/docs/README.md b/test/mocks/netconf-pnp-simulator/docs/README.md
new file mode 100644
index 000000000..8aa24504f
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/README.md
@@ -0,0 +1,63 @@
+# NETCONF Plug-and-Play Simulator
+
+[![GitHub Tag][gh-tag-badge]]()
+[![Docker Automated Build][dockerhub-badge]][dockerhub]
+
+## Overview
+
+This project builds a modular engine that allows the creation of NETCONF-enabled devices simulators,
+either physical (PNF) and virtual (VNF).
+
+Basically it's a docker container running Sysrepo and Netopeer2 servers enhanced with a plugger script that, at
+start-time, performs the following actions:
+
+1. Configures TLS and SSH secure accesses to the Netopeer2 server;
+2. Installs multiple YANG models into sysrepo datastore;
+3. Launches the corresponding subscriber applications.
+
+The picture below unveils the architecture of this solution.
+
+![Architecture](images/Architecture.png)
+
+A YANG module contains the following files:
+
+| Filename | Purpose
+| -------- | -------
+|`model.yang` | The YANG model specified according to [RFC-6020][yang-rfc]. Alternatively, you can use your model's name as a basename for this file. Example: `mynetconf.yang`.
+|`data.json` or `data.xml` | An optional data file used to initialize your model.
+|`subscriber.py` | The Python 3 application that implements the behavioral aspects of the YANG model.
+|`requirements.txt` | [Optional] Additional Python packages specified in the [Requirements File Format][py-requirements].
+
+## Application
+
+The `subscriber` is free to implement any wanted passive or active behaviour:
+
+**Passive Behaviour**: The subscriber will receive an event for each modification externally applied to the YANG model.
+
+**Active Behaviour**: At any point in time the subscriber can proactively change its own YANG model.
+
+## Runtime Configuration
+
+### Customizing TLS and SSH accesses
+
+The distributed docker image comes with a sample configuration for TLS and SSH, that can be found at
+`/config/tls` and `/home/netconf/.ssh` directories respectively. The user can replace one or both configurations
+by mounting a custom directory under the respective TLS or SSH mounting point.
+
+### Python Virtual Environment Support
+
+Python programs usually use additional packages not included in the standard Python distribution,
+like the `requests` package, for example.
+We support this scenario by creating isolated Python environments for each custom-provided module whenever
+a `requirements.txt` file is present in the module directory.
+
+## Example Module
+
+The directory `examples/mynetconf` contains an example YANG model and its subscriber along with a
+Docker Compose configuration file to launch a basic simulator.
+
+[dockerhub]: https://hub.docker.com/r/blueonap/netconf-pnp-simulator/
+[dockerhub-badge]: https://img.shields.io/docker/cloud/automated/blueonap/netconf-pnp-simulator
+[gh-tag-badge]: https://img.shields.io/github/v/tag/blue-onap/netconf-pnp-simulator?label=Release
+[py-requirements]: https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format
+[yang-rfc]: https://tools.ietf.org/html/rfc6020
diff --git a/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/data.json b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/data.json
new file mode 100644
index 000000000..63872eef9
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/data.json
@@ -0,0 +1,10 @@
+{
+ "mynetconf:netconflist": {
+ "netconf": [
+ {
+ "netconf-id": 3,
+ "netconf-param": 3
+ }
+ ]
+ }
+}
diff --git a/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml
new file mode 100644
index 000000000..ee70c4fd9
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/docker-compose.yml
@@ -0,0 +1,12 @@
+version: '3'
+
+services:
+ netopeer2:
+ image: nexus3.onap.org:10001/onap/integration/simulators/netconf-pnp-simulator:2.6.0
+ container_name: mynetconf
+ restart: always
+ ports:
+ - "830:830"
+ - "6513:6513"
+ volumes:
+ - ./:/config/modules/mynetconf
diff --git a/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/model.yang b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/model.yang
new file mode 100644
index 000000000..6c8c36ab0
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/model.yang
@@ -0,0 +1,29 @@
+module mynetconf {
+ yang-version 1.1;
+ namespace "urn:mynetconf:test";
+
+ prefix nft;
+
+ organization
+ "mynetconf";
+ contact
+ "my netconf address";
+ description
+ "yang model for mynetconf";
+ revision "2019-03-01" {
+ description
+ "initial version";
+ }
+
+ container netconflist {
+ list netconf {
+ key netconf-id;
+ leaf netconf-id {
+ type uint16;
+ }
+ leaf netconf-param {
+ type uint32;
+ }
+ }
+ }
+}
diff --git a/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/subscriber.py b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/subscriber.py
new file mode 100755
index 000000000..612729675
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/examples/mynetconf/subscriber.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+
+__author__ = "Mislav Novakovic <mislav.novakovic@sartura.hr>"
+__copyright__ = "Copyright 2018, Deutsche Telekom AG"
+__license__ = "Apache 2.0"
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This sample application demonstrates use of Python programming language bindings for sysrepo library.
+# Original c application was rewritten in Python to show similarities and differences
+# between the two.
+#
+# Most notable difference is in the very different nature of languages, c is weakly statically typed language
+# while Python is strongly dynamically typed. Python code is much easier to read and logic easier to comprehend
+# for smaller scripts. Memory safety is not an issue but lower performance can be expected.
+#
+# The original c implementation is also available in the source, so one can refer to it to evaluate trade-offs.
+
+import sysrepo as sr
+import sys
+
+
+# Helper function for printing changes given operation, old and new value.
+def print_change(op, old_val, new_val):
+ if op == sr.SR_OP_CREATED:
+ print(f"CREATED: {new_val.to_string()}")
+ elif op == sr.SR_OP_DELETED:
+ print(f"DELETED: {old_val.to_string()}")
+ elif op == sr.SR_OP_MODIFIED:
+ print(f"MODIFIED: {old_val.to_string()} to {new_val.to_string()}")
+ elif op == sr.SR_OP_MOVED:
+ print(f"MOVED: {new_val.xpath()} after {old_val.xpath()}")
+
+
+# Helper function for printing events.
+def ev_to_str(ev):
+ if ev == sr.SR_EV_VERIFY:
+ return "verify"
+ elif ev == sr.SR_EV_APPLY:
+ return "apply"
+ elif ev == sr.SR_EV_ABORT:
+ return "abort"
+ else:
+ return "unknown"
+
+
+# Function to print current configuration state.
+# It does so by loading all the items of a session and printing them out.
+def print_current_config(session, module_name):
+ select_xpath = f"/{module_name}:*//*"
+
+ values = session.get_items(select_xpath)
+
+ if values is not None:
+ print("========== BEGIN CONFIG ==========")
+ for i in range(values.val_cnt()):
+ print(values.val(i).to_string(), end='')
+ print("=========== END CONFIG ===========")
+
+
+# Function to be called for subscribed client of given session whenever configuration changes.
+def module_change_cb(sess, module_name, event, private_ctx):
+ try:
+ print("========== Notification " + ev_to_str(event) + " =============================================")
+ if event == sr.SR_EV_APPLY:
+ print_current_config(sess, module_name)
+
+ print("========== CHANGES: =============================================")
+
+ change_path = f"/{module_name}:*"
+
+ it = sess.get_changes_iter(change_path)
+
+ while True:
+ change = sess.get_change_next(it)
+ if change is None:
+ break
+ print_change(change.oper(), change.old_val(), change.new_val())
+
+ print("========== END OF CHANGES =======================================")
+ except Exception as e:
+ print(e)
+
+ return sr.SR_ERR_OK
+
+
+def main():
+ # Notable difference between c implementation is using exception mechanism for open handling unexpected events.
+ # Here it is useful because `Connection`, `Session` and `Subscribe` could throw an exception.
+ try:
+ module_name = "ietf-interfaces"
+ if len(sys.argv) > 1:
+ module_name = sys.argv[1]
+ else:
+ print("\nYou can pass the module name to be subscribed as the first argument")
+
+ print(f"Application will watch for changes in {module_name}")
+
+ # connect to sysrepo
+ conn = sr.Connection(module_name)
+
+ # start session
+ sess = sr.Session(conn)
+
+ # subscribe for changes in running config */
+ subscribe = sr.Subscribe(sess)
+
+ subscribe.module_change_subscribe(module_name, module_change_cb)
+
+ try:
+ print_current_config(sess, module_name)
+ except Exception as e:
+ print(e)
+
+ print("========== STARTUP CONFIG APPLIED AS RUNNING ==========")
+
+ sr.global_loop()
+
+ print("Application exit requested, exiting.")
+
+ except Exception as e:
+ print(e)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/mocks/netconf-pnp-simulator/docs/images/Architecture.png b/test/mocks/netconf-pnp-simulator/docs/images/Architecture.png
new file mode 100644
index 000000000..da95c9142
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/docs/images/Architecture.png
Binary files differ
diff --git a/test/mocks/netconf-pnp-simulator/engine/Dockerfile b/test/mocks/netconf-pnp-simulator/engine/Dockerfile
new file mode 100644
index 000000000..5432b646a
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/Dockerfile
@@ -0,0 +1,179 @@
+#-
+# ============LICENSE_START=======================================================
+# Copyright (C) 2020 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+
+FROM python:3.7.6-alpine3.11 as build
+
+ARG libyang_version=v1.0-r5
+ARG sysrepo_version=v0.7.9
+ARG libnetconf2_version=v0.12-r2
+ARG netopeer2_version=v0.7-r2
+
+WORKDIR /usr/src
+
+RUN set -eux \
+ && apk add \
+ autoconf \
+ bash \
+ build-base \
+ cmake \
+ curl-dev \
+ file \
+ git \
+ libev-dev \
+ openssh-keygen \
+ openssl \
+ openssl-dev \
+ pcre-dev \
+ pkgconfig \
+ protobuf-c-dev \
+ swig \
+ # for troubleshooting
+ the_silver_searcher \
+ vim \
+ # v0.9.3 has somes bugs as warned in libnetconf2/CMakeLists.txt:237
+ && apk add --repository http://dl-cdn.alpinelinux.org/alpine/v3.10/main libssh-dev==0.8.8-r0
+
+RUN git config --global advice.detachedHead false
+
+ENV PKG_CONFIG_PATH=/opt/lib64/pkgconfig
+ENV LD_LIBRARY_PATH=/opt/lib:/opt/lib64
+
+
+# libyang
+COPY patches/libyang/ ./patches/libyang/
+RUN set -eux \
+ && git clone --branch $libyang_version --depth 1 https://github.com/CESNET/libyang.git \
+ && cd libyang \
+ && for p in ../patches/libyang/*.patch; do patch -p1 -i $p; done \
+ && mkdir build && cd build \
+ && cmake -DCMAKE_BUILD_TYPE:String="Release" -DENABLE_BUILD_TESTS=OFF \
+ -DCMAKE_INSTALL_PREFIX:PATH=/opt \
+ -DGEN_LANGUAGE_BINDINGS=ON \
+ -DPYTHON_MODULE_PATH:PATH=/opt/lib/python3.7/site-packages \
+ .. \
+ && make -j2 \
+ && make install
+
+RUN set -eux \
+ && git clone --depth 1 https://github.com/sysrepo/libredblack.git \
+ && cd libredblack \
+ && ./configure --prefix=/opt --without-rbgen \
+ && make \
+ && make install
+
+# sysrepo
+COPY patches/sysrepo/ ./patches/sysrepo/
+RUN set -eux \
+ && git clone --branch $sysrepo_version --depth 1 https://github.com/sysrepo/sysrepo.git \
+ && cd sysrepo \
+ && for p in ../patches/sysrepo/*.patch; do patch -p1 -i $p; done \
+ && mkdir build && cd build \
+ && cmake -DCMAKE_BUILD_TYPE:String="Release" -DENABLE_TESTS=OFF \
+ -DREPOSITORY_LOC:PATH=/opt/etc/sysrepo \
+ -DCMAKE_INSTALL_PREFIX:PATH=/opt \
+ -DGEN_PYTHON_VERSION=3 \
+ -DPYTHON_MODULE_PATH:PATH=/opt/lib/python3.7/site-packages \
+ .. \
+ && make -j2 \
+ && make install
+
+# libnetconf2
+COPY patches/libnetconf2/ ./patches/libnetconf2/
+RUN set -eux \
+ && git clone --branch $libnetconf2_version --depth 1 https://github.com/CESNET/libnetconf2.git \
+ && cd libnetconf2 \
+ && for p in ../patches/libnetconf2/*.patch; do patch -p1 -i $p; done \
+ && mkdir build && cd build \
+ && cmake -DCMAKE_BUILD_TYPE:String="Release" -DENABLE_BUILD_TESTS=OFF \
+ -DCMAKE_INSTALL_PREFIX:PATH=/opt \
+ -DENABLE_PYTHON=ON \
+ -DPYTHON_MODULE_PATH:PATH=/opt/lib/python3.7/site-packages \
+ .. \
+ && make \
+ && make install
+
+# keystore
+COPY patches/Netopeer2/ ./patches/Netopeer2/
+RUN set -eux \
+ && git clone --branch $netopeer2_version --depth 1 https://github.com/CESNET/Netopeer2.git \
+ && cd Netopeer2 \
+ && for p in ../patches/Netopeer2/*.patch; do patch -p1 -i $p; done \
+ && cd keystored \
+ && mkdir build && cd build \
+ && cmake -DCMAKE_BUILD_TYPE:String="Release" \
+ -DCMAKE_INSTALL_PREFIX:PATH=/opt \
+ .. \
+ && make -j2 \
+ && make install
+
+# netopeer2
+RUN set -eux \
+ && cd Netopeer2/server \
+ && mkdir build && cd build \
+ && cmake -DCMAKE_BUILD_TYPE:String="Release" \
+ -DCMAKE_INSTALL_PREFIX:PATH=/opt \
+ .. \
+ && make -j2 \
+ && make install
+
+FROM python:3.7.6-alpine3.11
+LABEL authors="eliezio.oliveira@est.tech"
+
+RUN set -eux \
+ && pip install supervisor \
+ && apk update \
+ && apk upgrade -a \
+ && apk add \
+ libcurl \
+ libev \
+ openssh-keygen \
+ pcre \
+ protobuf-c \
+ # v0.9.3 has somes bugs as warned in libnetconf2/CMakeLists.txt:237
+ && apk add --repository http://dl-cdn.alpinelinux.org/alpine/v3.10/main libssh==0.8.8-r0 \
+ && rm -rf /var/cache/apk/*
+
+COPY --from=build /opt/ /opt/
+
+ENV LD_LIBRARY_PATH=/opt/lib:/opt/lib64
+
+COPY config/ /config
+VOLUME /config
+
+# finish setup and add netconf user
+RUN adduser --system --disabled-password --gecos 'Netconf User' netconf
+
+ENV HOME=/home/netconf
+VOLUME $HOME/.local/share/virtualenvs
+
+# generate ssh keys for netconf user
+RUN set -eux \
+ && mkdir -p $HOME/.cache \
+ && mkdir -p $HOME/.ssh \
+ && ssh-keygen -t dsa -P '' -f $HOME/.ssh/id_dsa \
+ && cat $HOME/.ssh/id_dsa.pub > $HOME/.ssh/authorized_keys
+
+EXPOSE 830
+
+COPY supervisord.conf /etc/supervisord.conf
+RUN mkdir /etc/supervisord.d
+
+COPY entrypoint.sh /opt/bin/
+
+CMD /opt/bin/entrypoint.sh
diff --git a/test/mocks/netconf-pnp-simulator/engine/LICENSE b/test/mocks/netconf-pnp-simulator/engine/LICENSE
new file mode 100644
index 000000000..c6aae559e
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/LICENSE
@@ -0,0 +1,13 @@
+Copyright (C) 2020 Nordix Foundation
+
+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.
diff --git a/test/mocks/netconf-pnp-simulator/engine/config/modules/.gitkeep b/test/mocks/netconf-pnp-simulator/engine/config/modules/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/config/modules/.gitkeep
diff --git a/test/mocks/netconf-pnp-simulator/engine/config/tls/load_server_certs.xml b/test/mocks/netconf-pnp-simulator/engine/config/tls/load_server_certs.xml
new file mode 100644
index 000000000..8872a8edb
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/config/tls/load_server_certs.xml
@@ -0,0 +1,62 @@
+<keystore xmlns="urn:ietf:params:xml:ns:yang:ietf-keystore">
+ <private-keys>
+ <private-key>
+ <name>server_key</name>
+ <certificate-chains>
+ <certificate-chain>
+ <name>server_cert</name>
+ <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=</certificate>
+ </certificate-chain>
+ </certificate-chains>
+ </private-key>
+ </private-keys>
+ <trusted-certificates>
+ <name>trusted_ca_list</name>
+ <trusted-certificate>
+ <name>ca</name>
+ <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==</certificate>
+ </trusted-certificate>
+ </trusted-certificates>
+</keystore>
diff --git a/test/mocks/netconf-pnp-simulator/engine/config/tls/server_key.pem b/test/mocks/netconf-pnp-simulator/engine/config/tls/server_key.pem
new file mode 100644
index 000000000..d61c77bdf
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/config/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-----
diff --git a/test/mocks/netconf-pnp-simulator/engine/config/tls/server_key.pem.pub b/test/mocks/netconf-pnp-simulator/engine/config/tls/server_key.pem.pub
new file mode 100644
index 000000000..9ccec4a0c
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/config/tls/server_key.pem.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdI1TBjzX1PgQXFuPCw5
+/kQwU7qkrhirMcFAXhI8EoXepPa9fKAVuMjHW32P6nNzDpnhFe0YGdNloIEN3hJJ
+87cVOqj4o7zZMbq3zVG2L8As7MTA8tYXm2fSC/0rIxxRRemcGUXM0q+4LEACjZj2
+pOKonaivF5VbhgNjPCO1Jj/TamUc0aViE577C9L9EiObGM+bGbabWk/KWKLsvxUc
++sKZXaJ7psTVgpggJAkUszlmwOQgFiMSR53E9/CAkQYhzGVCmH44Vs6Hzs3RZjOT
+bce4wr4ongiA5LbPeSNSCFjy9loKpaE1rtOjkNBVdiNPCQTmLuODXUTKgkeL+9v/
+OwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/test/mocks/netconf-pnp-simulator/engine/config/tls/tls_listen.xml b/test/mocks/netconf-pnp-simulator/engine/config/tls/tls_listen.xml
new file mode 100644
index 000000000..852f3d0f6
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/config/tls/tls_listen.xml
@@ -0,0 +1,27 @@
+<netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
+ <listen>
+ <endpoint>
+ <name>tls_listen_endpt</name>
+ <tls>
+ <address>0.0.0.0</address>
+ <port>6513</port>
+ <certificates>
+ <certificate>
+ <name>server_cert</name>
+ </certificate>
+ </certificates>
+ <client-auth>
+ <trusted-ca-certs>trusted_ca_list</trusted-ca-certs>
+ <cert-maps>
+ <cert-to-name>
+ <id>1</id>
+ <fingerprint>02:E9:38:1F:F6:8B:62:DE:0A:0B:C5:03:81:A8:03:49:A0:00:7F:8B:F3</fingerprint>
+ <map-type xmlns:x509c2n="urn:ietf:params:xml:ns:yang:ietf-x509-cert-to-name">x509c2n:specified</map-type>
+ <name>netconf</name>
+ </cert-to-name>
+ </cert-maps>
+ </client-auth>
+ </tls>
+ </endpoint>
+ </listen>
+</netconf-server>
diff --git a/test/mocks/netconf-pnp-simulator/engine/container-tag.yaml b/test/mocks/netconf-pnp-simulator/engine/container-tag.yaml
new file mode 100644
index 000000000..f705e1e02
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/container-tag.yaml
@@ -0,0 +1 @@
+tag: "2.6.0"
diff --git a/test/mocks/netconf-pnp-simulator/engine/entrypoint.sh b/test/mocks/netconf-pnp-simulator/engine/entrypoint.sh
new file mode 100755
index 000000000..951ca474b
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/entrypoint.sh
@@ -0,0 +1,132 @@
+#!/bin/sh
+# shellcheck disable=SC2086
+
+#-
+# ============LICENSE_START=======================================================
+# Copyright (C) 2020 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+
+set -o errexit
+set -o nounset
+set -o pipefail
+set -o xtrace
+
+export PATH=/opt/bin:/usr/local/bin:/usr/bin:/bin
+
+CONFIG=/config
+TLS_CONFIG=$CONFIG/tls
+MODELS_CONFIG=$CONFIG/modules
+KEY_PATH=/opt/etc/keystored/keys
+BASE_VIRTUALENVS=$HOME/.local/share/virtualenvs
+
+find_file() {
+ local dir=$1
+ shift
+ for prog in "$@"; do
+ if [ -f $dir/$prog ]; then
+ echo -n $dir/$prog
+ break
+ fi
+ done
+}
+
+find_executable() {
+ local dir=$1
+ shift
+ for prog in "$@"; do
+ if [ -x $dir/$prog ]; then
+ echo -n $dir/$prog
+ break
+ fi
+ done
+}
+
+configure_tls()
+{
+ cp $TLS_CONFIG/server_key.pem $KEY_PATH
+ cp $TLS_CONFIG/server_key.pem.pub $KEY_PATH
+ sysrepocfg --datastore=startup --format=xml ietf-keystore --merge=$TLS_CONFIG/load_server_certs.xml
+ sysrepocfg --datastore=startup --format=xml ietf-netconf-server --merge=$TLS_CONFIG/tls_listen.xml
+}
+
+configure_modules()
+{
+ for dir in "$MODELS_CONFIG"/*; do
+ if [ -d $dir ]; then
+ model=${dir##*/}
+ install_and_configure_yang_model $dir $model
+ prog=$(find_executable $dir subscriber.py)
+ if [ -n "$prog" ]; then
+ configure_subscriber_execution $dir $model $prog
+ fi
+ fi
+ done
+}
+
+install_and_configure_yang_model()
+{
+ local dir=$1
+ local model=$2
+
+ yang=$(find_file $dir $model.yang model.yang)
+ sysrepoctl --install --yang=$yang
+ data=$(find_file $dir startup.json startup.xml data.json data.xml)
+ if [ -n "$data" ]; then
+ sysrepocfg --datastore=startup --import=$data $model
+ fi
+}
+
+configure_subscriber_execution()
+{
+ local dir=$1
+ local model=$2
+ local prog=$3
+
+ PROG_PATH=$PATH
+ if [ -r "$dir/requirements.txt" ]; then
+ env_dir=$(create_python_venv $dir)
+ PROG_PATH=$env_dir/bin:$PROG_PATH
+ fi
+ cat > /etc/supervisord.d/$model.conf <<EOF
+[program:subs-$model]
+command=$prog $model
+redirect_stderr=true
+autorestart=true
+environment=PATH=$PROG_PATH,PYTHONPATH=/opt/lib/python3.7/site-packages,PYTHONUNBUFFERED="1"
+EOF
+}
+
+create_python_venv()
+{
+ local dir=$1
+
+ mkdir -p $BASE_VIRTUALENVS
+ env_dir=$BASE_VIRTUALENVS/$model
+ (
+ python3 -m venv --system-site-packages $env_dir
+ cd $env_dir
+ . ./bin/activate
+ pip install --upgrade pip
+ pip install -r "$dir"/requirements.txt
+ ) 1>&2
+ echo $env_dir
+}
+
+configure_tls
+configure_modules
+
+exec /usr/local/bin/supervisord -c /etc/supervisord.conf
diff --git a/test/mocks/netconf-pnp-simulator/engine/patches/Netopeer2/01-fix-grep-count.patch b/test/mocks/netconf-pnp-simulator/engine/patches/Netopeer2/01-fix-grep-count.patch
new file mode 100644
index 000000000..00bc93085
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/patches/Netopeer2/01-fix-grep-count.patch
@@ -0,0 +1,35 @@
+diff --git a/keystored/scripts/model-install.sh b/keystored/scripts/model-install.sh
+index a350950..671dd16 100755
+--- a/keystored/scripts/model-install.sh
++++ b/keystored/scripts/model-install.sh
+@@ -13,7 +13,7 @@ local_path=$(dirname $0)
+ is_yang_module_installed() {
+ module=$1
+
+- $SYSREPOCTL -l | grep --count "^$module [^|]*|[^|]*| Installed .*$" > /dev/null
++ $SYSREPOCTL -l | grep -c "^$module [^|]*|[^|]*| Installed .*$" > /dev/null
+ }
+
+ install_yang_module() {
+diff --git a/server/scripts/model-install.sh.in b/server/scripts/model-install.sh.in
+index 589d639..760ce42 100755
+--- a/server/scripts/model-install.sh.in
++++ b/server/scripts/model-install.sh.in
+@@ -13,7 +13,7 @@ shopt -s failglob
+ is_yang_module_installed() {
+ module=$1
+
+- $SYSREPOCTL -l | grep --count "^$module [^|]*|[^|]*| Installed .*$" > /dev/null
++ $SYSREPOCTL -l | grep -c "^$module [^|]*|[^|]*| Installed .*$" > /dev/null
+ }
+
+ install_yang_module() {
+@@ -31,7 +31,7 @@ enable_yang_module_feature() {
+ module=$1
+ feature=$2
+
+- if ! $SYSREPOCTL -l | grep --count "^$module [^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|.* $feature.*$" > /dev/null; then
++ if ! $SYSREPOCTL -l | grep -c "^$module [^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|.* $feature.*$" > /dev/null; then
+ echo "- Enabling feature $feature in $module..."
+ $SYSREPOCTL -m $module -e $feature
+ else
diff --git a/test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/01-configurable-PYTHON_MODULE_PATH.patch b/test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/01-configurable-PYTHON_MODULE_PATH.patch
new file mode 100644
index 000000000..3deb95c29
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/01-configurable-PYTHON_MODULE_PATH.patch
@@ -0,0 +1,14 @@
+--- a/python/CMakeLists.txt 2020-02-19 12:25:07.000000000 +0000
++++ b/python/CMakeLists.txt 2020-02-20 14:56:26.810463000 +0000
+@@ -22,7 +22,9 @@
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docs/Makefile.in ${CMAKE_CURRENT_SOURCE_DIR}/docs/Makefile)
+ add_custom_target(pyapi ALL COMMAND ${PYTHON} ${SETUP_PY} build -b ${PYAPI_BUILD_DIR} ${DEBUG})
+ add_custom_target(pyapidoc COMMAND make -f ${CMAKE_CURRENT_SOURCE_DIR}/docs/Makefile html)
+- execute_process(COMMAND ${PYTHON} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(plat_specific=True))"
+- OUTPUT_VARIABLE PYTHON_MODULE_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
++ if(NOT DEFINED PYTHON_MODULE_PATH)
++ execute_process(COMMAND ${PYTHON} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(plat_specific=True))"
++ OUTPUT_VARIABLE PYTHON_MODULE_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
++ endif()
+ install(CODE "execute_process(COMMAND ${PYTHON} ${SETUP_PY} build -b ${PYAPI_BUILD_DIR} install --install-lib=\$ENV{DESTDIR}/${PYTHON_MODULE_PATH})")
+ endif()
diff --git a/test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/02-fix-missing-include-dir.patch b/test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/02-fix-missing-include-dir.patch
new file mode 100644
index 000000000..556b9fd84
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/02-fix-missing-include-dir.patch
@@ -0,0 +1,11 @@
+--- a/python/setup.py.in 2020-02-20 20:04:33.000000000 +0000
++++ b/python/setup.py.in 2020-02-20 20:04:57.000000000 +0000
+@@ -13,7 +13,7 @@
+ "${CMAKE_CURRENT_COURCE_DIR}/rpc.h"
+ ],
+ libraries=["netconf2"],
+- extra_compile_args=["-Wall", "-I${CMAKE_CURRENT_BINARY_DIR}" @SSH_DEFINE@ @TLS_DEFINE@],
++ extra_compile_args=["-Wall", "-I${CMAKE_CURRENT_BINARY_DIR}", "-I${LIBYANG_INCLUDE_DIR}", "-I${LIBSSH_INCLUDE_DIR}" @SSH_DEFINE@ @TLS_DEFINE@],
+ extra_link_args=["-L${CMAKE_CURRENT_BINARY_DIR}/.."],
+ )
+
diff --git a/test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/03-fix-missing-pthread_rwlockattr_setkind_np.patch b/test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/03-fix-missing-pthread_rwlockattr_setkind_np.patch
new file mode 100644
index 000000000..65537a017
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/patches/libnetconf2/03-fix-missing-pthread_rwlockattr_setkind_np.patch
@@ -0,0 +1,20 @@
+diff --git a/src/session_server.c b/src/session_server.c
+index 636b1a2..57f2854 100644
+--- a/src/session_server.c
++++ b/src/session_server.c
+@@ -560,6 +560,7 @@ nc_server_init(struct ly_ctx *ctx)
+ errno=0;
+
+ if (pthread_rwlockattr_init(&attr) == 0) {
++#ifdef PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
+ if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
+ if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
+ ERR("%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
+@@ -570,6 +571,7 @@ nc_server_init(struct ly_ctx *ctx)
+ } else {
+ ERR("%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
+ }
++#endif
+ pthread_rwlockattr_destroy(&attr);
+ } else {
+ ERR("%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
diff --git a/test/mocks/netconf-pnp-simulator/engine/patches/libyang/01-configurable-PYTHON_MODULE_PATH.patch b/test/mocks/netconf-pnp-simulator/engine/patches/libyang/01-configurable-PYTHON_MODULE_PATH.patch
new file mode 100644
index 000000000..167297f06
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/patches/libyang/01-configurable-PYTHON_MODULE_PATH.patch
@@ -0,0 +1,17 @@
+--- a/swig/python/CMakeLists.txt 2020-02-19 12:24:05.000000000 +0000
++++ b/swig/python/CMakeLists.txt 2020-02-20 14:54:59.279634000 +0000
+@@ -20,9 +20,11 @@
+
+ file(COPY "examples" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+
+-execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(plat_specific=True))"
+- OUTPUT_VARIABLE PYTHON_MODULE_PATH
+- OUTPUT_STRIP_TRAILING_WHITESPACE )
++if(NOT DEFINED PYTHON_MODULE_PATH)
++ execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(plat_specific=True))"
++ OUTPUT_VARIABLE PYTHON_MODULE_PATH
++ OUTPUT_STRIP_TRAILING_WHITESPACE )
++endif()
+
+ install( TARGETS _${PYTHON_SWIG_BINDING} DESTINATION ${PYTHON_MODULE_PATH})
+ install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${PYTHON_SWIG_BINDING}.py" DESTINATION ${PYTHON_MODULE_PATH})
diff --git a/test/mocks/netconf-pnp-simulator/engine/patches/sysrepo/01-configurable-PYTHON_MODULE_PATH.patch b/test/mocks/netconf-pnp-simulator/engine/patches/sysrepo/01-configurable-PYTHON_MODULE_PATH.patch
new file mode 100644
index 000000000..3c6fa7b87
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/patches/sysrepo/01-configurable-PYTHON_MODULE_PATH.patch
@@ -0,0 +1,21 @@
+diff --git a/swig/python/CMakeLists.txt b/swig/python/CMakeLists.txt
+index 7d00a8b7..dc06da00 100644
+--- a/swig/python/CMakeLists.txt
++++ b/swig/python/CMakeLists.txt
+@@ -24,10 +24,12 @@ swig_link_libraries(${PYTHON_SWIG_BINDING} ${PYTHON_LIBRARIES} Sysrepo-cpp)
+
+ file(COPY "examples" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
+
+-execute_process(COMMAND
+- ${PYTHON_EXECUTABLE} -c
+- "from distutils.sysconfig import get_python_lib; print(get_python_lib())"
+-OUTPUT_VARIABLE PYTHON_MODULE_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
++if(NOT DEFINED PYTHON_MODULE_PATH)
++ execute_process(COMMAND
++ ${PYTHON_EXECUTABLE} -c
++ "from distutils.sysconfig import get_python_lib; print(get_python_lib())"
++ OUTPUT_VARIABLE PYTHON_MODULE_PATH OUTPUT_STRIP_TRAILING_WHITESPACE)
++endif()
+
+ install( FILES "${CMAKE_CURRENT_BINARY_DIR}/_${PYTHON_SWIG_BINDING}.so" DESTINATION ${PYTHON_MODULE_PATH} )
+ install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${PYTHON_SWIG_BINDING}.py" DESTINATION ${PYTHON_MODULE_PATH} )
diff --git a/test/mocks/netconf-pnp-simulator/engine/supervisord.conf b/test/mocks/netconf-pnp-simulator/engine/supervisord.conf
new file mode 100644
index 000000000..9e6fd4282
--- /dev/null
+++ b/test/mocks/netconf-pnp-simulator/engine/supervisord.conf
@@ -0,0 +1,45 @@
+#-
+# ============LICENSE_START=======================================================
+# Copyright (C) 2020 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+
+[supervisord]
+nodaemon=true
+logfile=/dev/null
+logfile_maxbytes=0
+loglevel=debug
+
+[program:sysrepod]
+command=/opt/bin/sysrepod -d -l3
+autorestart=true
+redirect_stderr=true
+priority=1
+
+[program:sysrepo-plugind]
+command=/opt/bin/sysrepo-plugind -d -l3
+autorestart=true
+redirect_stderr=true
+priority=2
+
+[program:netopeer2-server]
+command=/opt/bin/netopeer2-server -d -v3
+autorestart=true
+redirect_stderr=true
+priority=3
+
+[include]
+files=/etc/supervisord.d/*.conf