From 8752ca5273e2f50ea631f80ee71b18b562e8a0ec Mon Sep 17 00:00:00 2001 From: HariomGupta Date: Wed, 30 Jun 2021 11:52:42 +0530 Subject: [PATCH] PNF Registration to be sent after odu stack is up Issue-Id: ODUHIGH-349 Signed-off-by: HariomGupta Change-Id: Icca5b097b02e144339914e49027ca4881856e88f --- Dockerfile | 19 ++----- build/config/netconfConfig.json | 2 +- build/config/netconf_server_ipv6.xml | 35 ++++++++++++ build/odu/makefile | 24 ++++----- build/scripts/add_netconf_user.sh | 29 ++++++++++ build/scripts/load_yang.sh | 39 ++++++++++++++ build/scripts/troubleshoot_netconf.sh | 33 ++++++++++++ docs/ODU-O1-Arch.jpg | Bin 34485 -> 34555 bytes docs/README | 73 ++++++++++--------------- docs/installation-guide.rst | 84 +++++++++-------------------- docs/overview.rst | 8 +-- src/du_app/du_mgr_main.c | 6 +++ src/o1/ConfigInterface.cpp | 31 +---------- src/o1/NrCellCb.cpp | 33 ++++++------ src/o1/NrCellInfo.cpp | 6 +-- src/o1/NrCellList.cpp | 2 +- src/o1/O1App.cpp | 26 +++++---- src/o1/O1App.hpp | 4 +- src/o1/O1Interface.cpp | 40 ++++++++++++-- src/o1/O1Interface.h | 1 + src/o1/ves/HttpClient.cpp | 28 +++++----- src/o1/ves/JsonHelper.cpp | 14 ++--- src/o1/ves/JsonHelper.hpp | 2 +- src/o1/ves/PnfRegistration.cpp | 56 ++++++++++--------- src/o1/ves/PnfRegistrationThread.cpp | 48 +++++++++++++++++ src/o1/ves/PnfRegistrationThread.hpp | 44 +++++++++++++++ src/o1/ves/VesCommonHeader.cpp | 25 +++++---- src/o1/ves/VesEvent.cpp | 56 ++++++------------- src/o1/ves/VesEvent.hpp | 2 - src/o1/ves/VesEventHandler.cpp | 98 ++++++++++++++++++++++++++-------- src/o1/ves/VesEventHandler.hpp | 10 ++-- 31 files changed, 542 insertions(+), 336 deletions(-) create mode 100644 build/config/netconf_server_ipv6.xml create mode 100755 build/scripts/add_netconf_user.sh create mode 100755 build/scripts/load_yang.sh create mode 100755 build/scripts/troubleshoot_netconf.sh create mode 100644 src/o1/ves/PnfRegistrationThread.cpp create mode 100644 src/o1/ves/PnfRegistrationThread.hpp diff --git a/Dockerfile b/Dockerfile index 02e0e275a..84b83831c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,6 @@ FROM nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-ubuntu18-c-go:1.9.0 # ====================================================================== -# add netconf user -RUN \ - adduser --system netconf && \ - echo "netconf:netconf!" | chpasswd - -# generate ssh keys for netconf user -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 - ADD . /opt/o-du-l2 WORKDIR /opt/o-du-l2 @@ -24,10 +12,13 @@ RUN cd build/odu && make clean_odu odu MACHINE=BIT64 MODE=FDD RUN cd build/odu && make clean_odu odu MACHINE=BIT64 MODE=TDD #CMD /opt/o-du-l2/bin/odu/odu +# add netconf user +RUN cd build/scripts && /bin/bash add_netconf_user.sh + #cleanup netconf folder and install libraries RUN cd build/scripts && /bin/bash install_lib_O1.sh -c -# Install the data models based on the ODU yang model -RUN /usr/local/bin/sysrepoctl -i build/yang/o-ran-sc-odu-alarm-v1.yang +# Install yang models and load initial configuration +RUN cd build/scripts && /bin/bash load_yang.sh RUN cd build/odu && make clean_odu odu MACHINE=BIT64 MODE=FDD O1_ENABLE=YES diff --git a/build/config/netconfConfig.json b/build/config/netconfConfig.json index 272f6869e..238d962ed 100644 --- a/build/config/netconfConfig.json +++ b/build/config/netconfConfig.json @@ -2,7 +2,7 @@ "NetconfServer": { "MacAddress": "02:42:f7:d4:62:ce", "NetconfServerIpv4": "10.0.2.132", - "NetconfServerIpv6": "0:0:0:0:0:ffff:a0a:013", + "NetconfServerIpv6": "2002:c0a8:3865::", "NetconfPort": "830", "NetconfUsername": "netconf", "NetconfPassword": "netconf!" diff --git a/build/config/netconf_server_ipv6.xml b/build/config/netconf_server_ipv6.xml new file mode 100644 index 000000000..d0b6c5704 --- /dev/null +++ b/build/config/netconf_server_ipv6.xml @@ -0,0 +1,35 @@ + + + + default-ssh + + + :: + + 1 + 10 + 5 + + + + + + default-key + + genkey + + + + + + + + interactive + + + + + + + + diff --git a/build/odu/makefile b/build/odu/makefile index 3ca892a59..868696e67 100644 --- a/build/odu/makefile +++ b/build/odu/makefile @@ -211,8 +211,8 @@ du: $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/mt.mak OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CC1)' $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/phy_stub.mak OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CC1)' ifeq ($(O1_ENABLE),YES) - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/o1.mak OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CCPP1)' - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/ves.mak OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/o1.mak OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/ves.mak OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CCPP1)' endif link_du: du @@ -232,8 +232,8 @@ clean_odu: $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/mt.mak clean OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CC1)' $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/phy_stub.mak clean OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CC1)' ifeq ($(O1_ENABLE),YES) - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/o1.mak clean OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CCPP1)' - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/ves.mak clean OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/o1.mak clean OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/ves.mak clean OBJ_DIR=$(OBJ_ROOT)/odu LIB_DIR=$(LIB_ROOT)/odu LOG_DIR=$(LOG_ROOT)/odu CC='$(CCPP1)' endif $(Q)rm -rf $(OBJ_ROOT)/odu/* $(Q)rm -rf $(LIB_ROOT)/odu/* @@ -257,8 +257,8 @@ cu: $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/cm.mak OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CC1)' $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/mt.mak OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CC1)' ifeq ($(O1_ENABLE),YES) - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/o1.mak OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CCPP1)' - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/ves.mak OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/o1.mak OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/ves.mak OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CCPP1)' endif clean_cu: @@ -269,8 +269,8 @@ clean_cu: $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/cm.mak clean OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CC1)' $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/mt.mak clean OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CC1)' ifeq ($(O1_ENABLE),YES) - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/o1.mak clean OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CCPP1)' - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/ves.mak clean OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/o1.mak clean OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/ves.mak clean OBJ_DIR=$(OBJ_ROOT)/cu_stub LIB_DIR=$(LIB_ROOT)/cu_stub LOG_DIR=$(LOG_ROOT)/cu_stub CC='$(CCPP1)' endif $(Q)rm -rf $(OBJ_ROOT)/cu_stub/* $(Q)rm -rf $(LIB_ROOT)/cu_stub/* @@ -293,8 +293,8 @@ ric: $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/cm.mak OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CC1)' $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/mt.mak OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CC1)' ifeq ($(O1_ENABLE),YES) - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/o1.mak OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CCPP1)' - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/ves.mak OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/o1.mak OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/ves.mak OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CCPP1)' endif clean_ric: @@ -305,8 +305,8 @@ clean_ric: $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/cm.mak clean OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CC1)' $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/mt.mak clean OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CC1)' ifeq ($(O1_ENABLE),YES) - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/o1.mak clean OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CCPP1)' - $(Q)$(MAKE) -f $(COM_BUILD_DIR)/ves.mak clean OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/o1.mak clean OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CCPP1)' + $(Q)$(MAKE) -j -f $(COM_BUILD_DIR)/ves.mak clean OBJ_DIR=$(OBJ_ROOT)/ric_stub LIB_DIR=$(LIB_ROOT)/ric_stub LOG_DIR=$(LOG_ROOT)/ric_stub CC='$(CCPP1)' endif $(Q)rm -rf $(OBJ_ROOT)/ric_stub/* $(Q)rm -rf $(LIB_ROOT)/ric_stub/* diff --git a/build/scripts/add_netconf_user.sh b/build/scripts/add_netconf_user.sh new file mode 100755 index 000000000..d0ea1fb75 --- /dev/null +++ b/build/scripts/add_netconf_user.sh @@ -0,0 +1,29 @@ +################################################################################ +# Copyright (c) [2020-2021] [HCL Technologies Ltd.] # +# # +# 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 will add new netconf user +#!/bin/bash + +adduser --system netconf && \ + echo "netconf:netconf!" | chpasswd + +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 + +################################################################################ +# End of file # +################################################################################ diff --git a/build/scripts/load_yang.sh b/build/scripts/load_yang.sh new file mode 100755 index 000000000..d837210c6 --- /dev/null +++ b/build/scripts/load_yang.sh @@ -0,0 +1,39 @@ +################################################################################ +# Copyright (c) [2020-2021] [HCL Technologies Ltd.] # +# # +# 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 install yang module and load initial configuration +#!/bin/bash + + +CURRENT_DIR=$PWD +ROOT_DIR=$CURRENT_DIR/../../ + +#load yand models +echo "### loading yang model ###" +sysrepoctl -i $ROOT_DIR/build/yang/o-ran-sc-odu-alarm-v1.yang +sysrepoctl -i $ROOT_DIR/build/yang/o-ran-sc-du-hello-world.yang +sysrepoctl -i $ROOT_DIR/build/yang/o-ran-sc-odu-interface-v1.yang +echo "### loading yang model Done###" + +#load initial configuration +echo "### loading initial configuration ###" +sysrepocfg --import=$ROOT_DIR/bin/odu/config/startup_config.xml -v 3 --datastore running --module o-ran-sc-odu-interface-v1 +sysrepocfg --import=$ROOT_DIR/bin/odu/config/nacm_config.xml -v 3 --datastore running --module ietf-netconf-acm +sysrepocfg --import=$ROOT_DIR/bin/odu/config/netconf_server_ipv6.xml -v 3 --datastore running --module ietf-netconf-server +echo "### loading initial configuration done ###" + +################################################################################ +# End of file # +################################################################################ diff --git a/build/scripts/troubleshoot_netconf.sh b/build/scripts/troubleshoot_netconf.sh new file mode 100755 index 000000000..08e6805cb --- /dev/null +++ b/build/scripts/troubleshoot_netconf.sh @@ -0,0 +1,33 @@ +################################################################################ +# Copyright (c) [2020-2021] [HCL Technologies Ltd.] # +# # +# 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 clean-up sysrepo databas +#!/bin/bash + +CURRENT_DIR=$PWD +ROOT_DIR=$CURRENT_DIR/../.. + +if [ "$1" = "cleanup" ]; then + kill -9 `pidof netopeer2-server` + cd $ROOT_DIR/build/netconf/sysrepo/build/ + make sr_clean + cd $ROOT_DIR/build/netconf/Netopeer2/build/ + make install + cd $CURRENT_DIR +fi + +################################################################################ +# End of file # +################################################################################ diff --git a/docs/ODU-O1-Arch.jpg b/docs/ODU-O1-Arch.jpg index 7c5c75776f67d92a30896b44dbd125ba5afaf1df..234cdd1ea95eeee8bb9fac2e57f61172f0160f8a 100644 GIT binary patch literal 34555 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

