From 7dbf479029ba8bc528fb61a40ab2647489da28e9 Mon Sep 17 00:00:00 2001 From: Alex Stancu Date: Mon, 11 May 2020 12:49:32 +0300 Subject: [PATCH] Add simulator enhancements. Add configurable number of SSH and TLS connections. Add IPv6 support. Add K8S deployment support. Issue-ID: SIM-26 Change-Id: If010ce3cf4e2d4de1d55db1fd29fef83d9d27839 Signed-off-by: Alex Stancu --- ntsimulator/README.md | 138 ++++- ntsimulator/deploy/nts-manager/Dockerfile | 30 +- ntsimulator/deploy/nts-manager/container-tag.yaml | 2 +- ntsimulator/deploy/nts-manager/docker_stats.sh | 56 ++ ntsimulator/deploy/o-ran-sc/o-ran-ru/Dockerfile | 7 +- .../deploy/o-ran-sc/o-ran-ru/container-tag.yaml | 2 +- .../deploy/o-ran-sc/o-ran-ru/stock_config.xml | 95 ---- ntsimulator/deploy/o-ran/ru-fh/Dockerfile | 7 +- ntsimulator/deploy/o-ran/ru-fh/container-tag.yaml | 2 +- ntsimulator/deploy/o-ran/ru-fh/stock_config.xml | 95 ---- ntsimulator/deploy/o-ran/ru-fh/supervisord.conf | 8 + ntsimulator/deploy/tls/enable_connections.sh | 64 +++ ntsimulator/deploy/tls/enable_tls.sh | 10 +- ntsimulator/deploy/tls/set_NTS_IP.sh | 43 ++ ntsimulator/deploy/x-ran/Dockerfile | 7 +- ntsimulator/deploy/x-ran/container-tag.yaml | 2 +- ntsimulator/deploy/x-ran/stock_config.xml | 95 ---- ntsimulator/inc/utils/utils.h | 9 +- .../src/ntsimulator-manager/ntsimulator-manager.c | 280 ++++++++- .../src/ntsimulator-manager/simulator-operations.c | 630 ++++++++++++++++++--- .../src/ntsimulator-manager/simulator-operations.h | 12 +- .../src/o-ran-notifications/o-ran-notifications.c | 22 +- ntsimulator/src/utils/utils.c | 283 ++++++++- ntsimulator/src/ves-messages/heartbeat.c | 22 +- .../nts-manager/network-topology-simulator.xml | 2 + .../nts-manager/network-topology-simulator.yang | 27 + 26 files changed, 1472 insertions(+), 478 deletions(-) create mode 100755 ntsimulator/deploy/nts-manager/docker_stats.sh delete mode 100644 ntsimulator/deploy/o-ran-sc/o-ran-ru/stock_config.xml delete mode 100644 ntsimulator/deploy/o-ran/ru-fh/stock_config.xml create mode 100755 ntsimulator/deploy/tls/enable_connections.sh create mode 100755 ntsimulator/deploy/tls/set_NTS_IP.sh delete mode 100644 ntsimulator/deploy/x-ran/stock_config.xml diff --git a/ntsimulator/README.md b/ntsimulator/README.md index a1c8d01..5ae5319 100644 --- a/ntsimulator/README.md +++ b/ntsimulator/README.md @@ -29,8 +29,10 @@ module: network-topology-simulator +--rw simulator-config | +--rw simulated-devices? uint32 | +--rw mounted-devices? uint32 + | +--rw ssh-connections? uint32 + | +--rw tls-connections? uint32 | +--rw notification-config - | | +--rw fault-notification-delay-period? uint32 + | | +--rw fault-notification-delay-period* uint32 | | +--rw ves-heartbeat-period? uint32 | | +--rw is-netconf-available? boolean | | +--rw is-ves-available? boolean @@ -54,12 +56,38 @@ module: network-topology-simulator | +--ro base-netconf-port? uint32 | +--ro cpu-usage? percent | +--ro mem-usage? uint32 + +--ro notification-count + | +--ro total-ves-notifications + | | +--ro normal? uint32 + | | +--ro warning? uint32 + | | +--ro minor? uint32 + | | +--ro major? uint32 + | | +--ro critical? uint32 + | +--ro total-netconf-notifications + | +--ro normal? uint32 + | +--ro warning? uint32 + | +--ro minor? uint32 + | +--ro major? uint32 + | +--ro critical? uint32 +--ro simulated-devices-list* [uuid] - +--ro uuid string - +--ro device-ip? string - +--ro device-port* uint32 - +--ro is-mounted? boolean - +--ro operational-state? operational-state-type + +--ro uuid string + +--ro device-ip? string + +--ro device-port* uint32 + +--ro is-mounted? boolean + +--ro operational-state? operational-state-type + +--ro notification-count + +--ro ves-notifications + | +--ro normal? uint32 + | +--ro warning? uint32 + | +--ro minor? uint32 + | +--ro major? uint32 + | +--ro critical? uint32 + +--ro netconf-notifications + +--ro normal? uint32 + +--ro warning? uint32 + +--ro minor? uint32 + +--ro major? uint32 + +--ro critical? uint32 rpcs: +---x restart-simulation @@ -71,9 +99,11 @@ module: network-topology-simulator ##### Configuration * **simulated-devices** - represents the number of simulated devices. The default value is 0, meaning that when the NTS is started, there are no simulated devices. When this value is increased to **n**, the NTS Manager starts docker containers in order to reach **n** simulated devices. If the value is decreased to **k**, the NTS Manager will remove docker containers, until the number of simulated devices reaches **k**; -* **mounted-devices** - represents the number of devices to be mounted to an ODL based SDN Controller. The same phylosophy as in the case of the previous leaf applies. If this number is increased, the number of ODL mountpoints increases. Else, the simulated devices are being unmounted from ODL. The number of mounted devices cannot exceed the number of simulated devices. The details about the ODL controller where to mount/unmount are given by the **controller-details** container; **Please note that this cannot be set to a value > 0 if the *ves-registration* leaf is set to 'True'**; For each simulated device, 10 NETCONF endpoints will be mounted (7 SSH + 3 TLS). See **NETCONF Endpoints** section for more details. +* **mounted-devices** - represents the number of devices to be mounted to an ODL based SDN Controller. The same phylosophy as in the case of the previous leaf applies. If this number is increased, the number of ODL mountpoints increases. Else, the simulated devices are being unmounted from ODL. The number of mounted devices cannot exceed the number of simulated devices. The details about the ODL controller where to mount/unmount are given by the **controller-details** container; **Please note that this cannot be set to a value > 0 if the *ves-registration* leaf is set to 'True'**; For each simulated device, a number of NETCONF endpoints will be mounted, according to the **ssh-connections** and **tls-connections** leafs. See **NETCONF Endpoints** section for more details; +* **ssh-connections** - represents the number of SSH endpoints to be exposed by each of the simulated devices. **Please note that the total number of SSH and TLS connections cannot exceed 100.** The default value is 1. **The value can only be changed when simulated-devices is 0**. +* **tls-connectioons** - represents the number of TLS endpoints to be exposed by each of the simulated devices. **Please note that the total number of SSH and TLS connections cannot exceed 100.** The default value is 0. **The value can only be changed when simulated-devices is 0**. * **notification-config** - this container groups the configuration about fault notification generation of each simulated device; -* **fault-notification-delay-period** - the amount of seconds between two generated fault notifications. For example, if this has a value of *10*, each simulated device will generate a **random** fault notification every *10* seconds; +* **fault-notification-delay-period** - the amount of seconds between two generated fault notifications. For example, if this has a value of *10*, each simulated device will generate a **random** fault notification every *10* seconds; **when this is set to 0, it will reset the notification counters for the VES and NETCONF notifications, which are exposed in the simulator-status**; * **ves-heartbeat-period** - the amount of seconds between VES heartbeat messages that can be generated by each simulated device. The details about the VES connection endpoint are given in the **ves-endpoint-details** container; * **is-netconf-available** - if set to 'True', NETCONF notifications will be sent when a random fault notification is generated, The NETCONF notification that is being sent is currently *o-ran-fm:alarm-notif*; if set to 'False', NETCONF notifications are not being sent out; * **is-ves-available** - if set to 'True', VES *faultNotification* messages will be sent when a random fault notification is generated; if set to 'False', VES *faultNotification* messages are not generated; @@ -93,7 +123,7 @@ module: network-topology-simulator * **ves-endpoint-username** - the username to be used when authenticating to the VES endpoint; * **ves-endpoint-password** - the password to be used when authenticating to the VES endpoint; * **ves-endpoint-certificate** - the certificate to be used when authenticating to the VES endpoint; -* **ves-registration** - if this is set to 'True' **when simulated devices are starting**, each simulated device will send out *pnfRegistration* VES messages to the configured VES endpoint; if this is set to 'False', *pnfRegistration* VES messages will not be sent out. **Please note that this cannot be set to 'True' is simulated devices are already mounted to ODL based SDN controller (mounted-devices leaf > 0)**; For each simulated device, 10 pnfRegistration VES messages will be sent out (7 SSH + 3 TLS). See **NETCONF Endpoints** section for more details. +* **ves-registration** - if this is set to 'True' **when simulated devices are starting**, each simulated device will send out *pnfRegistration* VES messages to the configured VES endpoint; if this is set to 'False', *pnfRegistration* VES messages will not be sent out. **Please note that this cannot be set to 'True' is simulated devices are already mounted to ODL based SDN controller (mounted-devices leaf > 0)**; For each simulated device, **ssh-connections + tls-connections** pnfRegistration VES messages will be sent out. See **NETCONF Endpoints** section for more details. ##### Status @@ -103,12 +133,18 @@ module: network-topology-simulator * **base-netconf-port** - the port that was used as a base when craeting simulated devices; * **cpu-usage** - the percentage of the CPU used currently by the simulation framework; * **mem-usage** - the amount of RAM used (in MB) currently by the simulation framework; +* **notification-count** - this container groups the details about the total number of notifications that were generated by the simulated devices; +* **total-ves-notifications** - this container groups the details about the total number of VES notifications that were generated by the simulated devices, grouped by severity; +* **total-netcnof-notifications** - this container groups the details about the total number of NETCONF notifications that were generated by the simulated devices - grouped by severity; * **simulated-devices-list** - this list contains the details about each simulated devices that is currently running; * **uuid** - the Universally Unique ID of the simulated device; * **device-ip** - the IP address of the simulated device; * **device-port** - the port of the simulated device, where the NETCONF connection is exposed; * **is-mounted** - boolean to show whether the device is currently mounted to an ODL based SDN controller; -* **operational-state** - the operational state of the current simulated device; it can be either *not-specified*, *created*, *running* or *exited*. +* **operational-state** - the operational state of the current simulated device; it can be either *not-specified*, *created*, *running* or *exited*; +* **notification-count** - this container groups the details about the number of notifications that were generated by this particular simulated device; +* **ves-notifications** - this container groups the details about the number of VES notifications that were generated by this simulated device, grouped by severity; +* **netconf-notifications** - this container groups the details about the number of NETCONF notifications that were generated by this simulated device - grouped by severity. ##### RPCs @@ -121,19 +157,29 @@ Each simulated device is represented as a docker container, inside which the NET #### NETCONF Endpoints -Each simulated device exposes **10 NETCONF endpoints**, on 10 consecutive ports. The first simulated device uses the 10 ports starting from the **NETCONF_BASE** environment variable used when starting the NTST Manager, while the nextt one uses the next 10 ports and so on and so forth. E.g. if the **NETCONF_BASE=50000** the first simulated device will expose ports from *50000* to *50009*, the second simulated device will expose ports from *50010* to *50019* etc. +Each simulated device exposes a number of NETCONF endpoints which represented by the sum of the **ssh-connections** and **tls-connections** leafs, on consecutive ports. The first simulated device uses the ports starting from the **NETCONF_BASE** environment variable used when starting the NTS Manager, while the next one uses the next ports and so on and so forth. E.g. if the **NETCONF_BASE=50000** and **ssh-connections=5** and **tls-connections=3**, the first simulated device will expose ports from *50000* to *50007*, the second simulated device will expose ports from *5008* to *50015* etc. -The first 7 connections exposed by a simulated device are **SSH** based. A NETCONF client can connect to the exposed endpoint using one of the SSH ports (e.g. 50000 to 50006) and the **username/password**: *netconf/netconf*. +The first **ssh-connections** ports exposed by a simulated device are **SSH** based. A NETCONF client can connect to the exposed endpoint using one of the SSH ports (e.g. 50000 to 50007, considering the previous example) and the **username/password**: *netconf/netconf*. -The last 3 connections exposed by a simulated device are **TLS** based. A NETCONF client can connect to the exposed endpoint using one of the TLS ports (e.g. 50007 to 50009), using a valid certificate and the **username**: *netconf*. +The last **tls-connections** ports exposed by a simulated device are **TLS** based. A NETCONF client can connect to the exposed endpoint using one of the TLS ports (e.g. 50006 to 50008, considering the previous example), using a valid certificate and the **username**: *netconf*. ## Usage ### Building the images -The `docker-build-manager.sh` script can be used to built the docker image associated with the NTS Manager. This will create a docker image named *ntsim_manager*, which will be used to start the simulation framework. Inside the docker image, port 830 will wait for connections for the NETCONF/YANG management interface. +The `docker-build-nts-manager.sh` script can be used to built the docker image associated with the NTS Manager. This will create a docker image named *ntsim_manager_light*, which will be used to start the simulation framework. Inside the docker image, port 830 will wait for connections for the NETCONF/YANG management interface. -The `docker-build-model.sh` script can be used to build the docker image associated with a simulated device. Currently, this will create a docker image named *ntsim_oran*, which will be used by the manager to start the docker containers for each simulated device. +The `docker-build-onf-core-model-1-2.sh` script can be used to build the docker image associated with a simulated device, exposing the ONF CoreModel version 1.2. + +The `docker-build-onf-core-model-1-4.sh` script can be used to build the docker image associated with a simulated device, exposing the ONF CoreModel version 1.4. + +The `docker-build-openroadm.sh` script can be used to build the docker image associated with a simulated device, exposing the OpenROADM models. + +The `docker-build-o-ran-device.sh` script can be used to build the docker image associated with a simulated device, exposing the O-RAN models. + +The `docker-build-o-ran-sc-o-ran-ru.sh` script can be used to build the docker image associated with a simulated device, exposing the O-RAN-SC models. + +The `docker-build-x-ran-device.sh*` script can be used to build the docker image associated with a simulated device, exposing the X-RAN models. ### Starting the NTS Manager @@ -146,10 +192,10 @@ services: image: "ntsim_manager:latest" container_name: NTS_Manager ports: - - "172.17.0.1:8300:830" + - "8300:830" volumes: - "/var/run/docker.sock:/var/run/docker.sock" - - "/path/to/simulator/folder/ntsimulator/scripts:/opt/dev/scripts" + - "/var/tmp/NTS_Manager:/opt/dev/scripts" - "/usr/bin/docker:/usr/bin/docker" labels: "NTS-manager": "" @@ -158,17 +204,26 @@ services: NETCONF_BASE: 50000 DOCKER_ENGINE_VERSION: "1.40" MODELS_IMAGE: "ntsim_oran" + VesHeartbeatPeriod: 0 + IsVesAvailable: "true" + IsNetconfAvailable: "true" + VesRegistration: "false" + VesEndpointPort: 8080 + VesEndpointIp: "172.17.0.1" + SshConnections: 1 + TlsConnections: 0 + K8S_DEPLOYMENT: "false" ``` * Port mapping: - * `"172.17.0.1:8300:830"` - this maps the *830* port from inside the docker container of the NTS Manager to the port *8300* from the host, and binds it to the docker IP address *172.17.0.1*: + * `"8300:830"` - this maps the *830* port from inside the docker container of the NTS Manager to the port *8300* from the host, and binds it to any IP address on the host: * Volumes - these map 3 important things: * the docker socket from the host is mapped inside the docker container: `/var/run/docker.sock:/var/run/docker.sock` - **please do not modify the path inside the container!**; - * the **scripts** folder from the cloned repository needs to be mapped inside the container: - `/path/to/simulator/folder/ntsimulator/scripts:/opt/dev/scripts` - **please do not modify the path inside the container!**; + * any folder from the host can be mapped to inside the docker container into othe **/opt/dev/scripts** folder: + `/var/tmp/NTS_Manager:/opt/dev/scripts` - **please do not modify the path inside the container!**; * the path to the docker executable needs to be mapped inside the container: `/usr/bin/docker:/usr/bin/docker` - **please do not modify the path inside the container!**; @@ -178,6 +233,15 @@ services: * **NETCONF_BASE** - this is the starting port used to expose NETCONF endpoints. Starting from this, each device will use 10 consecutive ports for its endpoints; * **DOCKER_ENGINE_VERSION** - this is the version of the *docker engine* installed currently on the host. This can be verified using `docker version` command in the host, and looking to the `API version: #.##` variable from the Server details. * **MODELS_IMAGE** - this represents the name of the docker image that represents the simulated device. The NTS Manager will start containers using this image, when starting simulated devices. + * **VesHeartbeatPeriod** - this can change the default value of the **ves-heartbeat-period** leaf used by the NTS Manager. + * **IsVesAvailable** - this can change the default value of the **is-ves-available** leaf used by the NTS Manager. + * **IsNetconfAvailable** - this can change the default value of the **is-netconf-available** leaf used by the NTS Manager. + * **VesRegistration** - this can change the default value of the **ves-registration** leaf used by the NTS Manager. + * **VesEndpointPort** - this can change the default value of the **ves-endpoint-port** leaf used by the NTS Manager. + * **VesEndpointIp** - this can change the default value of the **ves-endpoint-ip** leaf used by the NTS Manager. + * **SshConnections** - this can change the default value of the **ssh-connections** leaf used by the NTS Manager. + * **TlsConnections** - this can change the default value of the **tls-connections** leaf used by the NTS Manager. + * **K8S_DEPLOYMENT** - this value can be set to `true` when the user wants to the NTS Framework in a Kubernetes deployment. The default is `false`. After modifying the `docker-compose.yml` file with values specific to your host, the NTS Manager can be started by running the command `docker-compose up` from the **scripts** folder. @@ -186,10 +250,10 @@ After the NTS Manager is started, it will wait for connections on its NETCONF/YA Example of `docker ps` command result, after the NTS Manager was started: ``` -7ff723b7f794 ntsim_manager:latest "sh -c '/usr/bin/sup…" 5 days ago Up 5 days 172.17.0.1:8300->830/tcp NTS_Manager +7ff723b7f794 ntsim_manager:latest "sh -c '/usr/bin/sup…" 5 days ago Up 5 days 0.0.0.0:8300->830/tcp NTS_Manager ``` -### Using the NTST Manager +### Using the NTS Manager When the NTS Manager is started, its default configuration looks like this: @@ -197,6 +261,8 @@ When the NTS Manager is started, its default configuration looks like this: 0 0 + 1 + 0 0 0 @@ -222,7 +288,27 @@ This configuration can be altered by connecting to the NTS Manager with a NETCON ### Starting a simulated device -Example of starting **one** simulated device: +Example RPC for starting **one** simulated device: + +```xml + + + + + + + + + 1 + + + + + + + +``` + If the leaf `1` will be set to a value of **1**, the NTS Manager will start a new docker container. We can verify that this was successfull by running the `docker ps` command. The results will look like this: @@ -230,8 +316,6 @@ If the leaf `1` will be set to a value of c18eb7a362f5 ntsim_oran "sh -c '/usr/bin/sup…" 4 days ago Up 4 days 172.17.0.1:50000->830/tcp, 172.17.0.1:50001->831/tcp, 172.17.0.1:50002->832/tcp, 172.17.0.1:50003->833/tcp, 172.17.0.1:50004->834/tcp, 172.17.0.1:50005->835/tcp, 172.17.0.1:50006->836/tcp, 172.17.0.1:50007->837/tcp, 172.17.0.1:50008->838/tcp, 172.17.0.1:50009->839/tcp reverent_bhabha ``` -We can see that the simulated device has 10 NETCONF Endpoints listening for connections. The first 7 (50000 to 50006) are SSH connections, while the last 3 (50007 to 50009) are TLS connections. - ## Troubleshooting @@ -242,7 +326,7 @@ If, after setting the leaf `1` to a value Example of a result of such a command: ``` -ntsim_oran latest 57b065de4458 4 days ago 785MB +ntsim_oran_light latest 57b065de4458 4 days ago 186MB ``` -This means that `MODELS_IMAGE: "ntsim_oran:latest"` can be used as an environment variable when starting the NTS Manager. \ No newline at end of file +This means that `MODELS_IMAGE: "ntsim_oran_light:latest"` can be used as an environment variable when starting the NTS Manager. \ No newline at end of file diff --git a/ntsimulator/deploy/nts-manager/Dockerfile b/ntsimulator/deploy/nts-manager/Dockerfile index bff4bd8..2396a5d 100644 --- a/ntsimulator/deploy/nts-manager/Dockerfile +++ b/ntsimulator/deploy/nts-manager/Dockerfile @@ -2,12 +2,6 @@ FROM ubuntu:18.04 AS builder LABEL maintainer="alexandru.stancu@highstreet-technologies.com" -ENV NETCONF_BASE=40000 -ENV NTS_IP="127.0.0.1" -ENV SCRIPTS_DIR=/opt/dev/scripts -ENV DOCKER_ENGINE_VERSION="1.40" -ENV MODELS_IMAGE="ntsim_oran" - RUN \ apt-get update && apt-get install -y \ # general tools @@ -43,12 +37,15 @@ RUN \ echo "netconf:netconf" | chpasswd # generate ssh keys for netconf user +USER netconf RUN \ mkdir -p /home/netconf/.ssh && \ ssh-keygen -A && \ ssh-keygen -t dsa -P '' -f /home/netconf/.ssh/id_dsa && \ - cat /home/netconf/.ssh/id_dsa.pub > /home/netconf/.ssh/authorized_keys && \ + cat /home/netconf/.ssh/id_dsa.pub > /home/netconf/.ssh/authorized_keys #echo "Host *\n StrictHostKeyChecking accept-new" >> /home/netconf/.ssh/config +USER root +RUN \ echo " StrictHostKeyChecking no" >> /etc/ssh/ssh_config && \ mkdir -p /root/.ssh && \ cat /home/netconf/.ssh/id_dsa.pub > /root/.ssh/authorized_keys @@ -136,20 +133,35 @@ ENV NTS_IP="127.0.0.1" ENV SCRIPTS_DIR=/opt/dev/scripts ENV DOCKER_ENGINE_VERSION="1.40" ENV MODELS_IMAGE="ntsim_oran" +ENV VesHeartbeatPeriod=0 +ENV IsVesAvailable=true +ENV IsNetconfAvailable=true +ENV VesRegistration=false +ENV VesEndpointPort=8080 +ENV VesEndpointIp="172.17.0.1" +ENV SshConnections=1 +ENV TlsConnections=0 +ENV K8S_DEPLOYMENT=false +ENV CONTAINER_NAME="ntsim" RUN \ - apt-get update && apt-get install -y supervisor + apt-get update && apt-get install -y supervisor bc # add netconf user RUN \ adduser --system netconf && \ echo "netconf:netconf" | chpasswd +USER netconf # generate ssh keys for netconf user RUN \ mkdir -p /home/netconf/.ssh COPY --from=builder /home/netconf/.ssh /home/netconf/.ssh + +USER root +COPY --from=builder /etc/ssh /etc/ssh +COPY --from=builder /root/.ssh /root/.ssh COPY --from=builder /usr/local/lib /usr/local/lib COPY --from=builder /usr/local/bin /usr/local/bin COPY --from=builder /usr/local/include /usr/local/include @@ -202,6 +214,8 @@ COPY --from=builder /usr/local/share/libnetconf2 /usr/local/share/libnetconf2 RUN ldconfig COPY ./deploy/nts-manager/supervisord.conf /etc/supervisord.conf + +COPY ./deploy/nts-manager/docker_stats.sh /opt/dev/docker_stats.sh ARG BUILD_DATE LABEL build-date=$BUILD_DATE diff --git a/ntsimulator/deploy/nts-manager/container-tag.yaml b/ntsimulator/deploy/nts-manager/container-tag.yaml index 16a8dcd..38caae4 100644 --- a/ntsimulator/deploy/nts-manager/container-tag.yaml +++ b/ntsimulator/deploy/nts-manager/container-tag.yaml @@ -1,2 +1,2 @@ --- -tag: 0.1.2 +tag: 0.2.1 diff --git a/ntsimulator/deploy/nts-manager/docker_stats.sh b/ntsimulator/deploy/nts-manager/docker_stats.sh new file mode 100755 index 0000000..317d3ce --- /dev/null +++ b/ntsimulator/deploy/nts-manager/docker_stats.sh @@ -0,0 +1,56 @@ +#!/bin/bash +################################################################################ +# +# Copyright 2020 highstreet technologies GmbH and others +# +# 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 script is used to complete the output of the docker stats command. +# The docker stats command does not compute the total amount of resources (RAM or CPU) + +# Get the output of the docker stat command. Will be displayed at the end +# Without modifying the special variable IFS the ouput of the docker stats command won't have + +CPU_CORES=`nproc` +# the new lines thus resulting in a failure when using awk to process each line +IFS=; +mapfile -t DOCKER_PS_RESULT < <(/usr/bin/docker ps --all --format "{{.ID}}" --filter "label=NTS_Manager=$1") + +CONTAINERS="" + +if [ ${#DOCKER_PS_RESULT[@]} -gt 0 ] +then + + for container in "${DOCKER_PS_RESULT[@]}" + do + CONTAINERS="$CONTAINERS $container" + done +fi + +if [ -z "$CONTAINERS" ] +then + CPU_SCALED=0 + SUM_RAM=0 +else + DOCKER_STATS_COMMAND="/usr/bin/docker stats --no-stream --format \"table {{.CPUPerc}}\t{{.MemUsage}}\" ${CONTAINERS}" + DOCKER_STATS_COMMAND_RESULT=$(eval "$DOCKER_STATS_COMMAND") + + SUM_CPU=`echo $DOCKER_STATS_COMMAND_RESULT | tail -n +2 | sed "s/%//g" | awk '{s+=$1} END {print s}'` + SUM_RAM=`echo $DOCKER_STATS_COMMAND_RESULT | tail -n +2 | sed "s/%//g" | awk '{s+=$2} END {print s}'` + + CPU_SCALED=$(echo "scale=2; $SUM_CPU/$CPU_CORES" | bc) +fi + +# Output the result +echo -e "CPU=${CPU_SCALED}%;RAM=${SUM_RAM}MiB" \ No newline at end of file diff --git a/ntsimulator/deploy/o-ran-sc/o-ran-ru/Dockerfile b/ntsimulator/deploy/o-ran-sc/o-ran-ru/Dockerfile index e70a0a6..ff4c008 100644 --- a/ntsimulator/deploy/o-ran-sc/o-ran-ru/Dockerfile +++ b/ntsimulator/deploy/o-ran-sc/o-ran-ru/Dockerfile @@ -107,8 +107,6 @@ RUN \ make install && \ ldconfig -# overwrite number of endpoints exposed by the NETCONF server -COPY ./deploy/o-ran-sc/o-ran-ru/stock_config.xml /opt/dev/Netopeer2/server/stock_config.xml # netopeer2 RUN \ cd /opt/dev/Netopeer2/server && mkdir build && cd build && \ @@ -205,7 +203,8 @@ RUN ldconfig WORKDIR /tmp RUN apt-get install -yqq wget \ - openssh-client + openssh-client \ + jq RUN mkdir py_install && cd py_install && \ wget https://files.pythonhosted.org/packages/b9/9a/3e9da40ea28b8210dd6504d3fe9fe7e013b62bf45902b458d1cdc3c34ed9/ipaddress-1.0.23.tar.gz && \ @@ -237,6 +236,6 @@ ARG BUILD_DATE LABEL build-date=$BUILD_DATE ENV EDITOR vim -EXPOSE 830-839 +EXPOSE 830-929 CMD ["sh", "-c", "/usr/bin/supervisord -c /etc/supervisord.conf"] diff --git a/ntsimulator/deploy/o-ran-sc/o-ran-ru/container-tag.yaml b/ntsimulator/deploy/o-ran-sc/o-ran-ru/container-tag.yaml index 16a8dcd..38caae4 100644 --- a/ntsimulator/deploy/o-ran-sc/o-ran-ru/container-tag.yaml +++ b/ntsimulator/deploy/o-ran-sc/o-ran-ru/container-tag.yaml @@ -1,2 +1,2 @@ --- -tag: 0.1.2 +tag: 0.2.1 diff --git a/ntsimulator/deploy/o-ran-sc/o-ran-ru/stock_config.xml b/ntsimulator/deploy/o-ran-sc/o-ran-ru/stock_config.xml deleted file mode 100644 index 014c840..0000000 --- a/ntsimulator/deploy/o-ran-sc/o-ran-ru/stock_config.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - MNG0 - -
0.0.0.0
- 830 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG1 - -
0.0.0.0
- 831 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG2 - -
0.0.0.0
- 832 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG3 - -
0.0.0.0
- 833 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG4 - -
0.0.0.0
- 834 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG5 - -
0.0.0.0
- 835 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG6 - -
0.0.0.0
- 836 - - - imported SSH key - ssh_host_rsa_key - - -
-
-
-
diff --git a/ntsimulator/deploy/o-ran/ru-fh/Dockerfile b/ntsimulator/deploy/o-ran/ru-fh/Dockerfile index a969262..e711258 100644 --- a/ntsimulator/deploy/o-ran/ru-fh/Dockerfile +++ b/ntsimulator/deploy/o-ran/ru-fh/Dockerfile @@ -107,8 +107,6 @@ RUN \ make install && \ ldconfig -# overwrite number of endpoints exposed by the NETCONF server -COPY ./deploy/o-ran/ru-fh/stock_config.xml /opt/dev/Netopeer2/server/stock_config.xml # netopeer2 RUN \ cd /opt/dev/Netopeer2/server && mkdir build && cd build && \ @@ -205,7 +203,8 @@ RUN ldconfig WORKDIR /tmp RUN apt-get install -yqq wget \ - openssh-client + openssh-client \ + jq RUN mkdir py_install && cd py_install && \ wget https://files.pythonhosted.org/packages/b9/9a/3e9da40ea28b8210dd6504d3fe9fe7e013b62bf45902b458d1cdc3c34ed9/ipaddress-1.0.23.tar.gz && \ @@ -246,6 +245,6 @@ ARG BUILD_DATE LABEL build-date=$BUILD_DATE ENV EDITOR vim -EXPOSE 830-839 +EXPOSE 830-929 CMD ["sh", "-c", "/usr/bin/supervisord -c /etc/supervisord.conf"] diff --git a/ntsimulator/deploy/o-ran/ru-fh/container-tag.yaml b/ntsimulator/deploy/o-ran/ru-fh/container-tag.yaml index 16a8dcd..38caae4 100644 --- a/ntsimulator/deploy/o-ran/ru-fh/container-tag.yaml +++ b/ntsimulator/deploy/o-ran/ru-fh/container-tag.yaml @@ -1,2 +1,2 @@ --- -tag: 0.1.2 +tag: 0.2.1 diff --git a/ntsimulator/deploy/o-ran/ru-fh/stock_config.xml b/ntsimulator/deploy/o-ran/ru-fh/stock_config.xml deleted file mode 100644 index 014c840..0000000 --- a/ntsimulator/deploy/o-ran/ru-fh/stock_config.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - MNG0 - -
0.0.0.0
- 830 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG1 - -
0.0.0.0
- 831 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG2 - -
0.0.0.0
- 832 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG3 - -
0.0.0.0
- 833 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG4 - -
0.0.0.0
- 834 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG5 - -
0.0.0.0
- 835 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG6 - -
0.0.0.0
- 836 - - - imported SSH key - ssh_host_rsa_key - - -
-
-
-
diff --git a/ntsimulator/deploy/o-ran/ru-fh/supervisord.conf b/ntsimulator/deploy/o-ran/ru-fh/supervisord.conf index 886a5f9..e9f3e2f 100644 --- a/ntsimulator/deploy/o-ran/ru-fh/supervisord.conf +++ b/ntsimulator/deploy/o-ran/ru-fh/supervisord.conf @@ -37,6 +37,14 @@ autorestart=false redirect_stderr=false priority=5 +[program:set-nts-ip-script] +directory=/home/netconf/.ssh +command=/home/netconf/.ssh/set_NTS_IP.sh +startsecs=0 +autorestart=false +redirect_stderr=false +priority=5 + [program:sysrepo-config-load] directory=/opt/dev/yang command=/opt/dev/yang/sysrepo-configuration-load.sh diff --git a/ntsimulator/deploy/tls/enable_connections.sh b/ntsimulator/deploy/tls/enable_connections.sh new file mode 100755 index 0000000..ff51e27 --- /dev/null +++ b/ntsimulator/deploy/tls/enable_connections.sh @@ -0,0 +1,64 @@ +#! /bin/bash +################################################################################ +# +# Copyright 2020 highstreet technologies GmbH and others +# +# 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. + +################################################################################ + +int_re='^[0-9]+$' + +ssh_conn=`jq '.["ssh-connections"]' /opt/dev/scripts/configuration.json` +tls_conn=`jq '.["tls-connections"]' /opt/dev/scripts/configuration.json` + +echo "Enabling $ssh_conn SSH connections and $tls_conn TLS connections in device..." + +# if [ "$#" -ne 2 ]; then +# echo "Usage: $0 NUM_SSH_CONNECTIONS NUM_TLS_CONNECTIONS" >&2 +# exit 1 +# fi + +if ! [[ $ssh_conn =~ $int_re ]] ; then + echo "error: Argument '$ssh_conn' is not a number" >&2 + exit 1 +fi + +if ! [[ $tls_conn =~ $int_re ]] ; then + echo "error: Argument '$tls_conn' is not a number" >&2 + exit 1 +fi + +netconf_port=830 + +echo '' > connections.xml + +for (( ssh_counter=0; ssh_counter<$ssh_conn; ssh_counter++ )) +do + echo "MNG$ssh_counter
::
$netconf_portimported SSH keyssh_host_rsa_key
" >> connections.xml + ((netconf_port++)) +done + +for (( tls_counter=0; tls_counter<$tls_conn; tls_counter++ )) +do + echo "MNGTLS$tls_counter
::
$netconf_portmelacon_server_certtrusted_ca_list102:E9:38:1F:F6:8B:62:DE:0A:0B:C5:03:81:A8:03:49:A0:00:7F:8B:F3x509c2n:specifiednetconf
" >> connections.xml + ((netconf_port++)) +done + +echo '
' >> connections.xml + +sysrepocfg --import=connections.xml --format=xml ietf-netconf-server +rm connections.xml + +echo 'Done' +exit 0 \ No newline at end of file diff --git a/ntsimulator/deploy/tls/enable_tls.sh b/ntsimulator/deploy/tls/enable_tls.sh index 97b539d..11fbb3d 100755 --- a/ntsimulator/deploy/tls/enable_tls.sh +++ b/ntsimulator/deploy/tls/enable_tls.sh @@ -50,14 +50,8 @@ echo ''"$CA_CERT"'' >> tls_endpoints.xml -echo 'MNG_TLS_1
0.0.0.0
837melacon_server_certtrusted_ca_list102:E9:38:1F:F6:8B:62:DE:0A:0B:C5:03:81:A8:03:49:A0:00:7F:8B:F3x509c2n:specifiednetconf
' >> tls_endpoints.xml -echo 'MNG_TLS_2
0.0.0.0
838melacon_server_certtrusted_ca_list102:E9:38:1F:F6:8B:62:DE:0A:0B:C5:03:81:A8:03:49:A0:00:7F:8B:F3x509c2n:specifiednetconf
' >> tls_endpoints.xml -echo 'MNG_TLS_3
0.0.0.0
839melacon_server_certtrusted_ca_list102:E9:38:1F:F6:8B:62:DE:0A:0B:C5:03:81:A8:03:49:A0:00:7F:8B:F3x509c2n:specifiednetconf
' >> tls_endpoints.xml -echo '
' >> tls_endpoints.xml - -sysrepocfg --merge=tls_endpoints.xml --format=xml ietf-netconf-server -rm tls_endpoints.xml +# enable the SSH and TLS connections, according to the configuration file +./enable_connections.sh echo 'Done' exit 0 \ No newline at end of file diff --git a/ntsimulator/deploy/tls/set_NTS_IP.sh b/ntsimulator/deploy/tls/set_NTS_IP.sh new file mode 100755 index 0000000..151e996 --- /dev/null +++ b/ntsimulator/deploy/tls/set_NTS_IP.sh @@ -0,0 +1,43 @@ +#!/bin/bash +################################################################################ +# +# Copyright 2020 highstreet technologies GmbH and others +# +# 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. + +################################################################################ + +sleep 30 + +if [ "$K8S_DEPLOYMENT" = "true" ]; then + NTS_IP="" + while true + do + echo "Trying to set the NTS_IP env var..." + if [ -z "$NTS_IP" ]; then + s=$HOSTNAME + count="$(cut -d'-' -f2 <<<"$s")" + id="NTSIM_${count}_SERVICE_HOST" + export NTS_IP=$(echo ${!id}) + echo "NTS_IP=$NTS_IP" + else + echo "export NTS_IP=$NTS_IP" >> /root/.bashrc + source /root/.bashrc + fi + sleep 10 + done +else + echo "Non k8s deployment, not doing anything for the NTS_IP..." +fi + +exit 0 diff --git a/ntsimulator/deploy/x-ran/Dockerfile b/ntsimulator/deploy/x-ran/Dockerfile index 0ecd983..b1577d7 100644 --- a/ntsimulator/deploy/x-ran/Dockerfile +++ b/ntsimulator/deploy/x-ran/Dockerfile @@ -107,8 +107,6 @@ RUN \ make install && \ ldconfig -# overwrite number of endpoints exposed by the NETCONF server -COPY ./deploy/x-ran/stock_config.xml /opt/dev/Netopeer2/server/stock_config.xml # netopeer2 RUN \ cd /opt/dev/Netopeer2/server && mkdir build && cd build && \ @@ -205,7 +203,8 @@ RUN ldconfig WORKDIR /tmp RUN apt-get install -yqq wget \ - openssh-client + openssh-client \ + jq RUN mkdir py_install && cd py_install && \ wget https://files.pythonhosted.org/packages/b9/9a/3e9da40ea28b8210dd6504d3fe9fe7e013b62bf45902b458d1cdc3c34ed9/ipaddress-1.0.23.tar.gz && \ @@ -246,6 +245,6 @@ ARG BUILD_DATE LABEL build-date=$BUILD_DATE ENV EDITOR vim -EXPOSE 830-839 +EXPOSE 830-929 CMD ["sh", "-c", "/usr/bin/supervisord -c /etc/supervisord.conf"] diff --git a/ntsimulator/deploy/x-ran/container-tag.yaml b/ntsimulator/deploy/x-ran/container-tag.yaml index 16a8dcd..38caae4 100644 --- a/ntsimulator/deploy/x-ran/container-tag.yaml +++ b/ntsimulator/deploy/x-ran/container-tag.yaml @@ -1,2 +1,2 @@ --- -tag: 0.1.2 +tag: 0.2.1 diff --git a/ntsimulator/deploy/x-ran/stock_config.xml b/ntsimulator/deploy/x-ran/stock_config.xml deleted file mode 100644 index 014c840..0000000 --- a/ntsimulator/deploy/x-ran/stock_config.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - MNG0 - -
0.0.0.0
- 830 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG1 - -
0.0.0.0
- 831 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG2 - -
0.0.0.0
- 832 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG3 - -
0.0.0.0
- 833 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG4 - -
0.0.0.0
- 834 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG5 - -
0.0.0.0
- 835 - - - imported SSH key - ssh_host_rsa_key - - -
-
- - MNG6 - -
0.0.0.0
- 836 - - - imported SSH key - ssh_host_rsa_key - - -
-
-
-
diff --git a/ntsimulator/inc/utils/utils.h b/ntsimulator/inc/utils/utils.h index 6208600..c046509 100644 --- a/ntsimulator/inc/utils/utils.h +++ b/ntsimulator/inc/utils/utils.h @@ -31,7 +31,9 @@ #define TRUE 1 #define FALSE 0 -#define NETCONF_CONNECTIONS_PER_DEVICE 10 +#define NETCONF_CONNECTIONS_PER_DEVICE (getSshConnectionsFromConfigJson() + getTlsConnectionsFromConfigJson()) +#define SSH_CONNECTIONS_PER_DEVICE getSshConnectionsFromConfigJson() +#define TLS_CONNECTIONS_PER_DEVICE getTlsConnectionsFromConfigJson() #define PREPARE_ADD_NEW_VALUE(v, num) {\ num++;\ @@ -72,6 +74,7 @@ cJSON* vesCreateFaultFields(char *alarm_condition, char *alarm_object, char *sev char* readConfigFileInString(void); void writeConfigFile(char *config); +int writeSkeletonConfigFile(void); int getFaultNotificationDelayPeriodFromConfigJson(int *period_array, int *count); int getVesHeartbeatPeriodFromConfigJson(void); @@ -81,6 +84,8 @@ int getVesPortFromConfigJson(void); int getVesRegistrationFromConfigJson(void); int getNetconfAvailableFromConfigJson(void); int getVesAvailableFromConfigJson(void); +int getSshConnectionsFromConfigJson(void); +int getTlsConnectionsFromConfigJson(void); void generateRandomMacAddress(char *mac_address); @@ -95,4 +100,6 @@ cJSON* getDeviceListFromStatusFile(void); int compute_notifications_count(counterAlarms *ves_counter, counterAlarms *netconf_counter); int getDeviceCounters(char *containerId, counterAlarms *ves_counter, counterAlarms *netconf_counter); +int getIntFromString(char *string, int def_value); + #endif /* EXAMPLES_NTSIMULATOR_UTILS_H_ */ diff --git a/ntsimulator/src/ntsimulator-manager/ntsimulator-manager.c b/ntsimulator/src/ntsimulator-manager/ntsimulator-manager.c index ea120c7..165e7bb 100644 --- a/ntsimulator/src/ntsimulator-manager/ntsimulator-manager.c +++ b/ntsimulator/src/ntsimulator-manager/ntsimulator-manager.c @@ -84,11 +84,23 @@ print_current_config(sr_session_ctx_t *session, const char *module_name) } } + char *ipv6 = strchr(odl_ip->data.string_val, ':'); + char odl_ip_string[URL_AND_CREDENTIALS_MAX_LEN]; + if (ipv6 != NULL) + { + sprintf(odl_ip_string, "[%s]", odl_ip->data.string_val); + } + else + { + sprintf(odl_ip_string, "%s", odl_ip->data.string_val); + } + + //URL used for mounting/unmounting a device; the device name needs to be appended char url[URL_AND_CREDENTIALS_MAX_LEN]; sprintf(url, "http://%s:%d/restconf/config/network-topology:network-topology/topology/" "topology-netconf/node/", - odl_ip->data.string_val, odl_port->data.uint32_val); + odl_ip_string, odl_port->data.uint32_val); char credentials[URL_AND_CREDENTIALS_MAX_LEN]; sprintf(credentials, "%s:%s", odl_username->data.string_val, odl_password->data.string_val); @@ -96,15 +108,15 @@ print_current_config(sr_session_ctx_t *session, const char *module_name) //URLs used for adding key pair to ODL, for TLS connections char url_for_keystore_add[URL_AND_CREDENTIALS_MAX_LEN]; sprintf(url_for_keystore_add, "http://%s:%d/restconf/operations/netconf-keystore:add-keystore-entry", - odl_ip->data.string_val, odl_port->data.uint32_val); + odl_ip_string, odl_port->data.uint32_val); char url_for_private_key_add[URL_AND_CREDENTIALS_MAX_LEN]; sprintf(url_for_private_key_add, "http://%s:%d/restconf/operations/netconf-keystore:add-private-key", - odl_ip->data.string_val, odl_port->data.uint32_val); + odl_ip_string, odl_port->data.uint32_val); char url_for_trusted_ca_add[URL_AND_CREDENTIALS_MAX_LEN]; sprintf(url_for_trusted_ca_add, "http://%s:%d/restconf/operations/netconf-keystore:add-trusted-certificate", - odl_ip->data.string_val, odl_port->data.uint32_val); + odl_ip_string, odl_port->data.uint32_val); strcpy(controller_details.url, url); strcpy(controller_details.credentials, credentials); @@ -126,6 +138,20 @@ static int simulated_devices_changed(int new_value) { int rc = SR_ERR_OK; + if (strcmp(getenv("K8S_DEPLOYMENT"), "true") == 0) + { + if (new_value != simulated_devices_config) + { + simulated_devices_config = new_value; + rc = send_k8s_scale(new_value); + if (rc != SR_ERR_OK) + { + printf("Could not send new_scale=%d to k8s cluster.\n", new_value); + } + } + return SR_ERR_OK; + } + if (simulated_devices_config > new_value) { //we are configuring less elements that currently @@ -140,6 +166,10 @@ static int simulated_devices_changed(int new_value) for (int i = 0; i < new_value - simulated_devices_config; ++i) { rc = start_device(device_list); + if (rc != SR_ERR_OK) + { + printf("ERROR: Could not start simulated device. Ignoring, trying with the next simulated device, if any...\n"); + } } } @@ -324,6 +354,56 @@ simulator_config_change_cb(sr_session_ctx_t *session, const char *module_name, s sr_free_val(val); val = NULL; + /* get the value from sysrepo, we do not care if the value did not change in our case */ + rc = sr_get_item(session, "/network-topology-simulator:simulator-config/ssh-connections", &val); + if (rc != SR_ERR_OK) { + printf("NTS Manager /network-topology-simulator:simulator-config/ssh-connections object not available, ignoring.."); + } + else + { + rc = ssh_connections_changed(val->data.uint32_val); + if (rc != SR_ERR_OK) { + goto sr_error; + } + + if (strcmp(getenv("K8S_DEPLOYMENT"), "true") == 0) + { + rc = send_k8s_extend_port(); + if (rc != SR_ERR_OK) + { + printf("Could not send the extended port to k8s cluster.\n"); + } + } + } + + sr_free_val(val); + val = NULL; + + /* get the value from sysrepo, we do not care if the value did not change in our case */ + rc = sr_get_item(session, "/network-topology-simulator:simulator-config/tls-connections", &val); + if (rc != SR_ERR_OK) { + printf("NTS Manager /network-topology-simulator:simulator-config/tls-connections object not available, ignoring.."); + } + else + { + rc = tls_connections_changed(val->data.uint32_val); + if (rc != SR_ERR_OK) { + goto sr_error; + } + + if (strcmp(getenv("K8S_DEPLOYMENT"), "true") == 0) + { + rc = send_k8s_extend_port(); + if (rc != SR_ERR_OK) + { + printf("Could not send the extended port to k8s cluster.\n"); + } + } + } + + sr_free_val(val); + val = NULL; + return SR_ERR_OK; sr_error: @@ -351,7 +431,8 @@ simulator_status_cb(const char *xpath, sr_val_t **values, size_t *values_cnt, printf("Could not compute the total number of notification count.\n"); } - if (sr_xpath_node_name_eq(xpath, "simulated-devices-list")) { + if (sr_xpath_node_name_eq(xpath, "simulated-devices-list")) + { sr_val_t *v; size_t current_num_of_values= 0; @@ -654,6 +735,12 @@ main(int argc, char **argv) setbuf(stdout, NULL); + rc = _init_curl_k8s(); + if (rc != SR_ERR_OK) + { + fprintf(stderr, "Could not initialize cURL for K8S connection: %s\n", sr_strerror(rc)); + } + device_list = new_device_stack(); rc = _init_curl(); if (rc != SR_ERR_OK) @@ -661,6 +748,12 @@ main(int argc, char **argv) fprintf(stderr, "Could not initialize cURL: %s\n", sr_strerror(rc)); } + rc = writeSkeletonConfigFile(); + if (rc != SR_ERR_OK) + { + fprintf(stderr, "Could not initialize configuration JSON file: %s\n", sr_strerror(rc)); + } + /* connect to sysrepo */ rc = sr_connect("network-topology-simulator", SR_CONN_DEFAULT, &connection); if (SR_ERR_OK != rc) { @@ -675,6 +768,177 @@ main(int argc, char **argv) goto cleanup; } + // setting the values that come in an ENV variable as defaults - ves-heartbeat-period + int vesHeartbeatPeriod = getIntFromString(getenv("VesHeartbeatPeriod"), 0); + + sr_val_t value = { 0 }; + value.type = SR_UINT32_T; + value.data.uint32_val = vesHeartbeatPeriod; + rc = sr_set_item(session, "/network-topology-simulator:simulator-config/notification-config/ves-heartbeat-period", + &value, SR_EDIT_DEFAULT); + if (SR_ERR_OK != rc) { + printf("Error by sr_set_item: %s\n", sr_strerror(rc)); + goto cleanup; + } + + rc = ves_heartbeat_period_changed(vesHeartbeatPeriod); + if (SR_ERR_OK != rc) { + printf("Error by ves_heartbeat_period_changed: %s\n", sr_strerror(rc)); + goto cleanup; + } + + // setting the values that come in an ENV variable as defaults - is-netconf-available + + int isNetconfAvailable = 1; + + char *isNetconfAvailablString = getenv("IsNetconfAvailable"); + if (isNetconfAvailablString != NULL) + { + if (strcmp(isNetconfAvailablString, "false") == 0) + { + isNetconfAvailable = 0; + } + } + + value = (const sr_val_t) { 0 }; + value.type = SR_BOOL_T; + value.data.bool_val = isNetconfAvailable; + rc = sr_set_item(session, "/network-topology-simulator:simulator-config/notification-config/is-netconf-available", + &value, SR_EDIT_DEFAULT); + if (SR_ERR_OK != rc) { + printf("Error by sr_set_item: %s\n", sr_strerror(rc)); + goto cleanup; + } + + rc = is_netconf_available_changed(isNetconfAvailable); + if (SR_ERR_OK != rc) { + printf("Error by is_netconf_available_changed: %s\n", sr_strerror(rc)); + goto cleanup; + } + + // setting the values that come in an ENV variable as defaults - is-ves-available + + int isVesAvailable = 1; + + char *isVesAvailablString = getenv("IsVesAvailable"); + if (isVesAvailablString != NULL) + { + if (strcmp(isVesAvailablString, "false") == 0) + { + isVesAvailable = 0; + } + } + + value = (const sr_val_t) { 0 }; + value.type = SR_BOOL_T; + value.data.bool_val = isVesAvailable; + rc = sr_set_item(session, "/network-topology-simulator:simulator-config/notification-config/is-ves-available", + &value, SR_EDIT_DEFAULT); + if (SR_ERR_OK != rc) { + printf("Error by sr_set_item: %s\n", sr_strerror(rc)); + goto cleanup; + } + + rc = is_ves_available_changed(isVesAvailable); + if (SR_ERR_OK != rc) { + printf("Error by is_ves_available_changed: %s\n", sr_strerror(rc)); + goto cleanup; + } + + // setting the values that come in an ENV variable as defaults - ves-endpoint-port + + int vesEndpointPort = getIntFromString(getenv("VesEndpointPort"), 8080); + + value = (const sr_val_t) { 0 }; + value.type = SR_UINT16_T; + value.data.uint16_val = vesEndpointPort; + rc = sr_set_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-endpoint-port", + &value, SR_EDIT_DEFAULT); + if (SR_ERR_OK != rc) { + printf("Error by sr_set_item: %s\n", sr_strerror(rc)); + goto cleanup; + } + + rc = ves_port_changed(vesEndpointPort); + if (SR_ERR_OK != rc) { + printf("Error by ves_port_changed: %s\n", sr_strerror(rc)); + goto cleanup; + } + + // setting the values that come in an ENV variable as defaults - ves-endpoint-ip + + value = (const sr_val_t) { 0 }; + value.type = SR_STRING_T; + value.data.string_val = getenv("VesEndpointIp"); + rc = sr_set_item(session, "/network-topology-simulator:simulator-config/ves-endpoint-details/ves-endpoint-ip", + &value, SR_EDIT_DEFAULT); + if (SR_ERR_OK != rc) { + printf("Error by sr_set_item: %s\n", sr_strerror(rc)); + goto cleanup; + } + + rc = ves_ip_changed(getenv("VesEndpointIp")); + if (SR_ERR_OK != rc) { + printf("Error by ves_ip_changed: %s\n", sr_strerror(rc)); + goto cleanup; + } + + // setting the values that come in an ENV variable as defaults - ssh-connections + + int sshConnections = getIntFromString(getenv("SshConnections"), 1); + + value = (const sr_val_t) { 0 }; + value.type = SR_UINT32_T; + value.data.uint32_val = sshConnections; + rc = sr_set_item(session, "/network-topology-simulator:simulator-config/ssh-connections", + &value, SR_EDIT_DEFAULT); + if (SR_ERR_OK != rc) { + printf("Error by sr_set_item: %s\n", sr_strerror(rc)); + goto cleanup; + } + + rc = ssh_connections_changed(sshConnections); + if (SR_ERR_OK != rc) { + printf("Error by ssh_connections_changed: %s\n", sr_strerror(rc)); + goto cleanup; + } + + // setting the values that come in an ENV variable as defaults - tls-connections + + int tlsConnections = getIntFromString(getenv("TlsConnections"), 0); + + value = (const sr_val_t) { 0 }; + value.type = SR_UINT32_T; + value.data.uint32_val = tlsConnections; + rc = sr_set_item(session, "/network-topology-simulator:simulator-config/tls-connections", + &value, SR_EDIT_DEFAULT); + if (SR_ERR_OK != rc) { + printf("Error by sr_set_item: %s\n", sr_strerror(rc)); + goto cleanup; + } + + rc = tls_connections_changed(tlsConnections); + if (SR_ERR_OK != rc) { + printf("Error by tls_connections_changed: %s\n", sr_strerror(rc)); + goto cleanup; + } + + if (strcmp(getenv("K8S_DEPLOYMENT"), "true") == 0) + { + rc = send_k8s_extend_port(); + if (rc != SR_ERR_OK) + { + printf("Could not send the number of ports to k8s cluster\n"); + } + } + + //commit the changes that we have done until now + rc = sr_commit(session); + if (SR_ERR_OK != rc) { + printf("Error by sr_commit: %s\n", sr_strerror(rc)); + goto cleanup; + } + /* read startup config */ printf("\n\n ========== READING STARTUP CONFIG network-topology-simulator: ==========\n\n"); print_current_config(session, "network-topology-simulator"); @@ -743,12 +1007,18 @@ cleanup: clean_current_docker_configuration(); rc = cleanup_curl(); rc = cleanup_curl_odl(); + rc = cleanup_curl_k8s(); return rc; } static void clean_current_docker_configuration(void) { + if (strcmp(getenv("K8S_DEPLOYMENT"), "true")) + { + return; + } + printf("Cleaning docker containers...\n"); if (device_list == NULL) diff --git a/ntsimulator/src/ntsimulator-manager/simulator-operations.c b/ntsimulator/src/ntsimulator-manager/simulator-operations.c index f734307..81736c3 100644 --- a/ntsimulator/src/ntsimulator-manager/simulator-operations.c +++ b/ntsimulator/src/ntsimulator-manager/simulator-operations.c @@ -28,6 +28,11 @@ static CURL *curl; //share the same curl connection for communicating with the Docker Engine API static CURL *curl_odl; //share the same curl connection for mounting servers in ODL +static CURL *curl_k8s; //share the same curl connection for communicating with the K8S cluster + +/* +curl -X POST -H 'Content-Type: application/json' -i http://localhost:5000/scale --data '{"simulatedDevices":2}' +*/ static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { @@ -51,35 +56,49 @@ static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, voi static void set_curl_common_info() { - struct curl_slist *chunk = NULL; - chunk = curl_slist_append(chunk, "Content-Type: application/json"); - chunk = curl_slist_append(chunk, "Accept: application/json"); + struct curl_slist *chunk = NULL; + chunk = curl_slist_append(chunk, "Content-Type: application/json"); + chunk = curl_slist_append(chunk, "Accept: application/json"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); - curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, "/var/run/docker.sock"); + curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, "/var/run/docker.sock"); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - curl_easy_setopt(curl_odl, CURLOPT_CONNECTTIMEOUT, 2L); // seconds timeout for a connection - curl_easy_setopt(curl_odl, CURLOPT_TIMEOUT, 5L); //seconds timeout for an operation + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 2L); // seconds timeout for a connection + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); //seconds timeout for an operation curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); } static void set_curl_common_info_odl() { - struct curl_slist *chunk = NULL; - chunk = curl_slist_append(chunk, "Content-Type: application/xml"); - chunk = curl_slist_append(chunk, "Accept: application/xml"); + struct curl_slist *chunk = NULL; + chunk = curl_slist_append(chunk, "Content-Type: application/xml"); + chunk = curl_slist_append(chunk, "Accept: application/xml"); curl_easy_setopt(curl_odl, CURLOPT_HTTPHEADER, chunk); curl_easy_setopt(curl_odl, CURLOPT_CONNECTTIMEOUT, 2L); // seconds timeout for a connection - curl_easy_setopt(curl_odl, CURLOPT_TIMEOUT, 5L); //seconds timeout for an operation + curl_easy_setopt(curl_odl, CURLOPT_TIMEOUT, 10L); //seconds timeout for an operation curl_easy_setopt(curl_odl, CURLOPT_VERBOSE, 1L); } +static void set_curl_common_info_k8s() +{ + struct curl_slist *chunk = NULL; + chunk = curl_slist_append(chunk, "Content-Type: application/json"); + chunk = curl_slist_append(chunk, "Accept: application/json"); + + curl_easy_setopt(curl_k8s, CURLOPT_HTTPHEADER, chunk); + + curl_easy_setopt(curl_k8s, CURLOPT_CONNECTTIMEOUT, 2L); // seconds timeout for a connection + curl_easy_setopt(curl_k8s, CURLOPT_TIMEOUT, 10L); //seconds timeout for an operation + + curl_easy_setopt(curl_k8s, CURLOPT_VERBOSE, 1L); +} + static cJSON* get_docker_container_bindings(void) { struct MemoryStruct curl_response_mem; @@ -146,14 +165,87 @@ static cJSON* get_docker_container_bindings(void) return NULL; } -static char* create_docker_container_curl(int base_netconf_port, cJSON* managerBinds) +static cJSON* get_docker_container_network_node(void) { - if (managerBinds == NULL) + struct MemoryStruct curl_response_mem; + + curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ + curl_response_mem.size = 0; /* no data at this point */ + + CURLcode res; + + curl_easy_reset(curl); + set_curl_common_info(); + + char url[200]; + sprintf(url, "http:/v%s/containers/%s/json", getenv("DOCKER_ENGINE_VERSION"), getenv("HOSTNAME")); + + curl_easy_setopt(curl, CURLOPT_URL, url); + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); + + res = curl_easy_perform(curl); + + if (res != CURLE_OK) + { + return NULL; + } + else + { + cJSON *json_response = cJSON_Parse(curl_response_mem.memory); + + printf("%lu bytes retrieved\n", (unsigned long)curl_response_mem.size); + + if (json_response == NULL) + { + printf("Could not parse JSON response for url=\"%s\"\n", url); + return NULL; + } + + cJSON *hostConfig = cJSON_GetObjectItemCaseSensitive(json_response, "HostConfig"); + + if (hostConfig == NULL) + { + printf("Could not get HostConfig object\n"); + return NULL; + } + + cJSON *networkMode = cJSON_GetObjectItemCaseSensitive(hostConfig, "NetworkMode"); + + if (networkMode == NULL) + { + printf("Could not get NetworkMode object\n"); + return NULL; + } + + cJSON *networkCopy = cJSON_Duplicate(networkMode, 1); + + cJSON_Delete(json_response); + + return networkCopy; + } + + return NULL; +} + +static char* create_docker_container_curl(int base_netconf_port, cJSON* managerBinds, cJSON* networkMode) +{ + if (managerBinds == NULL) + { + printf("Could not retrieve JSON object: Binds\n"); + return NULL; + } + cJSON *binds = cJSON_Duplicate(managerBinds, 1); + + if (networkMode == NULL) { - printf("Could not retrieve JSON object: Binds\n"); + printf("Could not retrieve JSON object: NetworkMode\n"); return NULL; } - cJSON *binds = cJSON_Duplicate(managerBinds, 1); + cJSON *netMode = cJSON_Duplicate(networkMode, 1); struct MemoryStruct curl_response_mem; @@ -288,18 +380,30 @@ static char* create_docker_container_curl(int base_netconf_port, cJSON* managerB } cJSON_AddItemToArray(env_variables_array, env_var_obj_2); - char scripts_dir[200]; - sprintf(scripts_dir, "SCRIPTS_DIR=%s", getenv("SCRIPTS_DIR")); - cJSON *env_var_obj_3 = cJSON_CreateString(scripts_dir); - if (env_var_obj_3 == NULL) - { - printf("Could not create JSON object: Env array object SCRIPTS_DIR\n"); - return NULL; - } - cJSON_AddItemToArray(env_variables_array, env_var_obj_3); + char scripts_dir[200]; + sprintf(scripts_dir, "SCRIPTS_DIR=%s", getenv("SCRIPTS_DIR")); + cJSON *env_var_obj_3 = cJSON_CreateString(scripts_dir); + if (env_var_obj_3 == NULL) + { + printf("Could not create JSON object: Env array object SCRIPTS_DIR\n"); + return NULL; + } + cJSON_AddItemToArray(env_variables_array, env_var_obj_3); + + char k8s_deployment[50]; + sprintf(k8s_deployment, "K8S_DEPLOYMENT=%s", getenv("K8S_DEPLOYMENT")); + cJSON *env_var_obj_4 = cJSON_CreateString(k8s_deployment); + if (env_var_obj_4 == NULL) + { + printf("Could not create JSON object: Env array object K8S_DEPLOYMENT\n"); + return NULL; + } + cJSON_AddItemToArray(env_variables_array, env_var_obj_4); cJSON_AddItemToObject(hostConfig, "Binds", binds); + cJSON_AddItemToObject(hostConfig, "NetworkMode", netMode); + char *post_data_string = NULL; post_data_string = cJSON_PrintUnformatted(postDataJson); @@ -317,6 +421,11 @@ static char* create_docker_container_curl(int base_netconf_port, cJSON* managerB res = curl_easy_perform(curl); + if (post_data_string != NULL) + { + free(post_data_string); + } + if (res != CURLE_OK) { return NULL; @@ -353,37 +462,76 @@ static char* create_docker_container_curl(int base_netconf_port, cJSON* managerB static int start_docker_container_curl(char *container_id) { - struct MemoryStruct curl_response_mem; + struct MemoryStruct curl_response_mem; - curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ - curl_response_mem.size = 0; /* no data at this point */ + curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ + curl_response_mem.size = 0; /* no data at this point */ - CURLcode res; + CURLcode res; - curl_easy_reset(curl); - set_curl_common_info(); + curl_easy_reset(curl); + set_curl_common_info(); - char url[100]; - sprintf(url, "http:/v%s/containers/%s/start", getenv("DOCKER_ENGINE_VERSION"), container_id); + char url[100]; + sprintf(url, "http:/v%s/containers/%s/start", getenv("DOCKER_ENGINE_VERSION"), container_id); - curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); - res = curl_easy_perform(curl); + res = curl_easy_perform(curl); - if (res != CURLE_OK) - { - return SR_ERR_OPERATION_FAILED; - } - else - { - printf("Container %s started successfully!\n", container_id); - } + if (res != CURLE_OK) + { + return SR_ERR_OPERATION_FAILED; + } + else + { + printf("Container %s started successfully!\n", container_id); + } - return SR_ERR_OK; + return SR_ERR_OK; +} + +static int rename_docker_container_curl(char *container_id, int device_number) +{ + struct MemoryStruct curl_response_mem; + + curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ + curl_response_mem.size = 0; /* no data at this point */ + + CURLcode res; + + curl_easy_reset(curl); + set_curl_common_info(); + + char device_name[100]; + sprintf(device_name, "%s-%d", getenv("CONTAINER_NAME"), device_number); + + char url[100]; + sprintf(url, "http:/v%s/containers/%s/rename?name=%s", getenv("DOCKER_ENGINE_VERSION"), container_id, + device_name); + + curl_easy_setopt(curl, CURLOPT_URL, url); + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&curl_response_mem); + + res = curl_easy_perform(curl); + + if (res != CURLE_OK) + { + return SR_ERR_OPERATION_FAILED; + } + else + { + printf("Container %s renamed successfully to %s!\n", container_id, device_name); + } + + return SR_ERR_OK; } static int kill_and_remove_docker_container_curl(char *container_id) @@ -437,20 +585,20 @@ static int send_mount_device_instance_ssh(char *url, char *credentials, char *de char post_data_xml[1500]; sprintf(post_data_xml, - "" - "%s_%d" - "%s" - "%d" - "%s" - "%s" - "false" - "120" - "false" - "1.5" - "20000" - "100" - "2000" - "", + "" + "%s_%d" + "%s" + "%d" + "%s" + "%s" + "false" + "120" + "false" + "1.5" + "20000" + "100" + "2000" + "", device_name, device_port, getenv("NTS_IP"), device_port, "netconf", "netconf"); printf("Post data:\n%s\n", post_data_xml); @@ -586,9 +734,10 @@ static int send_mount_device(device_t *current_device, controller_t controller_d { int rc = SR_ERR_OK; bool is_mounted = true; + int port = 0; //This is where we hardcoded: 7 devices will have SSH connections and 3 devices will have TLS connections - for (int port = 0; port < NETCONF_CONNECTIONS_PER_DEVICE - 3; ++port) + for (int i = 0; i < SSH_CONNECTIONS_PER_DEVICE; ++port, ++i) { rc = send_mount_device_instance_ssh(controller_details.url, controller_details.credentials, current_device->device_id, current_device->netconf_port + port); @@ -597,7 +746,7 @@ static int send_mount_device(device_t *current_device, controller_t controller_d is_mounted = false; } } - for (int port = NETCONF_CONNECTIONS_PER_DEVICE - 3; port < NETCONF_CONNECTIONS_PER_DEVICE; ++port) + for (int i = 0; i < TLS_CONNECTIONS_PER_DEVICE; ++port, ++i) { rc = send_mount_device_instance_tls(controller_details.url, controller_details.credentials, current_device->device_id, current_device->netconf_port + port); @@ -642,13 +791,14 @@ device_stack_t *new_device_stack(void) return stack; } -void push_device(device_stack_t *theStack, char *dev_id, int port) +void push_device(device_stack_t *theStack, char *dev_id, int port, int dev_num) { device_t *new_dev = malloc(sizeof(*new_dev)); if (new_dev) { new_dev->device_id = strdup(dev_id); new_dev->netconf_port = port; + new_dev->device_number = dev_num; new_dev->is_mounted = false; new_dev->operational_state = strdup("not-specified"); @@ -674,39 +824,38 @@ void pop_device(device_stack_t *theStack) int get_netconf_port_next(device_stack_t *theStack) { - if (theStack && theStack->stack_size > 0) { - return theStack->head->netconf_port + NETCONF_CONNECTIONS_PER_DEVICE; - } + if (theStack && theStack->stack_size > 0) { + return theStack->head->netconf_port + NETCONF_CONNECTIONS_PER_DEVICE; + } - return get_netconf_port_base(); + return get_netconf_port_base(); } int get_netconf_port_base() { - int netconf_port_base = 0, rc; + int netconf_port_base; - char *netconf_base_string = getenv("NETCONF_BASE"); + netconf_port_base = getIntFromString(getenv("NETCONF_BASE"), 50000); - if (netconf_base_string != NULL) - { - rc = sscanf(netconf_base_string, "%d", &netconf_port_base); - if (rc != 1) - { - printf("Could not get the NETCONF_BASE port! Using the default 30.000...\n"); - netconf_port_base = 30000; - } - } - - return netconf_port_base; + return netconf_port_base; } +// we start numbering the containers from 0 +int get_device_number_next(device_stack_t *theStack) +{ + if (theStack && theStack->stack_size > 0) { + return theStack->head->device_number + 1; + } + + return 0; +} char *get_id_last_device(device_stack_t *theStack) { - if (theStack && theStack->head) { - return theStack->head->device_id; - } - return NULL; + if (theStack && theStack->head) { + return theStack->head->device_id; + } + return NULL; } int get_current_number_of_mounted_devices(device_stack_t *theStack) @@ -732,6 +881,12 @@ int get_current_number_of_mounted_devices(device_stack_t *theStack) int get_current_number_of_devices(device_stack_t *theStack) { + //TODO implement function for k8s deployment + if (strcmp(getenv("K8S_DEPLOYMENT"), "true") == 0) + { + return 0; + } + struct MemoryStruct curl_response_mem; curl_response_mem.memory = malloc(1); /* will be grown as needed by the realloc above */ @@ -743,8 +898,8 @@ int get_current_number_of_devices(device_stack_t *theStack) set_curl_common_info(); char url[100]; - sprintf(url, "http:/v%s/containers/json?all=true&filters={\"label\":[\"NTS\"],\"status\":[\"running\"]}", - getenv("DOCKER_ENGINE_VERSION")); + sprintf(url, "http:/v%s/containers/json?all=true&filters={\"label\":[\"NTS_Manager=%s\"],\"status\":[\"running\"]}", + getenv("DOCKER_ENGINE_VERSION"), getenv("HOSTNAME")); curl_easy_setopt(curl, CURLOPT_URL, url); @@ -827,18 +982,29 @@ char* get_docker_container_operational_state(device_stack_t *theStack, char *con int start_device(device_stack_t *theStack) { int rc = SR_ERR_OK; - static cJSON* managerBindings = NULL; + static cJSON *managerBindings = NULL, *networkMode = NULL; - if (managerBindings == NULL) + if (managerBindings == NULL) + { + managerBindings = get_docker_container_bindings(); + } + + if (networkMode == NULL) { - managerBindings = get_docker_container_bindings(); + networkMode = get_docker_container_network_node(); } int netconf_base = get_netconf_port_next(theStack); + int device_number = get_device_number_next(theStack); - char *dev_id = create_docker_container_curl(netconf_base, managerBindings); + char *dev_id = create_docker_container_curl(netconf_base, managerBindings, networkMode); + if (dev_id == NULL) + { + printf("ERROR: Could not create docker container!\n"); + return SR_ERR_OPERATION_FAILED; + } - push_device(theStack, dev_id, netconf_base); + push_device(theStack, dev_id, netconf_base, device_number); rc = start_docker_container_curl(dev_id); if (rc != SR_ERR_OK) @@ -846,6 +1012,12 @@ int start_device(device_stack_t *theStack) printf("Could not start device with device_id=\"%s\"\n", dev_id); } + rc = rename_docker_container_curl(dev_id, device_number); + if (rc != SR_ERR_OK) + { + printf("Could not rename device with device_id=\"%s\"\n", dev_id); + } + if (dev_id) { free(dev_id); } @@ -897,6 +1069,28 @@ int cleanup_curl_odl() return SR_ERR_OK; } +int _init_curl_k8s() +{ + curl_k8s = curl_easy_init(); + + if (curl_k8s == NULL) { + printf("cURL initialization error! Aborting call!\n"); + return SR_ERR_OPERATION_FAILED; + } + + return SR_ERR_OK; +} + +int cleanup_curl_k8s() +{ + if (curl_k8s != NULL) + { + curl_easy_cleanup(curl_k8s); + } + + return SR_ERR_OK; +} + int stop_device(device_stack_t *theStack) { int rc = SR_ERR_OK; @@ -975,6 +1169,13 @@ int unmount_device(device_stack_t *theStack, controller_t controller_list) int get_docker_containers_operational_state_curl(device_stack_t *theStack) { + + //TODO implement function for k8s deployment + if (strcmp(getenv("K8S_DEPLOYMENT"), "true") == 0) + { + return SR_ERR_OK; + } + int rc = SR_ERR_OK; struct MemoryStruct curl_response_mem; @@ -987,7 +1188,8 @@ int get_docker_containers_operational_state_curl(device_stack_t *theStack) set_curl_common_info(); char url[100]; - sprintf(url, "http:/v%s/containers/json?all=true&filters={\"label\":[\"NTS\"]}", getenv("DOCKER_ENGINE_VERSION")); + sprintf(url, "http:/v%s/containers/json?all=true&filters={\"label\":[\"NTS_Manager=%s\"]}", + getenv("DOCKER_ENGINE_VERSION"), getenv("HOSTNAME")); curl_easy_setopt(curl, CURLOPT_URL, url); @@ -1047,13 +1249,19 @@ int get_docker_containers_operational_state_curl(device_stack_t *theStack) char* get_docker_container_resource_stats(device_stack_t *theStack) { + //TOD need to implement this for k8s deployment + if (strcmp(getenv("K8S_DEPLOYMENT"), "true")) + { + return strdup("CPU=0%;RAM=0MiB"); + } + char line[LINE_BUFSIZE]; int linenr; FILE *pipe; /* Get a pipe where the output from the scripts comes in */ char script[200]; - sprintf(script, "%s/docker_stats.sh", getenv("SCRIPTS_DIR")); + sprintf(script, "/opt/dev/docker_stats.sh %s", getenv("HOSTNAME")); pipe = popen(script, "r"); if (pipe == NULL) { /* check for errors */ @@ -1158,6 +1366,12 @@ int notification_delay_period_changed(sr_val_t *val, size_t count) stringConfiguration = cJSON_Print(jsonConfig); writeConfigFile(stringConfiguration); + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + cJSON_Delete(jsonConfig); return SR_ERR_OK; @@ -1211,6 +1425,12 @@ int ves_heartbeat_period_changed(int period) stringConfiguration = cJSON_Print(jsonConfig); writeConfigFile(stringConfiguration); + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + cJSON_Delete(jsonConfig); return SR_ERR_OK; @@ -1462,6 +1682,12 @@ int ves_ip_changed(char *new_ip) stringConfiguration = cJSON_Print(jsonConfig); writeConfigFile(stringConfiguration); + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + cJSON_Delete(jsonConfig); return SR_ERR_OK; @@ -1515,6 +1741,12 @@ int ves_port_changed(int new_port) stringConfiguration = cJSON_Print(jsonConfig); writeConfigFile(stringConfiguration); + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + cJSON_Delete(jsonConfig); return SR_ERR_OK; @@ -1568,6 +1800,12 @@ int ves_registration_changed(cJSON_bool new_bool) stringConfiguration = cJSON_Print(jsonConfig); writeConfigFile(stringConfiguration); + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + cJSON_Delete(jsonConfig); return SR_ERR_OK; @@ -1621,6 +1859,12 @@ int is_netconf_available_changed(cJSON_bool new_bool) stringConfiguration = cJSON_Print(jsonConfig); writeConfigFile(stringConfiguration); + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + cJSON_Delete(jsonConfig); return SR_ERR_OK; @@ -1674,7 +1918,211 @@ int is_ves_available_changed(cJSON_bool new_bool) stringConfiguration = cJSON_Print(jsonConfig); writeConfigFile(stringConfiguration); + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + cJSON_Delete(jsonConfig); return SR_ERR_OK; } + + int ssh_connections_changed(int number) + { + char *stringConfiguration = readConfigFileInString(); + + if (stringConfiguration == NULL) + { + printf("Could not read configuration file!\n"); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *jsonConfig = cJSON_Parse(stringConfiguration); + if (jsonConfig == NULL) + { + free(stringConfiguration); + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Could not parse JSON configuration! Error before: %s\n", error_ptr); + } + return SR_ERR_OPERATION_FAILED; + } + //we don't need the string anymore + free(stringConfiguration); + stringConfiguration = NULL; + + cJSON *sshConnections = cJSON_GetObjectItemCaseSensitive(jsonConfig, "ssh-connections"); + if (!cJSON_IsNumber(sshConnections)) + { + printf("Configuration JSON is not as expected: ssh-connections is not an object"); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + //we set the value of the ssh-connections object + cJSON_SetNumberValue(sshConnections, number); + + //writing the new JSON to the configuration file + stringConfiguration = cJSON_Print(jsonConfig); + writeConfigFile(stringConfiguration); + + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + + cJSON_Delete(jsonConfig); + + return SR_ERR_OK; +} + +int tls_connections_changed(int number) + { + char *stringConfiguration = readConfigFileInString(); + + if (stringConfiguration == NULL) + { + printf("Could not read configuration file!\n"); + return SR_ERR_OPERATION_FAILED; + } + + cJSON *jsonConfig = cJSON_Parse(stringConfiguration); + if (jsonConfig == NULL) + { + free(stringConfiguration); + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Could not parse JSON configuration! Error before: %s\n", error_ptr); + } + return SR_ERR_OPERATION_FAILED; + } + //we don't need the string anymore + free(stringConfiguration); + stringConfiguration = NULL; + + cJSON *tlsConnections = cJSON_GetObjectItemCaseSensitive(jsonConfig, "tls-connections"); + if (!cJSON_IsNumber(tlsConnections)) + { + printf("Configuration JSON is not as expected: tls-connections is not an object"); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + //we set the value of the tls-connections object + cJSON_SetNumberValue(tlsConnections, number); + + //writing the new JSON to the configuration file + stringConfiguration = cJSON_Print(jsonConfig); + writeConfigFile(stringConfiguration); + + if (stringConfiguration != NULL) + { + free(stringConfiguration); + stringConfiguration = NULL; + } + + cJSON_Delete(jsonConfig); + + return SR_ERR_OK; +} + +/* +curl -X POST -H 'Content-Type: application/json' -i http://localhost:5000/extend-ports --data '{"number-of-ports":12}' +*/ +int send_k8s_extend_port(void) +{ + int num_of_ports = getSshConnectionsFromConfigJson() + getTlsConnectionsFromConfigJson(); + + CURLcode res; + + curl_easy_reset(curl_k8s); + set_curl_common_info_k8s(); + + char url_for_curl[100]; + sprintf(url_for_curl, "http://localhost:5000/extend-ports"); + + curl_easy_setopt(curl_k8s, CURLOPT_URL, url_for_curl); + + char post_data_json[1500]; + + sprintf(post_data_json, + "{\"number-of-ports\":%d}", + num_of_ports); + + printf("Post data:\n%s\n", post_data_json); + + curl_easy_setopt(curl_k8s, CURLOPT_POSTFIELDS, post_data_json); + curl_easy_setopt(curl_k8s, CURLOPT_CUSTOMREQUEST, "POST"); + + res = curl_easy_perform(curl_k8s); + if (res != CURLE_OK) + { + printf("cURL failed to url=%s\n", url_for_curl); + } + + long http_response_code = 0; + curl_easy_getinfo (curl_k8s, CURLINFO_RESPONSE_CODE, &http_response_code); + if (http_response_code >= 200 && http_response_code <= 226 && http_response_code != CURLE_ABORTED_BY_CALLBACK) + { + printf("cURL succeeded to url=%s\n", url_for_curl); + } + else + { + printf("cURL to url=%s failed with code=%ld\n", url_for_curl, http_response_code); + return SR_ERR_OPERATION_FAILED; + } + + return SR_ERR_OK; +} + +/* +curl -X POST -H 'Content-Type: application/json' -i http://localhost:5000/scale --data '{"simulatedDevices":2}' +*/ +int send_k8s_scale(int number_of_devices) +{ + CURLcode res; + + curl_easy_reset(curl_k8s); + set_curl_common_info_k8s(); + + char url_for_curl[100]; + sprintf(url_for_curl, "http://localhost:5000/scale"); + + curl_easy_setopt(curl_k8s, CURLOPT_URL, url_for_curl); + + char post_data_json[1500]; + + sprintf(post_data_json, + "{\"simulatedDevices\":%d}", + number_of_devices); + + printf("Post data:\n%s\n", post_data_json); + + curl_easy_setopt(curl_k8s, CURLOPT_POSTFIELDS, post_data_json); + curl_easy_setopt(curl_k8s, CURLOPT_CUSTOMREQUEST, "POST"); + + res = curl_easy_perform(curl_k8s); + if (res != CURLE_OK) + { + printf("cURL failed to url=%s\n", url_for_curl); + } + + long http_response_code = 0; + curl_easy_getinfo (curl_k8s, CURLINFO_RESPONSE_CODE, &http_response_code); + if (http_response_code >= 200 && http_response_code <= 226 && http_response_code != CURLE_ABORTED_BY_CALLBACK) + { + printf("cURL succeeded to url=%s\n", url_for_curl); + } + else + { + printf("cURL to url=%s failed with code=%ld\n", url_for_curl, http_response_code); + return SR_ERR_OPERATION_FAILED; + } + + return SR_ERR_OK; +} \ No newline at end of file diff --git a/ntsimulator/src/ntsimulator-manager/simulator-operations.h b/ntsimulator/src/ntsimulator-manager/simulator-operations.h index 3b93e48..3540eb3 100644 --- a/ntsimulator/src/ntsimulator-manager/simulator-operations.h +++ b/ntsimulator/src/ntsimulator-manager/simulator-operations.h @@ -38,6 +38,7 @@ typedef struct device { char *device_id; int netconf_port; + int device_number; bool is_mounted; char *operational_state; struct device *next; @@ -61,10 +62,11 @@ typedef struct controller device_stack_t *new_device_stack(void); -void push_device(device_stack_t *theStack, char *dev_id, int port); +void push_device(device_stack_t *theStack, char *dev_id, int port, int dev_num); void pop_device(device_stack_t *theStack); int get_netconf_port_next(device_stack_t *theStack); int get_netconf_port_base(void); +int get_device_number_next(device_stack_t *theStack); char *get_id_last_device(device_stack_t *theStack); int get_current_number_of_devices(device_stack_t *theStack); int get_current_number_of_mounted_devices(device_stack_t *theStack); @@ -85,6 +87,9 @@ int cleanup_curl(void); int _init_curl_odl(void); int cleanup_curl_odl(void); +int _init_curl_k8s(void); +int cleanup_curl_k8s(void); + int start_device(device_stack_t *theStack); int stop_device(device_stack_t *theStack); @@ -103,10 +108,13 @@ int ves_port_changed(int new_port); int ves_registration_changed(cJSON_bool new_bool); int is_netconf_available_changed(cJSON_bool new_bool); int is_ves_available_changed(cJSON_bool new_bool); - +int ssh_connections_changed(int number); +int tls_connections_changed(int number); int add_key_pair_to_odl(controller_t *controller_list, int controller_list_size); +int send_k8s_scale(int number_of_devices); +int send_k8s_extend_port(void); #endif /* SRC_NTSIMULATOR_MANAGER_SIMULATOR_OPERATIONS_H_ */ diff --git a/ntsimulator/src/o-ran-notifications/o-ran-notifications.c b/ntsimulator/src/o-ran-notifications/o-ran-notifications.c index a46ae04..3699e28 100644 --- a/ntsimulator/src/o-ran-notifications/o-ran-notifications.c +++ b/ntsimulator/src/o-ran-notifications/o-ran-notifications.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "sysrepo.h" #include "sysrepo/values.h" @@ -99,7 +100,6 @@ static int cleanup_curl() static int send_fault_ves_message(char *alarm_condition, char *alarm_object, char *severity, char *date_time, char *specific_problem, int port) { - int rc = SR_ERR_OK; CURLcode res; static int sequence_id = 0; int netconf_port_base = 0; @@ -112,23 +112,14 @@ static int send_fault_ves_message(char *alarm_condition, char *alarm_object, cha if (event == NULL) { printf("Could not create JSON object: event\n"); - return 1; + return SR_ERR_OPERATION_FAILED; } cJSON_AddItemToObject(postDataJson, "event", event); char *hostname = getenv("HOSTNAME"); - char *netconf_base_string = getenv("NETCONF_BASE"); - if (netconf_base_string != NULL) - { - rc = sscanf(netconf_base_string, "%d", &netconf_port_base); - if (rc != 1) - { - printf("Could not find the NETCONF base port, aborting the PNF registration...\n"); - return 1; - } - netconf_port_base += port; - } + netconf_port_base = getIntFromString(getenv("NETCONF_BASE"), 1); + netconf_port_base += port; char source_name[100]; sprintf(source_name, "%s_%d", hostname, netconf_port_base); @@ -137,7 +128,7 @@ static int send_fault_ves_message(char *alarm_condition, char *alarm_object, cha if (commonEventHeader == NULL) { printf("Could not create JSON object: commonEventHeader\n"); - return 1; + return SR_ERR_OPERATION_FAILED; } cJSON_AddItemToObject(event, "commonEventHeader", commonEventHeader); @@ -149,7 +140,7 @@ static int send_fault_ves_message(char *alarm_condition, char *alarm_object, cha { cJSON_Delete(postDataJson); } - return 1; + return SR_ERR_OPERATION_FAILED; } cJSON_AddItemToObject(event, "faultFields", faultFields); @@ -360,7 +351,6 @@ main(int argc, char **argv) sr_session_ctx_t *session = NULL; sr_subscription_ctx_t *subscription = NULL; int rc = SR_ERR_OK; - int notification_delay_period = 0; //seconds setbuf(stdout, NULL); diff --git a/ntsimulator/src/utils/utils.c b/ntsimulator/src/utils/utils.c index 15bb426..d98dac0 100644 --- a/ntsimulator/src/utils/utils.c +++ b/ntsimulator/src/utils/utils.c @@ -455,7 +455,6 @@ void writeConfigFile(char *config) int getFaultNotificationDelayPeriodFromConfigJson(int *period_array, int *count) { char *stringConfig = readConfigFileInString(); - int notificationDelay = 0; if (stringConfig == NULL) { @@ -1304,6 +1303,17 @@ int writeSkeletonStatusFile() writeStatusFile(status_string); + if (status_string != NULL) + { + free(status_string); + status_string = NULL; + } + + if (statusObject != NULL) + { + cJSON_Delete(statusObject); + } + return SR_ERR_OK; } @@ -1534,7 +1544,16 @@ int writeStatusNotificationCounters(counterAlarms ves_counter, counterAlarms net char *stringStatus = cJSON_PrintUnformatted(jsonStatus); writeStatusFile(stringStatus); - cJSON_Delete(jsonStatus); + if (stringStatus != NULL) + { + free(stringStatus); + stringStatus = NULL; + } + + if (jsonStatus != NULL) + { + cJSON_Delete(jsonStatus); + } return SR_ERR_OK; } @@ -1590,7 +1609,16 @@ int removeDeviceEntryFromStatusFile(char *containerId) char *stringStatus = cJSON_PrintUnformatted(jsonStatus); writeStatusFile(stringStatus); - cJSON_Delete(jsonStatus); + if (stringStatus != NULL) + { + free(stringStatus); + stringStatus = NULL; + } + + if (jsonStatus != NULL) + { + cJSON_Delete(jsonStatus); + } return SR_ERR_OK; } @@ -1757,6 +1785,9 @@ int getDeviceCounters(char *containerId, counterAlarms *ves_counter, counterAlar int array_size = cJSON_GetArraySize(deviceList); + ves_counter->critical = ves_counter->major = ves_counter->minor = ves_counter->warning = ves_counter->normal = 0; + netconf_counter->critical = netconf_counter->major = netconf_counter->minor = netconf_counter->warning = netconf_counter->normal = 0; + for (int i=0; ivaluedouble); + + cJSON_Delete(jsonConfig); + + return num_of_ssh; +} + +int getTlsConnectionsFromConfigJson(void) +{ + char *stringConfig = readConfigFileInString(); + + if (stringConfig == NULL) + { + printf("Could not read JSON configuration file in string."); + return 0; + } + + cJSON *jsonConfig = cJSON_Parse(stringConfig); + if (jsonConfig == NULL) + { + free(stringConfig); + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Could not parse JSON configuration! Error before: %s\n", error_ptr); + } + return SR_ERR_OPERATION_FAILED; + } + //we don't need the string anymore + free(stringConfig); + stringConfig = NULL; + + cJSON *tlsConnections = cJSON_GetObjectItemCaseSensitive(jsonConfig, "tls-connections"); + if (!cJSON_IsNumber(tlsConnections)) + { + printf("Configuration JSON is not as expected: ssh-connections is not an object"); + cJSON_Delete(jsonConfig); + return SR_ERR_OPERATION_FAILED; + } + + int num_of_tls = (int)(tlsConnections->valuedouble); + + cJSON_Delete(jsonConfig); + + return num_of_tls; +} diff --git a/ntsimulator/src/ves-messages/heartbeat.c b/ntsimulator/src/ves-messages/heartbeat.c index 29d69f8..09d7e02 100644 --- a/ntsimulator/src/ves-messages/heartbeat.c +++ b/ntsimulator/src/ves-messages/heartbeat.c @@ -241,7 +241,7 @@ static int send_pnf_registration_instance(char *hostname, int port, bool is_tls) return SR_ERR_OK; } -static void pnf_registration(void) +static void *pnf_registration(void *arg) { // delay the PNF Registration VES message, until anything else is initialized printf("delay the PNF Registration VES message, until anything else is initialized"); @@ -253,25 +253,17 @@ static void pnf_registration(void) { //ves-registration object is set to False, we do not make an automatic PNF registration printf("ves-registration object is set to False, we do not make an automatic PNF registration"); - return; + return NULL; } int rc = SR_ERR_OK, netconf_port_base = 0; - char *netconf_base_string = getenv("NETCONF_BASE"); char *hostname_string = getenv("HOSTNAME"); + int port = 0; - if (netconf_base_string != NULL) - { - rc = sscanf(netconf_base_string, "%d", &netconf_port_base); - if (rc != 1) - { - printf("Could not find the NETCONF base port, aborting the PNF registration...\n"); - return; - } - } + netconf_port_base = getIntFromString(getenv("NETCONF_BASE"), 0); //TODO This is where we hardcoded: 7 devices will have SSH connections and 3 devices will have TLS connections - for (int port = 0; port < NETCONF_CONNECTIONS_PER_DEVICE - 3; ++port) + for (int i = 0; i < SSH_CONNECTIONS_PER_DEVICE; ++port, ++i) { pthread_mutex_lock(&lock); rc = send_pnf_registration_instance(hostname_string, netconf_port_base + port, FALSE); @@ -281,7 +273,7 @@ static void pnf_registration(void) } pthread_mutex_unlock(&lock); } - for (int port = NETCONF_CONNECTIONS_PER_DEVICE - 3; port < NETCONF_CONNECTIONS_PER_DEVICE; ++port) + for (int i = 0; port < TLS_CONNECTIONS_PER_DEVICE; ++port, ++i) { pthread_mutex_lock(&lock); rc = send_pnf_registration_instance(hostname_string, netconf_port_base + port, TRUE); @@ -292,7 +284,7 @@ static void pnf_registration(void) } } - return; + return NULL; } int diff --git a/ntsimulator/yang/nts-manager/network-topology-simulator.xml b/ntsimulator/yang/nts-manager/network-topology-simulator.xml index 5f1b481..46fa4de 100644 --- a/ntsimulator/yang/nts-manager/network-topology-simulator.xml +++ b/ntsimulator/yang/nts-manager/network-topology-simulator.xml @@ -1,6 +1,8 @@ 0 0 + 1 + 0 0 0 diff --git a/ntsimulator/yang/nts-manager/network-topology-simulator.yang b/ntsimulator/yang/nts-manager/network-topology-simulator.yang index 1bc43cb..766a3ef 100644 --- a/ntsimulator/yang/nts-manager/network-topology-simulator.yang +++ b/ntsimulator/yang/nts-manager/network-topology-simulator.yang @@ -16,6 +16,12 @@ module network-topology-simulator { description "This module contains a collection of YANG definitions for managing the Network Topology Simulator."; + revision 2020-04-22 { + description + "Add configuration for number of SSH and TLS connections exposed by each simulated device."; + reference + "O-RAN SC SIM project"; + } revision 2020-04-13 { description "Change fault-notification-delay-period to leaf-list."; @@ -188,6 +194,10 @@ module network-topology-simulator { } container simulator-config { + must "./ssh-connections + ./tls-connections <= 100" { + error-message "The total number of connections (SSH + TLS) needs to be less than or equal with 100."; + } + // presence "Enables NTS configuration."; description "Configuration container of the simulator."; leaf simulated-devices { @@ -198,10 +208,27 @@ module network-topology-simulator { } leaf mounted-devices { type uint32; + must ". <= ../simulated-devices" { + error-message "The number of mounted devices cannot be greater that the number of simulated devices"; + } default "0"; description "The number of devices to be mounted in ODL. The configured number should not exceed the number of mounted devices."; } + leaf ssh-connections { + when "../simulated-devices = 0"; + type uint32; + default "1"; + description + "The number of SSH connections to be exposed by each simulated device."; + } + leaf tls-connections { + when "../simulated-devices = 0"; + type uint32; + default "0"; + description + "The number of TLS connections to be exposed by each simulated device."; + } container notification-config { leaf-list fault-notification-delay-period { type uint32; -- 2.16.6