aboutsummaryrefslogtreecommitdiffstats
path: root/test/mocks/pnfsimulator/netconfsimulator/netconf
diff options
context:
space:
mode:
authorWojciech Sliwka <wojciech.sliwka@nokia.com>2019-07-10 13:48:52 +0200
committerGary Wu <gary.wu@futurewei.com>2019-07-19 17:18:49 +0000
commit032ff22ef20b59950a8b5fae8d2ba6d03e93ac93 (patch)
tree98058dcf2726b3f432cecffc42bd7cbccca3555e /test/mocks/pnfsimulator/netconfsimulator/netconf
parent79c9b78adb7fbc943fd2aee7d333fd3cadf5b8f3 (diff)
Opensourcing new version of Simulator
Additional info in README.md Issue-ID: INT-1134 Signed-off-by: Wojciech Sliwka <wojciech.sliwka@nokia.com> Change-Id: I06d41fd3f361b7a451b30b702882810e4136a129
Diffstat (limited to 'test/mocks/pnfsimulator/netconfsimulator/netconf')
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/__init__.py19
-rwxr-xr-xtest/mocks/pnfsimulator/netconfsimulator/netconf/initialize_netopeer.sh54
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/load_server_certs.xml40
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/newmodel.xml24
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/newmodel.yang9
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/pnf-simulator.data.xml24
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/pnf-simulator.yang9
-rwxr-xr-xtest/mocks/pnfsimulator/netconfsimulator/netconf/set-up-xmls.py153
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/test_yang_loader_server.py121
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/tls_listen.xml48
-rw-r--r--test/mocks/pnfsimulator/netconfsimulator/netconf/yang_loader_server.py172
11 files changed, 673 insertions, 0 deletions
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')