F9Pkf1C%}j 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 diff --git a/docs/README b/docs/README index 9eb9fe7b4..741ec5e3b 100644 --- a/docs/README +++ b/docs/README @@ -37,58 +37,31 @@ C. Pre-requisite for O1 Interface (Required only if run with O1 interface enable ----------------------------------------------------------------------------------- 1. Setup netconf server - Create new netconf user (login with root user and run following commands) - $adduser --system netconf && \ - echo "netconf:netconf!" | chpasswd - - $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 - Install netconf packages. - $cd l2/build/scripts - $chmod +x install_lib_O1.sh - $ ./install_lib_O1.sh -c - -2. Start Netopeer2-server: - $cd l2/build/scripts - $./netopeer-server.sh start - -3. Install the yang modules - $cd l2/build/yang - $sysrepoctl -i o-ran-sc-odu-alarm-v1.yang - $sysrepoctl -i o-ran-sc-odu-interface-v1.yang - $sysrepoctl -i o-ran-sc-du-hello-world.yang - -4. Configure the startup IP and Port configurations for DU, CU and RIC - - $cd l2/build/config - - Open the startup_config.xml and edit the desired IP and Port for CU, DU and RIC. - Then load the configuration in the sysrepo running datastore using the command below - - $sysrepocfg --import=startup_config.xml --datastore running --module o-ran-sc-odu-interface-v1 - -5. Configure the netconf server details for VES PNF Event - - $cd l2/build/config + a. Add new netconf user (login with root user or use sudo and run following script) + $cd l2/build/scripts + $sudo ./add_netconf_user.sh - Open the netconfConfig.json and edit the desired MAC address, IP, Port, Username and Password for VES PNF Registration. + b. Install netconf packages. + $chmod +x install_lib_O1.sh + $sudo ./install_lib_O1.sh -c -6. Configure the VES server details to send VES Events +2. Update the configuration according to your setup. $cd l2/build/config - Open the vesConfig.json and edit the desired IP, Port, Username and Password to send VES Event. + a. Open the startup_config.xml and edit the desired IP and Port for CU, DU and RIC. + b. Open the nacm_config.xml and edit the desired user name to provide the access to that user. + c. Open the netconf_server_ipv6.xml and edit the desired netconf server configuration. + d. Open the vesConfig.json and edit the details of VES collector. + e. Open the netconfConfig.json and edit the details of Netopeer server. + f. Install the yang modules and load initial configuration -7. Configure the nacm module to provide access to new user - - $cd l2/build/config - - Open the nacm_config.xml and edit the desired user-name to provide the access to that user. - - $sysrepocfg --import=nacm_config.xml --datastore running --module ietf-netconf-acm + $cd l2/build/scripts + $sudo ./load_yang.sh +3. Start Netopeer2-server: + $cd l2/build/scripts + $sudo ./netopeer-server.sh start D. How to Clean and Build: @@ -175,7 +148,7 @@ F. How to execute: b. ifconfig :CU_STUB "192.168.130.82" c. ifconfig :RIC_STUB "192.168.130.80" -PS: If O1 interface is enabled, IP should match those configured in step C.4. +PS: If O1 interface is enabled, IP should match those configured in step C.2.a. 2. Execute CU Stub: a. CU execution folder: @@ -305,3 +278,11 @@ H. How to execute the Health Check : get alarm-list The XML output is a list of active alarms in the O-DU High system. + +G. Troubleshooting Netconf server issues +---------------------------------------- + In case the Netconf server and sysrepo breaks down, run the following steps: + + $cd l2/build/scripts + $sudo ./troubleshoot_netconf.sh cleanup + execute section C.2.f, C.3 again diff --git a/docs/installation-guide.rst b/docs/installation-guide.rst index e8c005b0a..1c998e9a7 100644 --- a/docs/installation-guide.rst +++ b/docs/installation-guide.rst @@ -112,84 +112,50 @@ Cloning code Setting up Netconf server ------------------------- - Following steps are required to compile ODU with O1 interface enabled: - -- Install Netconf libraries: - - libssh, libyang, libnetconf2, sysrepo, netopeer2 - - Script is provided in the following folder to install these libraries - - - Ubuntu : - - | cd /l2/build/scripts - | sudo ./install_lib_O1.sh -c - -- Start Netopeer2-server: - - - Ubuntu : - | cd /l2/build/scripts - | sudo ./netopeer-server.sh start + Following steps are required to compile and run ODU with O1 interface enabled. + This requires SMO components (OAM and VES collector) to be running. - Create a new netconf user - - Switch to root user and run following commands - - - Ubuntu : - - | adduser --system netconf && \\ - | echo "netconf:netconf!" | chpasswd - | 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 - -- Install the YANG modules + Switch to root user or use sudo and run following commands - Ubuntu : + | cd /l2/build/scripts + | sudo ./add_netconf_user.sh + +- Install Netconf libraries: - | cd /l2/build/yang - | sysrepoctl -i ./yang/o-ran-sc-odu-alarm-v1.yang - | sysrepoctl -i ./yang/o-ran-sc-odu-interface-v1.yang - | sysrepoctl -i ./yang/o-ran-sc-du-hello-world.yang + libssh, libyang, libnetconf2, sysrepo, netopeer2 -- Configure the startup IP and Port configurations for DU, CU and RIC + Script is provided in the following folder to install these libraries - Ubuntu : + | cd /l2/build/scripts + | sudo ./install_lib_O1.sh -c - | cd /l2/build/config - | - | Open the startup_config.xml and edit the desired IP and Port for CU, DU and RIC. - | Then load the configuration in the sysrepo running datastore using the command below - | - | sysrepocfg --import=startup_config.xml --datastore running --module o-ran-sc-odu-interface-v1 +- Install the YANG modules and load initial configuration -- Configure the netconf server details for VES PNF Event + - Navigate to config folder and update the desired initial configuration - Ubuntu : + | cd /l2/build/config - | cd /l2/build/config - | - | Open the netconfConfig.json and edit the desired MAC address, IP, Port, Username and Password for VES PNF Registration. - -- Configure the VES server details to send VES Events + | Open the startup_config.xml and edit the desired IP and Port for CU, DU and RIC. + | Open the nacm_config.xml and edit the desired user name to provide the access to that user. + | Open the netconf_server_ipv6.xml and edit the desired netconf server configuration. + | Open the vesConfig.json and edit the details of VES collector. + | Open the netconfConfig.json and edit the details of Netopeer server. + | Install the yang modules and load initial configuration. - Ubuntu : + | cd /l2/build/scripts + | sudo ./load_yang.sh - | cd /l2/build/config - | - | Open the vesConfig.json and edit the desired IP, Port, Username and Password to send VES Event. - -- Configure the nacm module to provide access to new user +- Start Netopeer2-server: - Ubuntu : - - | cd /l2/build/config - | - | Open the nacm_config.xml and edit the desired user-name to provide the access to that user. - | - | $sysrepocfg --import=nacm_config.xml --datastore running --module ietf-netconf-acm + | cd /l2/build/scripts + | sudo ./netopeer-server.sh start Compilation diff --git a/docs/overview.rst b/docs/overview.rst index 371aab8a1..bed3972c0 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -111,14 +111,16 @@ As shown in figure 2 the O1 module runs as a thread in O-DU High. Alarm communic O1 architecture has following components: -- Session Handler: Subscribe to Netconf YANG modules and events. Register callback handler methods. +- Netconf Session Handler: Subscribe to Netconf YANG modules and events. Register callback handler methods. -- Alarm Manager: Stores and manages(add/updated/delete) alarms. +- VES Agent : Sends the VES events to SMO -- Unix socket server: Receives the alarm messages sent from O-DU High thread over Unix socket. +- Alarm Manager: Stores and manages(add/updated/delete) alarms. - Alarm Interface : Provides an interface to O-DU High threads for sending the alarm messages to O1 module over Unix socket. +- Config Interface : Interface to handle the configurations sent from SMO to the stack + - Netopeer server: Serves the northbound SMO/OAM Netconf requests. diff --git a/src/du_app/du_mgr_main.c b/src/du_app/du_mgr_main.c index d4988301c..14c810f0b 100644 --- a/src/du_app/du_mgr_main.c +++ b/src/du_app/du_mgr_main.c @@ -563,7 +563,13 @@ uint8_t tst(void) //Read all the configs from du_utils.c into duCfgParams duReadCfg(); +#ifdef O1_ENABLE + //Send VES PNF registration message to SMO + sendPnfRegistration(); +#endif + return ROK; + }/* end of main()*/ /********************************************************************** diff --git a/src/o1/ConfigInterface.cpp b/src/o1/ConfigInterface.cpp index 76c584de3..161d6caab 100644 --- a/src/o1/ConfigInterface.cpp +++ b/src/o1/ConfigInterface.cpp @@ -88,35 +88,6 @@ uint8_t getStartupConfig(StartupConfig *cfg) ******************************************************************/ uint8_t getStartupConfigForStub(StartupConfig *cfg) { -#if 0 - UnixSocketClient uxClient(O1::ALARM_SOCK_PATH); - O1_LOG("\nO1 CONFIG : getStartupConfig ------ \n"); - MsgHeader msg; - msg.msgType = CONFIGURATION; - msg.action = GET_STARTUP_CONFIG; - if (uxClient.openSocket() == O1::FAILURE) - { - return O1::FAILURE; - } - if (uxClient.sendData(&msg,sizeof(msg)) < 0 ) - { - uxClient.closeSocket(); - return O1::FAILURE; - } - if (uxClient.receiveData(cfg, sizeof(StartupConfig)) < 0) - { - uxClient.closeSocket(); - return O1::FAILURE; - } - O1_LOG("\nO1 CONFIG : ip du %s\n",cfg->DU_IPV4_Addr ); - O1_LOG("\nO1 CONFIG : ip cu %s\n",cfg->CU_IPV4_Addr ); - O1_LOG("\nO1 CONFIG : ip ric %s\n",cfg->RIC_IPV4_Addr ); - O1_LOG("\nO1 CONFIG : port cu %hu\n",cfg->CU_Port); - O1_LOG("\nO1 CONFIG : port du %hu\n",cfg->DU_Port); - O1_LOG("\nO1 CONFIG : port ric %hu\n",cfg->RIC_Port); - - uxClient.closeSocket(); -#endif SessionHandler sessHdlr; if ( sessHdlr.init() ) { @@ -166,7 +137,7 @@ uint8_t getStartupConfigForStub(StartupConfig *cfg) bool setCellOpState(uint16_t cellId, OpState opState, CellState cellState) { O1_LOG("\nO1 ConfigInterface: Setting cellId = %d, opState=%d, \ -cellState=%d\n", cellId, opState, cellState); +cellState=%d", cellId, opState, cellState); return NrCellList::instance().setCellOpState(cellId, opState, \ cellState); } diff --git a/src/o1/NrCellCb.cpp b/src/o1/NrCellCb.cpp index e97358b49..4d7365d74 100644 --- a/src/o1/NrCellCb.cpp +++ b/src/o1/NrCellCb.cpp @@ -54,7 +54,7 @@ int NrCellCb::oper_get_items(sysrepo::S_Session session, \ libyang::S_Data_Node &parent, \ void *private_data) { - O1_LOG("O1 NrCellCb : Callback called for path=%s on get request", path); + O1_LOG("\nO1 NrCellCb : Callback called for path=%s on get request", path); libyang::S_Context ctx = session->get_context(); libyang::S_Module mod = ctx->get_module(module_name); @@ -76,7 +76,7 @@ int NrCellCb::oper_get_items(sysrepo::S_Session session, \ std::map::const_iterator it; for(it = cellOpStateMap.begin(); it !=cellOpStateMap.end(); it++) { - O1_LOG("\nO1 NrCellCb : cellId = %d, opState=%d, cellState=%d\n", \ + O1_LOG("\nO1 NrCellCb : cellId = %d, opState=%d, cellState=%d", \ it->first, (int) it->second.getOpState(), (int) it->second.getCellState()); name.reset(new libyang::Data_Node(connection, mod, "name", \ to_string(it->first).c_str())); @@ -109,31 +109,31 @@ void NrCellCb::printChange(sysrepo::S_Change change) { switch(change->oper()) { case SR_OP_CREATED: if (nullptr != change->new_val()) { - O1_LOG("O1 NrCellCb : CREATED: %s", \ + O1_LOG("\nO1 NrCellCb : CREATED: %s", \ change->new_val()->to_string().c_str()); } break; case SR_OP_DELETED: if (nullptr != change->old_val()) { - O1_LOG("O1 NrCellCb : DELETED: %s", \ + O1_LOG("\nO1 NrCellCb : DELETED: %s", \ change->old_val()->to_string().c_str()); } break; case SR_OP_MODIFIED: if (nullptr != change->old_val() && nullptr != change->new_val()) { - O1_LOG("O1 NrCellCb : MODIFIED: old value %s :new value %s", \ + O1_LOG("\nO1 NrCellCb : MODIFIED: old value %s :new value %s", \ change->old_val()->to_string().c_str(), \ change->new_val()->to_string().c_str()); } break; case SR_OP_MOVED: if (nullptr != change->old_val() && nullptr != change->new_val()) { - O1_LOG("O1 NrCellCb : MOVED: %s :after %s ", \ + O1_LOG("\nO1 NrCellCb : MOVED: %s :after %s ", \ change->new_val()->xpath(), \ change->old_val()->xpath()); } else if (nullptr != change->new_val()) { - O1_LOG("O1 NrCellCb : MOVED: %s : first\n", \ + O1_LOG("\nO1 NrCellCb : MOVED: %s : first", \ change->new_val()->xpath()); } break; @@ -199,7 +199,7 @@ int NrCellCb::module_change(sysrepo::S_Session sess, \ char change_path[MAX_LEN]; try { - O1_LOG("O1 NrCellCb : Notification %s\n", evToStr(event)); + O1_LOG("\nO1 NrCellCb : Notification %s", evToStr(event)); if (SR_EV_CHANGE == event) { NrCellList & cellList = NrCellList::instance(); @@ -212,15 +212,15 @@ int NrCellCb::module_change(sysrepo::S_Session sess, \ //printChange(change); //enable only for debugging if(nullptr != change->new_val()) { - O1_LOG("O1 NrCellCb : Parameter value has been \ -changed val=%s\n", change->new_val()->val_to_string().c_str()); + O1_LOG("\nO1 NrCellCb : Parameter value has been \ +changed val=%s", change->new_val()->val_to_string().c_str()); std::map::const_iterator it; for(it = cellOpStateMap.begin(); it !=cellOpStateMap.end(); it++) { stringstream xpath; xpath << CELL_STATE_MODULE_PATH << "/du-to-ru-connection[name='" \ << it->first << "']/administrative-state"; - O1_LOG("O1 NrCellCb : created xpath = %s", \ + O1_LOG("\nO1 NrCellCb : created xpath = %s", \ xpath.str().c_str()); if((change->new_val()->to_string().find(xpath.str().c_str()) != \ @@ -229,11 +229,11 @@ changed val=%s\n", change->new_val()->val_to_string().c_str()); printChange(change); string val = change->new_val()->val_to_string(); AdminState newVal = cellInfo.adminStateToEnum(val); - O1_LOG("O1 NrCellCb : Update admin state \ -cellId =%d with admin-state value=%s\n", it->first, val.c_str()); + O1_LOG("\nO1 NrCellCb : Update admin state \ +cellId =%d with admin-state value=%s", it->first, val.c_str()); if(!setAdminState(it->first, newVal)) { - O1_LOG("O1 NrCellCb : Could not change \ -parameter value =%s\n", change->new_val()->val_to_string().c_str()); + O1_LOG("\nO1 NrCellCb : Could not change \ +parameter value =%s", change->new_val()->val_to_string().c_str()); return SR_ERR_INTERNAL; } } @@ -243,7 +243,7 @@ parameter value =%s\n", change->new_val()->val_to_string().c_str()); }//if evToStr(event) check } catch( const std::exception& e ) { - O1_LOG("exception : %s\n", e.what()); + O1_LOG("\nO1 NrCellCb exception : %s\n", e.what()); } return SR_ERR_OK; } @@ -287,4 +287,3 @@ bool NrCellCb::setAdminState(uint16_t cellId, AdminState newAdminState) /********************************************************************** End of file **********************************************************************/ - diff --git a/src/o1/NrCellInfo.cpp b/src/o1/NrCellInfo.cpp index 6fc55161b..4c9aef85b 100644 --- a/src/o1/NrCellInfo.cpp +++ b/src/o1/NrCellInfo.cpp @@ -48,7 +48,7 @@ AdminState NrCellInfo::adminStateToEnum(string val) else if(val == "SHUTTING_DOWN") ret = SHUTTING_DOWN; else - O1_LOG("O1 NrCellInfo : %s admin state not handled\n", \ + O1_LOG("\nO1 NrCellInfo : %s admin state not handled", \ val.c_str()); return ret; @@ -86,7 +86,7 @@ string NrCellInfo::enumToCellStateString(CellState val) ret = "IDLE"; break; default : - O1_LOG("O1 NrCellInfo : %d cell state not handled\n", val); + O1_LOG("\nO1 NrCellInfo : %d cell state not handled", val); } return ret; @@ -120,7 +120,7 @@ string NrCellInfo::enumToOperationalStateString(OpState val) ret = "ENABLED"; break; default : - O1_LOG("O1 NrCellInfo : %d operational state not handled\n", val); + O1_LOG("\nO1 NrCellInfo : %d operational state not handled", val); } return ret; diff --git a/src/o1/NrCellList.cpp b/src/o1/NrCellList.cpp index 9ea92e08f..eec8b0b30 100644 --- a/src/o1/NrCellList.cpp +++ b/src/o1/NrCellList.cpp @@ -49,7 +49,7 @@ bool NrCellList::setCellOpState(uint16_t cellId, \ CellState cellState) { O1_LOG("\nO1 NrCellList : Setting cellId = %d, opState=%d, \ -cellState=%d\n", cellId, opState, cellState); +cellState=%d", cellId, opState, cellState); NrCellInfo cellInfo ; cellInfo.setCellId( cellId); cellInfo.setOpState(opState); diff --git a/src/o1/O1App.cpp b/src/o1/O1App.cpp index 05bdf7ed9..5b1382160 100644 --- a/src/o1/O1App.cpp +++ b/src/o1/O1App.cpp @@ -29,6 +29,8 @@ #include "VesUtils.hpp" #include "VesEventHandler.hpp" + + /******************************************************************* * * @brief Constructor @@ -89,16 +91,12 @@ O1App::~O1App() bool O1App::run() { - + const int SLEEP_INTERVAL = 2; + const int DEFAUL_CELL_ID = 1; SessionHandler sessHdlr; - /*send ves PNF registration request*/ - VesEventHandler vesEvtHdr; - O1_LOG("\nO1 O1App : Sending VES Event"); - if(!vesEvtHdr.send(VesEventType::PNF_REGISTRATION)) - { - O1_LOG("\nO1 O1App : Could not send VES Request"); - return false; - } + + /*setting default cell state disabled*/ + setCellOpState(DEFAUL_CELL_ID, DISABLED, INACTIVE); /* Start Netconf session and subscribe to yang modules */ try @@ -121,19 +119,19 @@ bool O1App::run() if(mUxSocketServer.setAffinity(O1::CPU_CORE)) { - O1_LOG("\nO1 O1App : CPU affinity set " ); + O1_LOG("\nO1 O1App : CPU affinity set for UnixSocketServer thread to " ); mUxSocketServer.printAffinity(); } - sleep(2); + sleep(SLEEP_INTERVAL); if( mUxSocketServer.isRunning() ) { mStartupStatus = true; - O1_LOG("\nO1 O1App : Unix Socket server started\n"); + O1_LOG("\nO1 O1App : Unix Socket server started"); } else { - O1_LOG("\nO1 O1App : Unix Socket server failed to start\n"); + O1_LOG("\nO1 O1App : Unix Socket server failed to start"); return false; } /* Wait for the Unix Socket Server thread to end*/ @@ -141,7 +139,7 @@ bool O1App::run() } else { - O1_LOG("\nO1 O1App : Unix Socket server failed to start\n"); + O1_LOG("\nO1 O1App : Unix Socket server failed to start"); return false; } return true; diff --git a/src/o1/O1App.hpp b/src/o1/O1App.hpp index b1653d3f3..56eb02a0a 100644 --- a/src/o1/O1App.hpp +++ b/src/o1/O1App.hpp @@ -38,11 +38,11 @@ class O1App : public Singleton, public Thread UnixSocketServer mUxSocketServer; protected: + O1App(); + ~O1App(); bool run(); public: - O1App(); - ~O1App(); bool getStartupStatus()const; void cleanUp(void); }; diff --git a/src/o1/O1Interface.cpp b/src/o1/O1Interface.cpp index c3135b53a..c0c552a38 100644 --- a/src/o1/O1Interface.cpp +++ b/src/o1/O1Interface.cpp @@ -21,6 +21,8 @@ #include "O1Interface.h" #include "O1App.hpp" #include "GlobalDefs.hpp" +#include "PnfRegistrationThread.hpp" + #include #include @@ -43,7 +45,11 @@ ******************************************************************/ int static check_O1_module_status(void){ - for( int i = 0; i < 5 ; i++) + + const int N_RETRY = 5; + const int RETRY_DELAY = 1; + + for( int i = 0; i < N_RETRY; i++) { if( O1App::instance().getStartupStatus() == true) { @@ -51,7 +57,7 @@ int static check_O1_module_status(void){ } else { - sleep(1); + sleep(RETRY_DELAY); } } @@ -80,18 +86,44 @@ int start_O1_module(void) { if(O1App::instance().start() == false){ - O1_LOG("\nO1 O1Interface : Failed to start"); + O1_LOG("\nO1 O1Interface : Failed to start"); return O1::FAILURE; } if(O1App::instance().setAffinity(O1::CPU_CORE)) { - O1_LOG("\nO1 O1Interface : CPU affinity set " ); + O1_LOG("\nO1 O1Interface : CPU affinity set for O1App thread to" ); O1App::instance().printAffinity(); } return check_O1_module_status(); } +/******************************************************************* + * + * @brief Send VES PNF Registration Request + * + * @details + * + * Function : sendPnfRegistration + * + * Functionality: + * - Create a thread and send VES PNF Registration Request + * + * + * @params[in] void + * @return void + ******************************************************************/ + +void sendPnfRegistration(void) +{ + PnfRegistrationThread::instance().start(); + if(PnfRegistrationThread::instance().setAffinity(O1::CPU_CORE)) + { + O1_LOG("\nO1 O1Interface : CPU affinity set for PnfRegistration thread to " ); + PnfRegistrationThread::instance().printAffinity(); + } +} + /********************************************************************** End of file diff --git a/src/o1/O1Interface.h b/src/o1/O1Interface.h index cff161da6..e4e04d8ef 100644 --- a/src/o1/O1Interface.h +++ b/src/o1/O1Interface.h @@ -26,6 +26,7 @@ extern "C" { #endif int start_O1_module(void); +void sendPnfRegistration(void); #ifdef __cplusplus } diff --git a/src/o1/ves/HttpClient.cpp b/src/o1/ves/HttpClient.cpp index 4fc675cb7..261f5aed8 100644 --- a/src/o1/ves/HttpClient.cpp +++ b/src/o1/ves/HttpClient.cpp @@ -89,28 +89,28 @@ bool HttpClient::prepareHttpHeader(struct curl_slist *header, CURL *curl) if(!setUserPassword(curl)) { - O1_LOG("O1 VES : unable to set user:password \n"); + O1_LOG("\nO1 HttpClient : unable to set user:password"); curl_slist_free_all(header); } setCurlOptions(curl); header = curl_slist_append(header, "Content-Type: application/json"); if(!header) { - O1_LOG("O1 VES : curl_slist_append failed\n"); + O1_LOG("\nO1 HttpClient : curl_slist_append failed"); curl_easy_cleanup(curl); return false; } header = curl_slist_append(header, "Accept: application/json"); if(!header) { - O1_LOG("O1 VES : curl_slist_append failed\n"); + O1_LOG("\nO1 HttpClient : curl_slist_append failed"); curl_easy_cleanup(curl); return false; } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header); if(!createUrl(curl)) { - O1_LOG("O1 VES : could not create URL\n"); + O1_LOG("\nO1 HttpClient : could not create URL"); return false; } return true; @@ -142,7 +142,7 @@ bool HttpClient::setUserPassword(CURL *curl) if((mServerUsername.c_str()) && (mServerPassword.c_str())) { asprintf(&credentials, "%s:%s", mServerUsername.c_str(), mServerPassword.c_str()); if(credentials == 0) { - O1_LOG("O1 VES : credentials is blank\n"); + O1_LOG("\nO1 HttpClient : Credential is blank"); curl_easy_cleanup(curl); return false; } @@ -181,7 +181,7 @@ bool HttpClient::createUrl(CURL *curl) if((mServerIp.c_str()) && (mServerPort.c_str())) { oss <<"https://"<res, mem->size + realsize + 1); if(ptr == NULL) { - O1_LOG("O1 VES : realloc failed"); + O1_LOG("\nO1 HttpClient : realloc failed"); return 0; } diff --git a/src/o1/ves/JsonHelper.cpp b/src/o1/ves/JsonHelper.cpp index 5eeb39f26..db8627511 100644 --- a/src/o1/ves/JsonHelper.cpp +++ b/src/o1/ves/JsonHelper.cpp @@ -223,15 +223,15 @@ const char *JsonHelper::getError() * * ****************************************************************/ -char* JsonHelper::getValue(cJSON *json, const char *node) +string JsonHelper::getValue(cJSON *json, const char *node) { cJSON *object; - char * value = NULL; + string value = ""; object = cJSON_GetObjectItem(json, node); if(object) { value = object->valuestring; - O1_LOG("O1 VES : [ %s] : [%s]\n",node, value ); + O1_LOG("O1 VES : [ %s] : [%s]\n",node, value.c_str() ); } else O1_LOG("O1 VES : node [ %s] not found\n",node); @@ -283,16 +283,16 @@ cJSON* JsonHelper::read(const char * fileName) std::fstream fs(fileName, std::ios::in | std::ios::binary); if (!fs) { - O1_LOG("O1 VES : json can NOT open file %s\n", fileName); - return NULL; + O1_LOG("\nO1 JsonHelper : Cannot open file %s", fileName); + return NULL; } std::stringstream iss; iss << fs.rdbuf(); - cJSON *json = cJSON_Parse(iss.str().c_str()); - return json; + cJSON *json = cJSON_Parse(iss.str().c_str()); + return json; } /********************************************************************** diff --git a/src/o1/ves/JsonHelper.hpp b/src/o1/ves/JsonHelper.hpp index 154677619..6509ddaf5 100644 --- a/src/o1/ves/JsonHelper.hpp +++ b/src/o1/ves/JsonHelper.hpp @@ -44,7 +44,7 @@ class JsonHelper static cJSON_bool addJsonNodeToObject(cJSON * parent, \ const char * nodeName, cJSON * node); static cJSON* read(const char * fileName); - static char* getValue(cJSON *json, const char *node); + static std::string getValue(cJSON *json, const char *node); static cJSON * getNode(cJSON *json, const char *node); static char *printUnformatted(cJSON * node); static char *print(cJSON * node); diff --git a/src/o1/ves/PnfRegistration.cpp b/src/o1/ves/PnfRegistration.cpp index 37eae20ca..27f692a7a 100644 --- a/src/o1/ves/PnfRegistration.cpp +++ b/src/o1/ves/PnfRegistration.cpp @@ -88,7 +88,7 @@ string PnfRegistration::getNetconfMacAddr() return mNetconfMacAddr; } else { - O1_LOG("O1 VES : could not get Netconf Mac Address\n"); + O1_LOG("\nO1 PnfRegistration : could not get Netconf Mac Address"); return ""; } } @@ -116,7 +116,7 @@ string PnfRegistration::getNetconfV4ServerIP() return mNetconfIpv4; } else { - O1_LOG("O1 VES : could not get Netconf IPv4 ip\n"); + O1_LOG("\nO1 PnfRegistration : could not get Netconf IPv4 ip"); return ""; } } @@ -144,7 +144,7 @@ string PnfRegistration::getNetconfPort() return mNetconfPort; } else { - O1_LOG("O1 VES : Could not get Netconf Port number\n"); + O1_LOG("\nO1 PnfRegistration : Could not get Netconf Port number"); return NETCONF_DEFAULT_PORT; } } @@ -173,7 +173,7 @@ string PnfRegistration::getUsername() return mNetconfUsername; } else { - O1_LOG("O1 VES : could not get Netconf username\n"); + O1_LOG("\nO1 PnfRegistration : could not get Netconf username"); return ""; } } @@ -201,7 +201,7 @@ string PnfRegistration::getPassword() return mNetconfPassword; } else { - O1_LOG("O1 VES : could not get Netconf password\n"); + O1_LOG("\nO1 PnfRegistration : could not get Netconf password"); return ""; } } @@ -228,7 +228,7 @@ string PnfRegistration::getNetconfV6ServerIP() return mNetconfIpv6; } else { - O1_LOG("O1 VES : could not get Netconf IPv6 ip\n"); + O1_LOG("\nO1 PnfRegistration : could not get Netconf IPv6 ip"); return ""; } } @@ -309,8 +309,8 @@ bool PnfRegistration::prepareEventFields() cJSON* pnfFields = this->mVesEventFields; if(!readConfigFile()) { - O1_LOG("O1 VES : config file reading failed\n"); - //return false; + O1_LOG("\nO1 PnfRegistration : Failed to read config file"); + return false; } if(JsonHelper::addNodeToObject(pnfFields, "pnfRegistrationFieldsVersion", \ PNF_REGISTRATION_VERSION_2_1) == 0) { @@ -336,9 +336,10 @@ bool PnfRegistration::prepareEventFields() getNetconfV4ServerIP().c_str()) == 0) { ret = false; } - else if(JsonHelper::addNodeToObject(pnfFields, "oamV6IpAddress", \ - getNetconfV6ServerIP().c_str()) == 0) { - ret = false; + else if(getNetconfV6ServerIP() != "" && (JsonHelper::addNodeToObject( \ + pnfFields, "oamV6IpAddress", \ + getNetconfV6ServerIP().c_str()) == 0)) { + ret = false; } else if(JsonHelper::addNodeToObject(pnfFields, "serialNumber", \ getSerialNumber().c_str()) == 0) { @@ -364,29 +365,26 @@ bool PnfRegistration::prepareEventFields() { cJSON *addFields = JsonHelper::createNode(); if(addFields == 0) { - O1_LOG("O1 VES : could not create additional fields JSON object\n"); + O1_LOG("\nO1 PnfRegistration : could not create additional fields JSON object"); return false; } if(prepareAdditionalFields(addFields)) { - O1_LOG("O1 VES : PNF parameter prepration done adding additional parameters \ -if required \n"); - if(addFields == 0) { - O1_LOG("O1 VES : could not prepare additional fields cJSON object\n"); - JsonHelper::deleteNode(pnfFields); - return false; + if(addFields == 0) { + O1_LOG("\nO1 PnfRegistration : could not prepare additional fields cJSON object"); + JsonHelper::deleteNode(pnfFields); + return false; } - if(JsonHelper::addJsonNodeToObject(pnfFields, "additionalFields", \ + if(JsonHelper::addJsonNodeToObject(pnfFields, "additionalFields", \ addFields) == 0) { - O1_LOG("O1 VES : could not add additional fileds\n"); - JsonHelper::deleteNode(pnfFields); - return false; + O1_LOG("\nO1 PnfRegistration : could not add additional fields"); + JsonHelper::deleteNode(pnfFields); + return false; } - } - O1_LOG("O1 VES : Preparation on PNF registration additional fields done \ -successfully \n"); + } + O1_LOG("\nO1 PnfRegistration : Event fields prepared for PNF registration"); } return ret; } @@ -455,7 +453,7 @@ bool PnfRegistration::prepareAdditionalFields(cJSON *addFields) KEEPALIVE_DELAY_120) == 0) { ret = false; } - O1_LOG("O1 VES : additonal field preparation of PNF done successfully \n"); + O1_LOG("\nO1 PnfRegistration : Additional fields prepared for PNF registration"); return ret; } @@ -480,14 +478,14 @@ bool PnfRegistration::readConfigFile() { cJSON *json = JsonHelper::read(NETCONF_CONFIG); if(json == NULL) { - O1_LOG("O1 VES : Config file reading error is :%s\n", JsonHelper::getError()); + O1_LOG("\nO1 PnfRegistration : Config file reading error is :%s", JsonHelper::getError()); return false; } else { cJSON *rootNode = NULL; rootNode = JsonHelper::getNode(json, "NetconfServer"); if(rootNode) { - O1_LOG("O1 VES : Reading NetconfServer config file\n"); + O1_LOG("\nO1 PnfRegistration : Reading NetconfServer config file"); mNetconfMacAddr = JsonHelper::getValue(rootNode, "MacAddress"); mNetconfIpv4 = JsonHelper::getValue(rootNode, "NetconfServerIpv4"); mNetconfIpv6 = JsonHelper::getValue(rootNode, "NetconfServerIpv6"); @@ -496,7 +494,7 @@ bool PnfRegistration::readConfigFile() mNetconfPassword = JsonHelper::getValue(rootNode, "NetconfPassword"); } else { - O1_LOG("O1 VES : smoConfig Object is not availbale in config file\n"); + O1_LOG("\nO1 PnfRegistration : smoConfig Object is not available in config file"); return false; } } diff --git a/src/o1/ves/PnfRegistrationThread.cpp b/src/o1/ves/PnfRegistrationThread.cpp new file mode 100644 index 000000000..61583273f --- /dev/null +++ b/src/o1/ves/PnfRegistrationThread.cpp @@ -0,0 +1,48 @@ +/******************************************************************************* +################################################################################ +# Copyright (c) [2020-2021] [HCL Technologies Ltd.] # +# # +# 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 file contains the class which runs as a thread to send the PNF + registration message. +*/ + + +#include "PnfRegistrationThread.hpp" +#include "VesUtils.hpp" +#include "VesEventHandler.hpp" + +#include + + +bool PnfRegistrationThread::run() +{ + + const int N_RETRY = 2; + const int RETRY_DELAY = 10; + /* Prepare VES PNF registration request */ + VesEventHandler vesEvtHdr; + if(vesEvtHdr.prepare(VesEventType::PNF_REGISTRATION)) + { + /* Send VES PNF registration request */ + for( int i = 0; i < N_RETRY; i++) + { + sleep(RETRY_DELAY); + O1_LOG("\nO1 PnfRegistrationThread : Sending PNF registration. Attempt %d\n", i ); + vesEvtHdr.send(); + } + } +} diff --git a/src/o1/ves/PnfRegistrationThread.hpp b/src/o1/ves/PnfRegistrationThread.hpp new file mode 100644 index 000000000..607bd5f11 --- /dev/null +++ b/src/o1/ves/PnfRegistrationThread.hpp @@ -0,0 +1,44 @@ +/******************************************************************************* +################################################################################ +# Copyright (c) [2020-2021] [HCL Technologies Ltd.] # +# # +# 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 file contains the class which runs as a thread to send the PNF + registration message. +*/ + +#ifndef __PNF_REGISTRATION_THREAD_HPP__ +#define __PNF_REGISTRATION_THREAD_HPP__ + +#include "Thread.hpp" +#include "Singleton.hpp" + + +class PnfRegistrationThread : public Singleton, public Thread +{ + friend Singleton; + + protected: + PnfRegistrationThread(){} + ~PnfRegistrationThread(){} + bool run(); + + public: + void cleanUp(void){} +}; + + +#endif diff --git a/src/o1/ves/VesCommonHeader.cpp b/src/o1/ves/VesCommonHeader.cpp index 3c4b1f95b..864ea84ca 100644 --- a/src/o1/ves/VesCommonHeader.cpp +++ b/src/o1/ves/VesCommonHeader.cpp @@ -107,7 +107,7 @@ string VesCommonHeader::getEventTypeToStr() str = "heartbeat"; break; default: - O1_LOG("O1 VES : VES msg Type is not avilable\n"); + O1_LOG("\nO1 VesCommonHeader : VES msg Type not supported"); break; } return str; @@ -144,8 +144,8 @@ string VesCommonHeader::getEventId() evntId = getEventTypeToStr() + "_" + getCurrentTime(); break; default: - O1_LOG("O1 VES : this VES msg Type support in getEventId is \ -not available\n"); + O1_LOG("\nO1 VesCommonHeader : this VES msg Type support in getEventId is \ +not available"); break; } return evntId; @@ -184,8 +184,8 @@ string VesCommonHeader::getEventType() evntType = EVENT_TYPE_ORAN_COMPONENET; break; default: - O1_LOG("O1 VES : this VES msg Type support in getEvenType is \ -not available\n"); + O1_LOG("\nO1 VesCommonHeader : this VES msg Type support in getEvenType is \ +not available"); break; } return evntType; @@ -223,8 +223,8 @@ string VesCommonHeader::getPriority() evntId = PRIORITY_LOW; break; default: - O1_LOG("O1 VES : This VES msg Type support in getPriority is \ -not available\n"); + O1_LOG("\nO1 VesCommonHeader : This VES msg Type support in getPriority is \ +not available"); break; } return evntId; @@ -263,8 +263,8 @@ string VesCommonHeader::getEventName() evntName = getEventTypeToStr() + "_" + EVENT_TYPE_ORAN_COMPONENET; break; default: - O1_LOG("O1 VES : This VES msg Type support in getEventName is \ -not available\n"); + O1_LOG("\nO1 VesCommonHeader : This VES msg Type support in getEventName is \ +not available"); break; } return evntName; @@ -298,8 +298,8 @@ string VesCommonHeader::getReportingEntityName() evntName = getSourceName(); break; default: - O1_LOG("O1 VES : This VES msg Type support in \ -getReportingEntityName is not available\n"); + O1_LOG("\nO1 VesCommonHeader : This VES msg Type support in \ +getReportingEntityName is not available"); break; } return evntName; @@ -424,7 +424,6 @@ string VesCommonHeader::getCurrentTime() bool VesCommonHeader::prepare(cJSON *commonHeader, \ VesEventType type) { - O1_LOG("O1 VES : createCommonHeader fuction started\n"); bool ret=true; string typeStr; string evntId; @@ -518,7 +517,7 @@ bool VesCommonHeader::prepare(cJSON *commonHeader, \ } else { - O1_LOG("O1 VES : VES common Header prepared successfully \n"); + O1_LOG("\nO1 VesCommonHeader : VES common Header prepared"); } return ret; diff --git a/src/o1/ves/VesEvent.cpp b/src/o1/ves/VesEvent.cpp index 6565fdfb7..23a4d88ea 100644 --- a/src/o1/ves/VesEvent.cpp +++ b/src/o1/ves/VesEvent.cpp @@ -60,9 +60,8 @@ VesEvent::~VesEvent() bool VesEvent::prepare() { - O1_LOG("O1 VES : prepare PNF Registration start\n"); - if(!getVesCollectorDetails()) { - O1_LOG("O1 VES : Could not get successfully details of Ves Collector\n"); + if(!readConfigFile()) { + O1_LOG("\nO1 VesEvent : Could not get SMO details"); return false; } @@ -71,26 +70,26 @@ bool VesEvent::prepare() cJSON *rootNode = JsonHelper::createNode(); if(rootNode == 0) { - O1_LOG("O1 VES : could not create cJSON root Node object\n"); + O1_LOG("\nO1 VesEvent : could not create cJSON root Node object"); return false; } cJSON *event = JsonHelper::createNode(); if(event == 0) { - O1_LOG("O1 VES : could not create event cJSON object\n"); + O1_LOG("\nO1 VesEvent : could not create event cJSON object"); JsonHelper::deleteNode(rootNode); return false; } if(JsonHelper::addJsonNodeToObject(rootNode, "event", event) == 0) { - O1_LOG("O1 VES : could not add event Object\n"); + O1_LOG("\nO1 VesEvent : could not add event Object"); JsonHelper::deleteNode(rootNode); return false; } cJSON *commHdrNode = JsonHelper::createNode(); if(commHdrNode == 0) { - O1_LOG("O1 VES : could not create common header Node JSON object"); + O1_LOG("\nO1 VesEvent : could not create common header Node JSON object"); return false; } @@ -100,7 +99,7 @@ bool VesEvent::prepare() { if(JsonHelper::addJsonNodeToObject(event, "commonEventHeader", \ commHdrNode) == 0) { - O1_LOG("O1 VES : could not add commonEventHeader object\n"); + O1_LOG("\nO1 VesEvent : could not add commonEventHeader object"); JsonHelper::deleteNode(rootNode); return false; } @@ -109,29 +108,29 @@ bool VesEvent::prepare() //add header into the message and create pnfFields mVesEventFields = JsonHelper::createNode(); if(mVesEventFields == 0) { - O1_LOG("O1 VES : could not create Ves Event Fields JSON object\n"); + O1_LOG("\nO1 VesEvent : could not create Ves Event Fields JSON object"); return false; } if(!prepareEventFields()) { - O1_LOG("O1 VES : could not prepare Ves Event Fields Node \n"); + O1_LOG("\nO1 VesEvent : could not prepare Ves Event Fields Node"); JsonHelper::deleteNode(rootNode); return false; } if(JsonHelper::addJsonNodeToObject(event, "pnfRegistrationFields", mVesEventFields) == 0) { - O1_LOG("O1 VES : could not add mVesEventFields object\n"); + O1_LOG("\nO1 VesEvent : could not add mVesEventFields object"); JsonHelper::deleteNode(rootNode); return false; } mSendData = JsonHelper::printUnformatted(rootNode); - O1_LOG("O1 VES : final ves request : -- \n%s\n", JsonHelper::print(rootNode)); + O1_LOG("\nO1 VesEvent : VES request : -- \n%s\n", JsonHelper::print(rootNode)); } } else { - O1_LOG("O1 VES : preparePnfRegistration common header preparation failed\n"); + O1_LOG("\nO1 VesEvent : Failed to prepare preparePnfRegistration common header"); JsonHelper::deleteNode(rootNode); return false; } @@ -143,29 +142,6 @@ bool VesEvent::send() return mHttpClient->send(mSendData); } -/******************************************************************* - * - * @brief Gets the Ves Collector Server details - * - * @details - * - * Function : getVesCollectorDetails - * - * Functionality: - * - Gets the Ves Collector Server details by reading config file - * - * @params[in] IN - pointer to pnfFields - * @return true - success - * false - failure - * - * ****************************************************************/ - - -bool VesEvent::getVesCollectorDetails() -{ - return readConfigFile(); -} - /******************************************************************* * * @brief Read Ves Collector config json file @@ -187,21 +163,21 @@ bool VesEvent::readConfigFile() { cJSON *json = JsonHelper::read(VES_CONFIG); if(json == NULL) { - O1_LOG("O1 VES : Config file reading error is :%s\n", JsonHelper::getError()); - return false; + O1_LOG("\nO1 VesEvent : Error reading config file :%s", JsonHelper::getError()); + return false; } else { cJSON *rootNode = NULL; rootNode = JsonHelper::getNode(json, "vesConfig"); if(rootNode) { - O1_LOG("O1 VES : Reading smoConfig.json file\n"); + O1_LOG("\nO1 VesEvent : Reading smoConfig.json file\n"); mVesServerIp = JsonHelper::getValue(rootNode, "vesV4IpAddress"); mVesServerPort = JsonHelper::getValue(rootNode, "vesPort"); mVesServerUsername = JsonHelper::getValue(rootNode, "username"); mVesServerPassword = JsonHelper::getValue(rootNode, "password"); } else { - O1_LOG("O1 VES : smoConfig Object is not availbale in config file\n"); + O1_LOG("\nO1 VesEvent : smoConfig Object is not available in config file"); return false; } } diff --git a/src/o1/ves/VesEvent.hpp b/src/o1/ves/VesEvent.hpp index f6e746d80..c1254da9e 100644 --- a/src/o1/ves/VesEvent.hpp +++ b/src/o1/ves/VesEvent.hpp @@ -47,9 +47,7 @@ class VesEvent{ cJSON* mVesEventFields; private: - //Member variable bool readConfigFile(); - bool getVesCollectorDetails(); char * mSendData; HttpClient *mHttpClient; string mVesServerIp; diff --git a/src/o1/ves/VesEventHandler.cpp b/src/o1/ves/VesEventHandler.cpp index 18258e143..48e0ff62d 100644 --- a/src/o1/ves/VesEventHandler.cpp +++ b/src/o1/ves/VesEventHandler.cpp @@ -25,14 +25,53 @@ /******************************************************************* * - * @brief prepare and send Ves Message + * @brief Constructor * * @details * - * Function : sendVesEvent + * Function : VesEventHandler * * Functionality: - * - prepare VES event and send to oam + * - Constructor intialization + * + * @params[in] NULL + * @return None + ******************************************************************/ +VesEventHandler::VesEventHandler() : mVesEvent(NULL) +{ + +} + +/******************************************************************* + * + * @brief Destructor + * + * @details + * + * Function : ~VesEventHandler + * + * Functionality: + * - Destructor + * + * @params[in] None + * @return None + ******************************************************************/ +VesEventHandler::~VesEventHandler() +{ + if( mVesEvent != NULL ) + delete mVesEvent; +} + +/******************************************************************* + * + * @brief Prepare VES Message + * + * @details + * + * Function : prepare + * + * Functionality: + * - prepare VES event * * @params[in] void * @return true - success @@ -40,47 +79,64 @@ * * ****************************************************************/ -bool VesEventHandler::send(VesEventType evtType) +bool VesEventHandler::prepare(VesEventType evtType) { //check event type and call funtions accordingly bool ret = true; - //char *sendData; - O1_LOG("O1 VES : sendVesEvent started\n"); - VesEvent *vesEvent; - //common header switch(evtType) { case VesEventType::PNF_REGISTRATION: { - O1_LOG("O1 VES : send PNP registration\n"); - vesEvent = new PnfRegistration; + O1_LOG("\nO1 VesEventHandler : Preparing PNF registration"); + mVesEvent = new PnfRegistration; break; } case VesEventType::FAULT_NOTIFICATION: - O1_LOG("O1 VES : send VES fault notification\n"); + O1_LOG("\nO1 VesEventHandler : Preparing VES fault notification"); break; case VesEventType::PM_NOTIFICATION: - O1_LOG("O1 VES : send VES pm notification\n"); + O1_LOG("\nO1 VesEventHandler : Preparing VES pm notification"); break; case VesEventType::HEARTBEAT: - O1_LOG("O1 VES : send VES heartbeat \n"); + O1_LOG("\nO1 VesEventHandler : Preparing VES heartbeat"); break; default: - O1_LOG("O1 VES : send VES msg Type is not avilable\n"); + O1_LOG("\nO1 VesEventHandler : VES msg Type is not available"); ret = false; break; } - if(!vesEvent->prepare()) { - O1_LOG("O1 VES : could not send VES Event\n"); - ret = false; - } - else if (!vesEvent->send()) { - O1_LOG("O1 VES : could not send VES Event\n"); + if(!mVesEvent->prepare()) { + O1_LOG("\nO1 VesEventHandler : Failed to prepare VES message"); ret = false; } - delete vesEvent; return ret; } + +/******************************************************************* + * + * @brief Send Ves Message + * + * @details + * + * Function : send + * + * Functionality: + * - Send VES event to SMO + * + * @params[in] void + * @return true - success + * false - failure + * + * ****************************************************************/ + +bool VesEventHandler::send() +{ + if (!mVesEvent->send()) { + O1_LOG("\nO1 VesEventHandler : Failed to send VES event"); + return false; + } + return true; +} /********************************************************************** End of file **********************************************************************/ diff --git a/src/o1/ves/VesEventHandler.hpp b/src/o1/ves/VesEventHandler.hpp index 614967ac3..c3041909d 100644 --- a/src/o1/ves/VesEventHandler.hpp +++ b/src/o1/ves/VesEventHandler.hpp @@ -32,9 +32,13 @@ class VesEventHandler public: /* Default constructor/Destructor*/ - VesEventHandler(){} - ~VesEventHandler(){} - bool send(VesEventType evtType); + VesEventHandler(); + ~VesEventHandler(); + bool prepare(VesEventType evtType); + bool send(); + + private: + VesEvent *mVesEvent; }; -- 2.16.6