diff options
39 files changed, 935 insertions, 11 deletions
@@ -3,3 +3,4 @@ **/target **/logs **/venv +**/__pycache__ diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..5aa7e46 --- /dev/null +++ b/.gitreview @@ -0,0 +1,6 @@ + + [gerrit] + host=gerrit.onap.org + port=29418 + project=integration/simulators/nf-simulator/netconf-server + defaultbranch=master @@ -1,10 +1,17 @@ FROM docker.io/sysrepo/sysrepo-netopeer2:latest COPY ./models /resources/models COPY ./scripts ./scripts +COPY ./src/python/netconf_server ./application/netconf_server +COPY ./src/python/netconf_server_application.py ./application/netconf_server_application.py +COPY ./src/python/requirements.txt ./application/requirements.txt +COPY ./src/python/setup.py ./application/setup.py + +RUN apt-get update && apt-get install -y python3 python3-pip && pip3 install -e ./application/ ENV ENABLE_TLS=false RUN mkdir -p /resources/certs && \ ./scripts/generate-certificates.sh /resources/certs +RUN mkdir /logs ENTRYPOINT ["./scripts/set-up-netopeer.sh", "/resources/models", "/resources/certs"] @@ -27,9 +27,12 @@ and **TLS, be default exposed on port 6513**. ### custom models new models are loaded on the image start up from catalog `/resources/models`. -Be default this directory contains `pnf-simulator.yang` model. +Be default this directory contains `pnf-simulator.yang` model and +default configuration file for config change subscription `models-configuration.ini`. +This file is required for application to start. +More about that file in ***config change subscription*** section. In order to load custom models on start up, -volume with models, should be mounted to `/resources/models` directory. +volume with models and configuration file, should be mounted to `/resources/models` directory. It can be done in docker-compose, by putting `./path/to/cusom/models:/resources/models` in *volumes* section. @@ -40,7 +43,7 @@ In order to enable TLS, that environment variable need to be set to `true` It can be done in docker-compose, by putting `ENABLE_TLS=true` in *environment* section. -#### Custom certificate +#### custom certificate When TLS is enabled server will use auto generated certificates, be default. That certificates are generated during image build and are located in `/resources/certs` directory. @@ -54,6 +57,25 @@ In this volume following files are required, **named accordingly**: - **server.key** - server private key - **server_pub.key** - server public key +### config change subscription +Netconf server image run python application on the startup. +More on that application in README located in `src/python` directory. +This application allows subscribing on config change for selected models. +Data about witch models change should be subscribed to, are located in config file. +Config file must be located in models directory, on the image that directory is `/resources/models`. +For more data about models go back to ***custom models*** section. +Configuration file should be called `models-configuration.ini`, +although that can be changed, by setting environment variable `MODELS_CONFIGURATION_FILE_NAME`. +Configuration file should be formatted in proper way: +```ini +[SUBSCRIPTION] +models = my-model-1,my-model-2,my-model-3 +``` +Custom modules, to subscribe to, should be separated with comma. + +### logging +Netconf server print all logs on to the console. +Logs from python application are also stored in file `/logs/netconf_saver.log` ## Development guide ### building image @@ -62,9 +84,10 @@ In order to build image mvn command can be run: mvn clean install -p docker ``` -### image building process +### Image building process To build image, Dockerfile is used. -During an image building: + +#### During an image building: - catalog `scripts` is copied to image home directory. That catalog contains all scripts needed for installing initial models and configuring TLS. @@ -75,6 +98,13 @@ During an image building: stored in `/resources/certs` directory. - set-up-netopeer script is set to be run on image start up. +#### During an image startup: + - install all models from `/resources/models` directory + - if flag `ENABLE_TLS` is set to true, configure TLS + - run python netconf server application in detach mode. + More on that application in README located in `src/python` directory. + + ### change log This project contains `Changeloge.md` file. Please update this file when change is made, diff --git a/docker-compose.yml b/docker-compose.yml index d9afeac..a4080dc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: netconf-server: container_name: netconf-server - image: onap/org.onap.integration.simulators.netconf-server:latest + image: onap/org.onap.integration.nfsimulator.netconfserver:latest environment: - ENABLE_TLS=true ports: diff --git a/models/models-configuration.ini b/models/models-configuration.ini new file mode 100644 index 0000000..0cf2f0b --- /dev/null +++ b/models/models-configuration.ini @@ -0,0 +1,2 @@ +[SUBSCRIPTION] +models = pnf-simulator diff --git a/models/pnf-simulator.data.xml b/models/pnf-simulator.data.xml new file mode 100644 index 0000000..56537c3 --- /dev/null +++ b/models/pnf-simulator.data.xml @@ -0,0 +1,24 @@ +<!-- + ============LICENSE_START======================================================= + Simulator + ================================================================================ + Copyright (C) 2021 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> @@ -46,6 +46,33 @@ <docker-image.name.prefix>org.onap.integration.nfsimulator</docker-image.name.prefix> </properties> + <build> + <finalName>${project.artifactId}-${project.version}</finalName> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.2.1</version> + <executions> + <execution> + <id>python-test</id> + <phase>test</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <workingDirectory>./src/python</workingDirectory> + <executable>tox</executable> + <arguments> + <argument>.</argument> + </arguments> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + <profiles> <profile> <id>docker</id> diff --git a/scripts/generate-certificates.sh b/scripts/generate-certificates.sh index 01eaa8c..788d9ab 100755 --- a/scripts/generate-certificates.sh +++ b/scripts/generate-certificates.sh @@ -1,7 +1,7 @@ #!/bin/sh ### # ============LICENSE_START======================================================= -# Netconf-server +# Netconf Server # ================================================================================ # Copyright (C) 2021 Nokia. All rights reserved. # ================================================================================ diff --git a/scripts/install-all-module-from-directory.sh b/scripts/install-all-module-from-directory.sh index 6644715..efa54db 100755 --- a/scripts/install-all-module-from-directory.sh +++ b/scripts/install-all-module-from-directory.sh @@ -1,7 +1,7 @@ #!/bin/bash ### # ============LICENSE_START======================================================= -# Netconf-server +# Netconf Server # ================================================================================ # Copyright (C) 2021 Nokia. All rights reserved. # ================================================================================ diff --git a/scripts/install-tls-with-custom-certificates.sh b/scripts/install-tls-with-custom-certificates.sh index 545d01b..c499e15 100755 --- a/scripts/install-tls-with-custom-certificates.sh +++ b/scripts/install-tls-with-custom-certificates.sh @@ -1,7 +1,7 @@ #!/bin/bash ### # ============LICENSE_START======================================================= -# Netconf-server +# Netconf Server # ================================================================================ # Copyright (C) 2021 Nokia. All rights reserved. # ================================================================================ diff --git a/scripts/run-netconf-server-application.sh b/scripts/run-netconf-server-application.sh new file mode 100755 index 0000000..5cc51f4 --- /dev/null +++ b/scripts/run-netconf-server-application.sh @@ -0,0 +1,29 @@ +#!/bin/bash +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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========================================================= +### + +if [ "$#" -eq 2 ]; then + + echo "Starting NETCONF server" + python3 ./application/netconf_server_application.py $1/$2 & + +else + echo "Missing argument: path to file with models to subscribe to." +fi diff --git a/scripts/set-up-netopeer.sh b/scripts/set-up-netopeer.sh index f6308d0..e7b4f76 100755 --- a/scripts/set-up-netopeer.sh +++ b/scripts/set-up-netopeer.sh @@ -1,7 +1,7 @@ #!/bin/bash ### # ============LICENSE_START======================================================= -# Netconf-server +# Netconf Server # ================================================================================ # Copyright (C) 2021 Nokia. All rights reserved. # ================================================================================ @@ -24,6 +24,7 @@ if [ "$#" -ge 1 ]; then ## Set up variable SCRIPTS_DIR=$PWD/"$(dirname $0)" enable_tls=${ENABLE_TLS:-false} + models_configuration_file_name=${MODELS_CONFIGURATION_FILE_NAME:-models-configuration.ini} ## Install all modules from given directory $SCRIPTS_DIR/install-all-module-from-directory.sh $1 @@ -38,6 +39,9 @@ if [ "$#" -ge 1 ]; then fi fi + ## Run netconf server application + $SCRIPTS_DIR/run-netconf-server-application.sh $1 $models_configuration_file_name + ## Run sysrepo supervisor /usr/bin/supervisord -c /etc/supervisord.conf diff --git a/scripts/tls/set-up-tls-certificates.py b/scripts/tls/set-up-tls-certificates.py index 16934b5..8a22ebf 100755 --- a/scripts/tls/set-up-tls-certificates.py +++ b/scripts/tls/set-up-tls-certificates.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ### # ============LICENSE_START======================================================= -# Netconf-server +# Netconf Server # ================================================================================ # Copyright (C) 2021 Nokia. All rights reserved. # ================================================================================ diff --git a/src/python/README.md b/src/python/README.md new file mode 100644 index 0000000..90906c6 --- /dev/null +++ b/src/python/README.md @@ -0,0 +1,17 @@ +# Netconf Server Python Application +This application is providing core Netconf Server capabilities. +It is started in detached mode on image startup. + +Application capabilities: + - Subscribing on config change per model. + - Models to subscribe to are loaded from configuration file, + provided as application parameter. + - When configuration of one of models change + information about change are logged + + +## Testing +Tox file with pytest are used fo testing. + +## Logging +Application prints logs on to the console and to file `/logs/netconf_saver.log` diff --git a/src/python/netconf_server/__init__.py b/src/python/netconf_server/__init__.py new file mode 100644 index 0000000..eeb06d5 --- /dev/null +++ b/src/python/netconf_server/__init__.py @@ -0,0 +1,19 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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/src/python/netconf_server/netconf_server.py b/src/python/netconf_server/netconf_server.py new file mode 100644 index 0000000..b790604 --- /dev/null +++ b/src/python/netconf_server/netconf_server.py @@ -0,0 +1,39 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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 + +from netconf_server.sysrepo_interface.config_change_data import ConfigChangeData + +logger = logging.getLogger("netconf_saver") + + +class NetconfServer(object): + + def __init__(self, subscriptions: list): + self.subscriptions = subscriptions + + def run(self, session): + for subscription in self.subscriptions: + subscription.callback_function = self.__on_module_configuration_change + subscription.subscribe_on_model_change(session) + + @staticmethod + def __on_module_configuration_change(config_change_data: ConfigChangeData): + logger.info("Received module changed: %s , %s " % (config_change_data.event, config_change_data.changes)) diff --git a/src/python/netconf_server/netconf_server_factory.py b/src/python/netconf_server/netconf_server_factory.py new file mode 100644 index 0000000..28297ad --- /dev/null +++ b/src/python/netconf_server/netconf_server_factory.py @@ -0,0 +1,40 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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 + +from netconf_server.netconf_server import NetconfServer +from netconf_server.sysrepo_interface.config_change_subscriber import ConfigChangeSubscriber + +logger = logging.getLogger("netconf_saver") + + +class NetconfServerFactory(object): + + def __init__(self, modules_to_subscribe_names: list): + self.modules_to_subscribe_names = modules_to_subscribe_names + + def create(self) -> NetconfServer: + subscriptions = list() + for module_name in self.modules_to_subscribe_names: + subscriptions.append( + ConfigChangeSubscriber(module_name) + ) + return NetconfServer(subscriptions) + diff --git a/src/python/netconf_server/sysrepo_configuration/__init__.py b/src/python/netconf_server/sysrepo_configuration/__init__.py new file mode 100644 index 0000000..eeb06d5 --- /dev/null +++ b/src/python/netconf_server/sysrepo_configuration/__init__.py @@ -0,0 +1,19 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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/src/python/netconf_server/sysrepo_configuration/sysrepo_configuration.py b/src/python/netconf_server/sysrepo_configuration/sysrepo_configuration.py new file mode 100644 index 0000000..fa48098 --- /dev/null +++ b/src/python/netconf_server/sysrepo_configuration/sysrepo_configuration.py @@ -0,0 +1,24 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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========================================================= +### + +class SysrepoConfiguration(object): + + def __init__(self, models_to_subscribe_to: list): + self.models_to_subscribe_to = models_to_subscribe_to diff --git a/src/python/netconf_server/sysrepo_configuration/sysrepo_configuration_loader.py b/src/python/netconf_server/sysrepo_configuration/sysrepo_configuration_loader.py new file mode 100644 index 0000000..dc7ac90 --- /dev/null +++ b/src/python/netconf_server/sysrepo_configuration/sysrepo_configuration_loader.py @@ -0,0 +1,58 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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 os +from configparser import ConfigParser + +from netconf_server.sysrepo_configuration.sysrepo_configuration import SysrepoConfiguration + +MODELS_LIST_TAG = "models" +SUBSCRIPTION_TAG = "SUBSCRIPTION" + +logger = logging.getLogger("sysrep_configuration_loader") + + +class SysrepoConfigurationLoader(object): + + # configuration_file must be in .ini format + @staticmethod + def load_configuration(configuration_file: str) -> SysrepoConfiguration: + if os.path.isfile(configuration_file): + config_object = ConfigParser() + config_object.read(configuration_file) + if SUBSCRIPTION_TAG in config_object and MODELS_LIST_TAG in config_object[SUBSCRIPTION_TAG]: + logger.info("Loading configuration from file %s" % configuration_file) + models_to_subscribe_to = config_object[SUBSCRIPTION_TAG][MODELS_LIST_TAG].split(",") + return SysrepoConfiguration(models_to_subscribe_to) + else: + logger.warning("Loading configuration failed, %s is not valid configuration file" % configuration_file) + raise ConfigLoadingException( + "Loading sysrepo configuration have failed, %s is not correct config file" % configuration_file + ) + else: + logger.warning("Loading configuration failed, %s does not exist or is not a file" % configuration_file) + raise ConfigLoadingException( + "Loading sysrepo configuration have failed, %s is not valid file" % configuration_file + ) + + +class ConfigLoadingException(Exception): + pass diff --git a/src/python/netconf_server/sysrepo_interface/__init__.py b/src/python/netconf_server/sysrepo_interface/__init__.py new file mode 100644 index 0000000..eeb06d5 --- /dev/null +++ b/src/python/netconf_server/sysrepo_interface/__init__.py @@ -0,0 +1,19 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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/src/python/netconf_server/sysrepo_interface/config_change_data.py b/src/python/netconf_server/sysrepo_interface/config_change_data.py new file mode 100644 index 0000000..8e329b5 --- /dev/null +++ b/src/python/netconf_server/sysrepo_interface/config_change_data.py @@ -0,0 +1,28 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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========================================================= +### + +class ConfigChangeData(object): + + def __init__(self, event: str, req_id: int, changes: list): + self.event = event + self.req_id = req_id + self.changes = changes + + diff --git a/src/python/netconf_server/sysrepo_interface/config_change_subscriber.py b/src/python/netconf_server/sysrepo_interface/config_change_subscriber.py new file mode 100644 index 0000000..faa8254 --- /dev/null +++ b/src/python/netconf_server/sysrepo_interface/config_change_subscriber.py @@ -0,0 +1,49 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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 + +from netconf_server.sysrepo_interface.config_change_data import ConfigChangeData + +logger = logging.getLogger("sysrep_config_change_subscriber") + + +class ConfigChangeSubscriber(object): + + def __init__(self, module_name: str, callback_function: callable = None): + self.module_name = module_name + if callback_function is None: + self.callback_function = self.default_callback + else: + self.callback_function = callback_function + + def subscribe_on_model_change(self, session): + logger.info("Subscribing on config change for module %s" % self.module_name) + session.subscribe_module_change( + self.module_name, None, self.on_module_have_changed, asyncio_register=True + ) + + async def on_module_have_changed(self, event: str, req_id: int, changes: list, private_data: any): + logger.debug("Module changed: %s (request ID %s)" % (event, req_id)) + self.callback_function(ConfigChangeData(event, req_id, changes)) + + @staticmethod + def default_callback(config_change_data: ConfigChangeData): + logger.info("Received module changed: %s , %s " % (config_change_data.event, config_change_data.changes)) diff --git a/src/python/netconf_server/sysrepo_interface/sysrepo_client.py b/src/python/netconf_server/sysrepo_interface/sysrepo_client.py new file mode 100644 index 0000000..fcd29e2 --- /dev/null +++ b/src/python/netconf_server/sysrepo_interface/sysrepo_client.py @@ -0,0 +1,29 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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 sysrepo + + +class SysrepoClient(object): + + @staticmethod + def run_in_session(method_to_run: callable, *extra_args): + with sysrepo.SysrepoConnection() as connection: + with connection.start_session() as session: + method_to_run(session, *extra_args) diff --git a/src/python/netconf_server_application.py b/src/python/netconf_server_application.py new file mode 100644 index 0000000..e112490 --- /dev/null +++ b/src/python/netconf_server_application.py @@ -0,0 +1,55 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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 asyncio +import sys +import logging + +from netconf_server.netconf_server import NetconfServer +from netconf_server.netconf_server_factory import NetconfServerFactory +from netconf_server.sysrepo_configuration.sysrepo_configuration_loader import SysrepoConfigurationLoader, \ + ConfigLoadingException +from netconf_server.sysrepo_interface.sysrepo_client import SysrepoClient + +logging.basicConfig( + handlers=[logging.StreamHandler(), logging.FileHandler("/logs/netconf_saver.log")], + level=logging.DEBUG +) +logger = logging.getLogger("netconf_saver") + + +def run_server_forever(session, server: NetconfServer): + server.run(session) + asyncio.get_event_loop().run_forever() + + +def create_configured_server() -> NetconfServer: + configuration = SysrepoConfigurationLoader.load_configuration(sys.argv[1]) + return NetconfServerFactory(configuration.models_to_subscribe_to).create() + + +if __name__ == "__main__": + if len(sys.argv) >= 2: + try: + netconf_server = create_configured_server() + SysrepoClient().run_in_session(run_server_forever, netconf_server) + except ConfigLoadingException: + logger.error("File to load configuration from file %s" % sys.argv[1]) + else: + logger.error("Missing path to file with configuration argument required to start netconf server.") diff --git a/src/python/requirements.txt b/src/python/requirements.txt new file mode 100644 index 0000000..ee6c404 --- /dev/null +++ b/src/python/requirements.txt @@ -0,0 +1,21 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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========================================================= +### + +sysrepo==0.4.2 diff --git a/src/python/setup.py b/src/python/setup.py new file mode 100644 index 0000000..394143f --- /dev/null +++ b/src/python/setup.py @@ -0,0 +1,32 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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 setuptools + +with open('requirements.txt') as f: + required = f.read().splitlines() + +setuptools.setup( + name="netconf-server", + version="1.0.0", + description="Application that exposes REST API for managing sysrepo", + packages=setuptools.find_packages(include=['netconf_server', 'netconf_server.*']), + classifiers=["Programming Language :: Python :: 3.6"], + install_requires=required +) diff --git a/src/python/test-requirements.txt b/src/python/test-requirements.txt new file mode 100644 index 0000000..4c3f573 --- /dev/null +++ b/src/python/test-requirements.txt @@ -0,0 +1,22 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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========================================================= +### + +pytest==6.2.2 + diff --git a/src/python/tests/__init__.py b/src/python/tests/__init__.py new file mode 100644 index 0000000..eeb06d5 --- /dev/null +++ b/src/python/tests/__init__.py @@ -0,0 +1,19 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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/src/python/tests/mocs/__init__.py b/src/python/tests/mocs/__init__.py new file mode 100644 index 0000000..eeb06d5 --- /dev/null +++ b/src/python/tests/mocs/__init__.py @@ -0,0 +1,19 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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/src/python/tests/mocs/mocked_session.py b/src/python/tests/mocs/mocked_session.py new file mode 100644 index 0000000..d7adb1b --- /dev/null +++ b/src/python/tests/mocs/mocked_session.py @@ -0,0 +1,34 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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 asyncio + + +class MockedSession(object): + + def __init__(self): + self.__callback = None + + def subscribe_module_change(self, module_name, _, on_module_have_changed, asyncio_register=True): + self.__callback = on_module_have_changed + pass + + def call_config_changed(self): + loop = asyncio.get_event_loop() + loop.run_until_complete(self.__callback('event', 'req_id', 'changes', 'private_data')) diff --git a/src/python/tests/netconf_server/__init__.py b/src/python/tests/netconf_server/__init__.py new file mode 100644 index 0000000..eeb06d5 --- /dev/null +++ b/src/python/tests/netconf_server/__init__.py @@ -0,0 +1,19 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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/src/python/tests/netconf_server/sysrepo_configuration/__init__.py b/src/python/tests/netconf_server/sysrepo_configuration/__init__.py new file mode 100644 index 0000000..eeb06d5 --- /dev/null +++ b/src/python/tests/netconf_server/sysrepo_configuration/__init__.py @@ -0,0 +1,19 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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/src/python/tests/netconf_server/sysrepo_configuration/test_sysrepo_configuration_loader.py b/src/python/tests/netconf_server/sysrepo_configuration/test_sysrepo_configuration_loader.py new file mode 100644 index 0000000..e5462e4 --- /dev/null +++ b/src/python/tests/netconf_server/sysrepo_configuration/test_sysrepo_configuration_loader.py @@ -0,0 +1,84 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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 +import os + +from netconf_server.sysrepo_configuration.sysrepo_configuration_loader import \ + SysrepoConfigurationLoader, ConfigLoadingException + +test_config_file_name = "./test-subscription-configuration.ini" +test_config_file_content = '[SUBSCRIPTION]\nmodels = test-module,test-module-2\n' + +test_wrong_config_file_name = "./test-subscription-wrong-configuration.ini" +test_wrong_config_file_content = '[SUBSCRIPTION]\n' + +test_non_existing_config_file_name = "./test-subscription-non-existing-configuration.ini" + + +class TestSysrepoConfigurationLoader(unittest.TestCase): + + def test_should_load_configuration_from_file(self): + # when + config = SysrepoConfigurationLoader.load_configuration(test_config_file_name) + + # then + self.assertEqual(config.models_to_subscribe_to, ["test-module", "test-module-2"]) + + def test_should_raise_exception_if_given_configuration_file_is_wrong(self): + # then + with self.assertRaises(ConfigLoadingException): + # when + SysrepoConfigurationLoader.load_configuration(test_wrong_config_file_name) + + def test_should_raise_exception_if_given_configuration_file_does_not_exist(self): + # then + with self.assertRaises(ConfigLoadingException): + # when + SysrepoConfigurationLoader.load_configuration(test_non_existing_config_file_name) + + @classmethod + def setUpClass(cls): + cls.__create_configuration_file() + cls.__create_wrong_configuration_file() + + @classmethod + def tearDownClass(cls): + cls.__remove_configuration_files() + cls.__remove_wrong_configuration_files() + + @staticmethod + def __create_configuration_file(): + f = open(test_config_file_name, "a") + f.write(test_config_file_content) + f.close() + + @staticmethod + def __remove_configuration_files(): + os.remove(test_config_file_name) + + @staticmethod + def __create_wrong_configuration_file(): + f = open(test_wrong_config_file_name, "a") + f.write(test_wrong_config_file_content) + f.close() + + @staticmethod + def __remove_wrong_configuration_files(): + os.remove(test_wrong_config_file_name) diff --git a/src/python/tests/netconf_server/sysrepo_interface/__init__.py b/src/python/tests/netconf_server/sysrepo_interface/__init__.py new file mode 100644 index 0000000..eeb06d5 --- /dev/null +++ b/src/python/tests/netconf_server/sysrepo_interface/__init__.py @@ -0,0 +1,19 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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/src/python/tests/netconf_server/sysrepo_interface/test_config_change_subscriber.py b/src/python/tests/netconf_server/sysrepo_interface/test_config_change_subscriber.py new file mode 100644 index 0000000..9817ba4 --- /dev/null +++ b/src/python/tests/netconf_server/sysrepo_interface/test_config_change_subscriber.py @@ -0,0 +1,46 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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.mock import MagicMock + +from netconf_server.sysrepo_interface.config_change_data import ConfigChangeData +from netconf_server.sysrepo_interface.config_change_subscriber import ConfigChangeSubscriber +from tests.mocs.mocked_session import MockedSession + + +class TestConfigChangeSubscriber(unittest.TestCase): + + @staticmethod + def __test_callback(config_change_data: ConfigChangeData): + pass + + def test_should_create_subscriber_and_call_callback_when_session_detects_change(self): + # given + self.__test_callback = MagicMock() + subscriber = ConfigChangeSubscriber("test", self.__test_callback) + session = MockedSession() + subscriber.subscribe_on_model_change(session) + self.__test_callback.assert_not_called() + + # when + session.call_config_changed() + + # then + self.__test_callback.assert_called_once() diff --git a/src/python/tests/netconf_server/test_netconf_server.py b/src/python/tests/netconf_server/test_netconf_server.py new file mode 100644 index 0000000..6306dd9 --- /dev/null +++ b/src/python/tests/netconf_server/test_netconf_server.py @@ -0,0 +1,53 @@ +### +# ============LICENSE_START======================================================= +# Netconf Server +# ================================================================================ +# Copyright (C) 2021 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.mock import MagicMock + +from netconf_server.netconf_server_factory import NetconfServerFactory +from tests.mocs.mocked_session import MockedSession + + +class TestNetconfServer(unittest.TestCase): + + def test_should_create_and_run_netconf_server_with_one_model(self): + # given + modules_to_subscribe_names = ["test"] + server = NetconfServerFactory(modules_to_subscribe_names).create() + session = MockedSession() + session.subscribe_module_change = MagicMock() + + # when + server.run(session) + + # then + session.subscribe_module_change.assert_called_once() + + def test_should_create_and_run_netconf_server_with_multiple_models(self): + # given + modules_to_subscribe_names = ["test", "test2", "test3"] + server = NetconfServerFactory(modules_to_subscribe_names).create() + session = MockedSession() + session.subscribe_module_change = MagicMock() + + # when + server.run(session) + + # then + self.assertEqual(session.subscribe_module_change.call_count, 3) diff --git a/src/python/tox.ini b/src/python/tox.ini new file mode 100644 index 0000000..dd76991 --- /dev/null +++ b/src/python/tox.ini @@ -0,0 +1,11 @@ +[tox] +envlist = py36 +skipsdist = true + +[testenv] +commands = pytest +basepython = python3 +deps = -r test-requirements.txt + +[testenv:pytest] +commands = pytest -v |