aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrej Kozemcak <andrej.kozemcak@pantheon.tech>2019-02-21 16:23:03 +0100
committerHongjun Ni <hongjun.ni@intel.com>2019-03-07 00:55:12 +0000
commit1b1eb0d115f5f3c047dc78862c17d39fb29beba5 (patch)
tree9fccaebab80def6f257f2dc6299fa265b858adaa
parent3430b042873f2eac94acd323125eaaa4a5f7f3e9 (diff)
Init gNMI server.
Change-Id: Ie898b5385096e735bf947775e0278c3c8f4797a8 Signed-off-by: Andrej Kozemcak <andrej.kozemcak@pantheon.tech>
-rw-r--r--Makefile87
-rw-r--r--src/cmake/FindJSONCPP.cmake56
-rw-r--r--src/cmake/FindPUGIXML.cmake52
-rw-r--r--src/gnmi/CMakeLists.txt73
-rw-r--r--src/gnmi/eventreciverbase.h78
-rw-r--r--src/gnmi/gnmidata.cpp112
-rw-r--r--src/gnmi/gnmidata.h71
-rw-r--r--src/gnmi/gnmiserver.cpp483
-rw-r--r--src/gnmi/gnmiserver.h88
-rw-r--r--src/gnmi/log.cpp28
-rw-r--r--src/gnmi/log.h35
-rw-r--r--src/gnmi/main.cpp222
-rw-r--r--src/gnmi/proto/CMakeLists.txt52
-rw-r--r--src/gnmi/proto/gnmi.proto457
-rw-r--r--src/gnmi/proto/gnmi_ext.proto74
-rw-r--r--src/gnmi/sysrepoapi.cpp419
-rw-r--r--src/gnmi/sysrepoapi.h109
-rw-r--r--src/gnmi/sysrepoapipool.cpp42
-rw-r--r--src/gnmi/sysrepoapipool.h48
-rw-r--r--src/gnmi/xml2json.cpp326
-rw-r--r--src/gnmi/xml2json.h91
21 files changed, 2990 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index 079ba4a..7933b38 100644
--- a/Makefile
+++ b/Makefile
@@ -40,10 +40,13 @@ endif
DEB_DEPENDS = curl build-essential autoconf automake ccache git
DEB_DEPENDS += bison flex libpcre3-dev libev-dev libavl-dev libprotobuf-c-dev protobuf-c-compiler libcmocka-dev
-DEB_DEPENDS += cmake ninja-build python-pkgconfig python-dev libssl-dev indent wget
+DEB_DEPENDS += cmake ninja-build python-pkgconfig python-dev libssl-dev indent wget zlib1g-dev
+
+DEB_GNMI_DEPENDS = libpugixml-dev libjsoncpp-dev libtool pkg-config golang libc-ares-dev libc-ares2
RPM_DEPENDS = curl autoconf automake ccache bison flex pcre-devel libev-devel protobuf-c-devel protobuf-c-compiler libcmocka-devel
RPM_DEPENDS += cmake ninja-build python-pkgconfig python-devel openssl-devel graphviz wget gcc gcc-c++ indent git cmake3
+RPM_GNMI_DEPENDS = pugixml jsoncpp c-ares c-ares-devel libtool pugixml-devel jsoncpp-devel devtoolset-8-gcc-c++
ifeq ($(findstring y,$(UNATTENDED)),y)
CONFIRM=-y
@@ -63,18 +66,20 @@ endef
help:
@echo "Make Targets:"
- @echo " install-dep - install software dependencies"
- @echo " install-dep-extra - install software extra dependencips from source code"
- @echo " install-vpp - install released vpp"
- @echo " checkstyle - check coding style"
- @echo " fixstyle - fix coding style"
- @echo " build-scvpp - build scvpp"
- @echo " build-plugins - build plugins"
- @echo " build-package - build rpm or deb package"
- @echo " docker - build sweetcomb in docker enviroment"
- @echo " docker_test - run test in docker enviroment"
- @echo " clean - clean all build"
- @echo " distclean - remove all build directory"
+ @echo " install-dep - install software dependencies"
+ @echo " install-dep-extra - install software extra dependencips from source code"
+ @echo " install-vpp - install released vpp"
+ @echo " install-dep-gnmi-extra - install software extra dependencips from source code for gNMI"
+ @echo " checkstyle - check coding style"
+ @echo " fixstyle - fix coding style"
+ @echo " build-scvpp - build scvpp"
+ @echo " build-plugins - build plugins"
+ @echo " build-gnmi - build gNMIServer"
+ @echo " build-package - build rpm or deb package"
+ @echo " docker - build sweetcomb in docker enviroment"
+ @echo " docker_test - run test in docker enviroment"
+ @echo " clean - clean all build"
+ @echo " distclean - remove all build directory"
$(BR)/.deps.ok:
ifeq ($(findstring y,$(UNATTENDED)),y)
make install-dep
@@ -177,6 +182,52 @@ install-dep-extra:
\
&&cd ../ && rm -rf $(BR)/downloads
+install-dep-gnmi-extra:
+ @rm -rf $(BR)/downloads
+ @mkdir -p $(BR)/downloads/
+ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID))
+ @sudo -E apt-get update
+ @sudo -E apt-get $(APT_ARGS) $(CONFIRM) $(FORCE) install $(DEB_GNMI_DEPENDS)
+ @sudo apt-get install -y software-properties-common
+ @sudo add-apt-repository ppa:ubuntu-toolchain-r/test
+ @sudo apt update
+ @sudo apt install g++-7 -y
+ @sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 \
+ --slave /usr/bin/g++ g++ /usr/bin/g++-7
+ @sudo update-alternatives --config gcc
+
+else ifeq ($(OS_ID),centos)
+ @sudo -E yum install $(CONFIRM) $(RPM_GNMI_DEPENDS)
+else
+ $(error "This option currently works only on Ubuntu, Debian, Centos or openSUSE systems")
+endif
+
+ @cd $(BR)/downloads/\
+ &&wget https://github.com/Kitware/CMake/releases/download/v3.14.0-rc2/cmake-3.14.0-rc2.tar.gz \
+ &&tar xvf cmake-3.14.0-rc2.tar.gz &&cd cmake-3.14.0-rc2\
+ &&./configure &&make &&make install\
+ \
+ &&cd $(BR)/downloads/\
+ &&wget https://github.com/c-ares/c-ares/releases/download/cares-1_15_0/c-ares-1.15.0.tar.gz\
+ &&tar xvf c-ares-1.15.0.tar.gz\
+ &&mkdir -p c-ares-1.15.0/build && cd c-ares-1.15.0/build\
+ &&cmake -DCMAKE_BUILD_TYPE=Release ../ &&make install\
+ \
+ &&cd $(BR)/downloads/\
+ &&wget https://github.com/protocolbuffers/protobuf/archive/v3.7.0rc2.tar.gz\
+ &&tar xvf v3.7.0rc2.tar.gz\
+ &&cd protobuf-3.7.0rc2 &&./autogen.sh &&./configure --prefix=/usr\
+ &&make &&make install &&ldconfig\
+ \
+ &&cd $(BR)/downloads/\
+ &&wget https://github.com/grpc/grpc/archive/v1.18.0.tar.gz\
+ &&tar xvf v1.18.0.tar.gz &&cd grpc-1.18.0 &&mkdir -p build && cd build\
+ &&cmake -DgRPC_INSTALL:BOOL=ON -DgRPC_BUILD_TESTS:BOOL=OFF \
+ -DgRPC_ZLIB_PROVIDER:STRING=package -DgRPC_CARES_PROVIDER:STRING=package \
+ -DgRPC_SSL_PROVIDER:STRING=package -DgRPC_PROTOBUF_PROVIDER=package \
+ -DCMAKE_BUILD_TYPE=Release ../\
+ &&make ../ &&make install
+
install-vpp:
@echo "please install vpp as vpp's guide from source if failed"
ifeq ($(PKG),deb)
@@ -207,6 +258,14 @@ docker:
docker_test:
@test/run_test.sh
+build-gnmi:
+ifeq ($(OS_ID),centos)
+ @source /opt/rh/devtoolset-8/enable \
+ &&mkdir -p $(BR)/build-gnmi/;cd $(BR)/build-gnmi/;cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr $(WS_ROOT)/src/gnmi/;make; make install;
+else
+ @mkdir -p $(BR)/build-gnmi/;cd $(BR)/build-gnmi/;cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr $(WS_ROOT)/src/gnmi/;make; make install;
+endif
+
build-package:
@mkdir -p $(BR)/build-scvpp/;cd $(BR)/build-scvpp;cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr $(WS_ROOT)/src/scvpp/;make install;
@mkdir -p $(BR)/build-package/;cd $(BR)/build-package/;$(cmake) $(WS_ROOT)/src/;make package;rm -rf $(BR)/build-package/_CPack_Packages;
@@ -215,8 +274,10 @@ clean:
@cd $(BR)/build-scvpp && make clean;
@cd $(BR)/build-plugins && make clean;
@cd $(BR)/build-package && make clean;
+ @cd $(BR)/build-gnmi && make clean;
distclean:
@rm -rf $(BR)/build-scvpp
@rm -rf $(BR)/build-plugins
@rm -rf $(BR)/build-package
+ @rm -rf $(BR)/build-gnmi
diff --git a/src/cmake/FindJSONCPP.cmake b/src/cmake/FindJSONCPP.cmake
new file mode 100644
index 0000000..a342f08
--- /dev/null
+++ b/src/cmake/FindJSONCPP.cmake
@@ -0,0 +1,56 @@
+# - Try to find LibJSONCPP
+# Once done this will define
+#
+# LIBJSONCPP_FOUND - system has LibJSONCPP
+# LIBJSONCPP_INCLUDE_DIRS - the LibJSONCPP include directory
+# LIBJSONCPP_LIBRARIES - Link these to use LIBJSONCPP
+
+
+if (LIBJSONCPP_LIBRARIES AND LIBJSONCPP_INCLUDE_DIRS)
+ # in cache already
+ set(LIBJSONCPP_FOUND TRUE)
+else (LIBJSONCPP_LIBRARIES AND LIBJSONCPP_INCLUDE_DIRS)
+
+ find_path(LIBJSONCPP_INCLUDE_DIR
+ NAMES
+ json/json.h
+ PATHS
+ /usr/include
+ /usr/local/include
+ /usr/include/jsoncpp
+ /opt/local/include
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_library(LIBJSONCPP_LIBRARY
+ NAMES
+ jsoncpp
+ PATHS
+ /usr/lib
+ /usr/lib64
+ /usr/lib/x86_64-linux-gnu
+ /usr/local/lib
+ /usr/local/lib64
+ /opt/local/lib
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ if (LIBJSONCPP_INCLUDE_DIR AND LIBJSONCPP_LIBRARY)
+ set(LIBJSONCPP_FOUND TRUE)
+ else (LIBJSONCPP_INCLUDE_DIR AND LIBJSONCPP_LIBRARY)
+ set(LIBJSONCPP_FOUND FALSE)
+ endif (LIBJSONCPP_INCLUDE_DIR AND LIBJSONCPP_LIBRARY)
+
+ set(LIBJSONCPP_INCLUDE_DIRS ${LIBJSONCPP_INCLUDE_DIR})
+ set(LIBJSONCPP_LIBRARIES ${LIBJSONCPP_LIBRARY})
+
+ # show the LIBJSONCPP_INCLUDE_DIRS and LIBJSONCPP_LIBRARIES variables only in the advanced view
+ mark_as_advanced(LIBJSONCPP_INCLUDE_DIRS LIBJSONCPP_LIBRARIES)
+
+endif (LIBJSONCPP_LIBRARIES AND LIBJSONCPP_INCLUDE_DIRS)
+
+
+
+
diff --git a/src/cmake/FindPUGIXML.cmake b/src/cmake/FindPUGIXML.cmake
new file mode 100644
index 0000000..dc7c86b
--- /dev/null
+++ b/src/cmake/FindPUGIXML.cmake
@@ -0,0 +1,52 @@
+# - Try to find LibPUGIXML
+# Once done this will define
+#
+# LIBPUGIXML_FOUND - system has LibPUGIXML
+# LIBPUGIXML_INCLUDE_DIRS - the LibPUGIXML include directory
+# LIBPUGIXML_LIBRARIES - Link these to use LIBPUGIXML
+
+if (LIBPUGIXML_LIBRARIES AND LIBPUGIXML_INCLUDE_DIRS)
+ # in cache already
+ set(LIBPUGIXML_FOUND TRUE)
+else (LIBPUGIXML_LIBRARIES AND LIBPUGIXML_INCLUDE_DIRS)
+
+ find_path(LIBPUGIXML_INCLUDE_DIR
+ NAMES
+ pugixml.hpp
+ PATHS
+ /usr/include
+ /usr/local/include
+ /opt/local/include
+ ${CMAKE_INCLUDE_PATH}
+ ${CMAKE_INSTALL_PREFIX}/include
+ )
+
+ find_library(LIBPUGIXML_LIBRARY
+ NAMES
+ pugixml
+ PATHS
+ /usr/lib
+ /usr/lib64
+ /usr/local/lib
+ /usr/local/lib64
+ /opt/local/lib
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib
+ )
+
+ if (LIBPUGIXML_INCLUDE_DIR AND LIBPUGIXML_LIBRARY)
+ set(LIBPUGIXML_FOUND TRUE)
+ else (LIBPUGIXML_INCLUDE_DIR AND LIBPUGIXML_LIBRARY)
+ set(LIBPUGIXML_FOUND FALSE)
+ endif (LIBPUGIXML_INCLUDE_DIR AND LIBPUGIXML_LIBRARY)
+
+ set(LIBPUGIXML_INCLUDE_DIRS ${LIBPUGIXML_INCLUDE_DIR})
+ set(LIBPUGIXML_LIBRARIES ${LIBPUGIXML_LIBRARY})
+
+ # show the LIBPUGIXML_INCLUDE_DIRS and LIBPUGIXML_LIBRARIES variables only in the advanced view
+ mark_as_advanced(LIBPUGIXML_INCLUDE_DIRS LIBPUGIXML_LIBRARIES)
+
+endif (LIBPUGIXML_LIBRARIES AND LIBPUGIXML_INCLUDE_DIRS)
+
+
+
diff --git a/src/gnmi/CMakeLists.txt b/src/gnmi/CMakeLists.txt
new file mode 100644
index 0000000..e373827
--- /dev/null
+++ b/src/gnmi/CMakeLists.txt
@@ -0,0 +1,73 @@
+#
+# Copyright (c) 2019 PANTHEON.tech.
+#
+# 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.
+#
+
+cmake_minimum_required(VERSION 2.8)
+project(gNMI-server)
+
+set(CMAKE_CXX_STANDARD 17)
+# set compiler option
+set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
+set(CMAKE_C_FLAGS "-Wall -std=c++17")
+set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG -O2")
+set(CMAKE_C_FLAGS_DEBUG "-g -O0")
+set(CMAKE_INSTALL_LIBDIR "/usr/lib")
+
+# Cmake find modules
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../cmake")
+
+find_package(PUGIXML REQUIRED)
+find_package(JSONCPP REQUIRED)
+find_package(PkgConfig)
+
+
+pkg_check_modules(SYSREPO REQUIRED libsysrepo)
+
+add_subdirectory(proto)
+
+if(LIBPUGIXML_FOUND)
+ message ("PUGIXML found")
+else()
+ message (FATAL_ERROR "Cannot find PUGIXML")
+endif()
+
+if(LIBJSONCPP_FOUND)
+ message ("LIBJSONCPP found")
+else()
+ message (FATAL_ERROR "Cannot find LIBJSONCPP")
+endif()
+
+set(GNMI_SRC main.cpp
+ log.cpp
+ gnmiserver.cpp
+ sysrepoapi.cpp
+ sysrepoapipool.cpp
+ xml2json.cpp
+ gnmidata.cpp)
+
+add_executable(gnmi_server ${GNMI_SRC})
+
+target_include_directories(gnmi_server PRIVATE ${PROTOBUF_INCLUDE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}/proto
+ ${LIBPUGIXML_INCLUDE_DIR}
+ ${LIBJSONCPP_INCLUDE_DIR}
+ ${SYSREPO_INCLUDE_DIRS})
+
+target_link_libraries(gnmi_server proto
+ ${LIBPUGIXML_LIBRARY}
+ ${LIBJSONCPP_LIBRARY}
+ ${SYSREPO_LIBRARIES})
+
+install(TARGETS gnmi_server RUNTIME DESTINATION bin)
diff --git a/src/gnmi/eventreciverbase.h b/src/gnmi/eventreciverbase.h
new file mode 100644
index 0000000..13f79ff
--- /dev/null
+++ b/src/gnmi/eventreciverbase.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#ifndef EVENTRECIVERBASE_H
+#define EVENTRECIVERBASE_H
+
+#include <exception>
+#include <iostream>
+
+class EventReceiverBase
+{
+public:
+ virtual ~EventReceiverBase() {}
+};
+
+template<typename T>
+class EventReceiver : public virtual EventReceiverBase
+{
+public:
+ virtual void receiveWriteEvent(T *psender) = 0;
+};
+
+class EventSender
+{
+public:
+
+ template<typename T>
+ void registerReciver(T *receiver) {
+ pReceiver = receiver;
+ }
+
+ void registerStream(void *stream) {
+ this->stream = stream;
+ }
+
+ void *getStream() {
+ return stream;
+ }
+
+ template<typename T>
+ void sendEvent(T* pSender) {
+ if (nullptr == pReceiver) {
+ throw std::runtime_error("Receiver is not register.");
+ }
+
+ EventReceiver<T> *pCastedReceiver =
+ dynamic_cast<EventReceiver<T>*>(pReceiver);
+ pCastedReceiver->receiveWriteEvent(pSender);
+ }
+
+private:
+ EventReceiverBase *pReceiver = nullptr;
+ void *stream = nullptr;
+};
+
+template<typename T>
+class BaseSender : public virtual EventSender
+{
+public:
+ void send() {
+ sendEvent((T*) this);
+ }
+};
+
+#endif // EVENTRECIVERBASE_H
diff --git a/src/gnmi/gnmidata.cpp b/src/gnmi/gnmidata.cpp
new file mode 100644
index 0000000..5ee53c4
--- /dev/null
+++ b/src/gnmi/gnmidata.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#include "gnmidata.h"
+
+gNMIData::ValueType gNMIData::dataType() const
+{
+ return dtype;
+}
+
+void gNMIData::clean()
+{
+ dtype = ValueType::UnknownVal;
+ value = {};
+ xpath = "";
+}
+
+void gNMIData::setXPath(const std::string& str, xPathType type)
+{
+ std::size_t pos = 0;
+
+ xpath = str;
+
+ if (xPathType::gNMIPath == type) {
+ return;
+ }
+
+ while (std::string::npos != (pos = xpath.find(":", pos))) {
+ xpath.replace(pos, std::string(":").length(), "/");
+ }
+}
+
+void gNMIData::setValue(const std::string& str)
+{
+ dtype = ValueType::dStringVal;
+ value = str;
+}
+
+void gNMIData::setValue(int val)
+{
+ dtype = ValueType::dIntVal;
+ value = val;
+}
+
+std::string gNMIData::getXPath(gNMIData::xPathType type) const
+{
+ switch (type) {
+ case xPathType::gNMIPath:
+ return xpath;
+
+ case xPathType::sysrepoPath:
+ return convertToSyrepoPath();
+
+ default:
+ break;
+ }
+
+ return xpath;
+}
+
+std::string gNMIData::getStr() const
+{
+ switch (dtype) {
+ case ValueType::dIntVal:
+ return std::to_string(getInt());
+
+ case ValueType::dStringVal:
+ return std::get<std::string>(value);
+
+ case ValueType::UnknownVal:
+ default:
+ //TODO: I`m not sure with N/Al
+ return "N/A";
+ }
+
+ return "N/A";
+}
+
+int gNMIData::getInt() const
+{
+ return std::get<int>(value);
+}
+
+std::string gNMIData::convertToSyrepoPath() const
+{
+ std::string str = xpath;
+ std::size_t pos = 0;
+ int i = 0;
+
+ while (std::string::npos != (pos = str.find("/", pos))) {
+ if (1 == i++) {
+ str.replace(pos, std::string("/").length(), ":");
+ break;
+ }
+ pos++;
+ }
+
+ return str;
+}
diff --git a/src/gnmi/gnmidata.h b/src/gnmi/gnmidata.h
new file mode 100644
index 0000000..e4c15d2
--- /dev/null
+++ b/src/gnmi/gnmidata.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#ifndef GNMIDATA_H
+#define GNMIDATA_H
+
+#include <string>
+#include <variant>
+
+/**
+ * @todo write docs
+ */
+class gNMIData
+{
+public:
+ enum class ValueType {
+ dStringVal,
+ dIntVal,
+ UnknownVal
+ };
+
+ enum class xPathType {
+ gNMIPath,
+ sysrepoPath,
+ };
+
+public:
+ /**
+ * Default constructor
+ */
+ gNMIData() = default;
+
+ /**
+ * Destructor
+ */
+ ~gNMIData() = default;
+
+ ValueType dataType() const;
+ void clean();
+
+ void setXPath(const std::string &str, xPathType type = xPathType::gNMIPath);
+ void setValue(const std::string &str);
+ void setValue(int val);
+
+ std::string getXPath(xPathType type = xPathType::gNMIPath) const;
+ std::string getStr() const;
+ int getInt() const;
+
+private:
+ std::string convertToSyrepoPath() const;
+
+private:
+ ValueType dtype = ValueType::UnknownVal;
+ std::variant<int, std::string> value;
+ std::string xpath;
+};
+
+#endif // GNMIDATA_H
diff --git a/src/gnmi/gnmiserver.cpp b/src/gnmi/gnmiserver.cpp
new file mode 100644
index 0000000..44ce6c5
--- /dev/null
+++ b/src/gnmi/gnmiserver.cpp
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#include "gnmiserver.h"
+#include "log.h"
+#include "xml2json.h"
+
+#include <iostream>
+#include <exception>
+#include <chrono>
+#include <ctime>
+#include <ratio>
+#include <list>
+
+#include <sysrepo.h>
+#include <sysrepo/xpath.h>
+
+gNMIServer::gNMIServer(SysrepoAPI& sysrepo)
+ : sysrepo(sysrepo)
+{
+}
+
+grpc::Status gNMIServer::Capabilities(grpc::ServerContext* context,
+ const gnmi::CapabilityRequest* request,
+ gnmi::CapabilityResponse* reply)
+{
+ DEBUG("Capabilities message");
+
+ if (0 < request->extension_size()) {
+ //TODO;
+ }
+
+ sysrepo.createSession();
+ const auto &ss = sysrepo.getSchemas();
+
+ for (const auto schema : ss) {
+ auto model = reply->add_supported_models();
+ model->set_name(schema.moduleName);
+ model->set_version(schema.revision);
+ }
+
+ reply->add_supported_encodings(gnmi::Encoding::ASCII);
+
+ //FIXME: I`m not sure, if this is a correct version of gnmi.
+ reply->set_gnmi_version(std::to_string(gnmi::kGnmiServiceFieldNumber));
+
+ sysrepo.closeSession();
+
+ return Status::OK;
+}
+
+grpc::Status gNMIServer::Get(grpc::ServerContext* context,
+ const gnmi::GetRequest* request,
+ gnmi::GetResponse* reply)
+{
+ DEBUG("Get message");
+
+ std::cout << gnmi::GetRequest_DataType_Name(request->type()) << std::endl;
+
+ try {
+ sysrepo.createSession();
+
+ auto &prefix = request->prefix();
+ dataPrefix = convertToXPath(prefix);
+ DEBUG("Prefix: %s", dataPrefix.c_str());
+
+ DEBUG("Data type: %s",
+ request->DataType_Name(request->type()).c_str());
+ DEBUG("Encoding: %s", gnmi::Encoding_Name(request->encoding()).c_str());
+
+ for (const auto &model : request->use_models()) {
+ DEBUG("Model, name: %s, organization: %s, version: %s",
+ model.name().c_str(), model.organization().c_str(),
+ model.version().c_str());
+ }
+
+// for (const auto &extension : request->extension()) {
+// DEBUG(ex);
+// }
+
+ if (0 < request->path().size()) {
+ for (const auto &path : request->path()) {
+ std::string strPath = convertToXPath(path);
+ vData.clean();
+ vData.setXPath(dataPrefix + strPath);
+ sysrepo.addData(vData);
+ sysrepo.getItemMessage();
+
+ auto notification = reply->add_notification();
+
+ notification->set_timestamp(getTimeNanosec());
+ auto elem = notification->mutable_prefix();
+ xpathTogNMIEl(dataPrefix, *elem);
+
+ const auto &oData = sysrepo.getOutputData();
+
+ for (const auto &data : oData) {
+ auto nupdate = notification->add_update();
+ xpathTogNMIEl(data.getXPath(), *nupdate->mutable_path());
+ auto uval = nupdate->mutable_val();
+ uval->set_string_val(data.getStr());
+ }
+ }
+ }
+
+ sysrepo.cleanData();
+ sysrepo.closeSession();
+ } catch (...) {
+ sysrepo.cleanData();
+ sysrepo.closeSession();
+ return Status::CANCELLED;
+ }
+
+ return Status::OK;
+}
+
+//TODO: Need handle INVALID operation, somehow, but how????
+grpc::Status gNMIServer::Set(grpc::ServerContext* context,
+ const gnmi::SetRequest* request,
+ gnmi::SetResponse* reply)
+{
+ DEBUG("Set message");
+ std::string path;
+
+ reply->set_timestamp(getTimeNanosec());
+
+ auto prefix = reply->mutable_prefix();
+ prefix->add_elem();
+
+ try {
+ sysrepo.createSession(SR_DS_RUNNING);
+
+ dataPrefix = convertToXPath(request->prefix());
+
+ if (0 < request->delete__size()) {
+ for (const auto &del : request->delete_()) {
+ vData.clean();
+ path = convertToXPath(del);
+ vData.setXPath(dataPrefix + path);
+ DEBUG("Delete: %s", vData.getXPath().c_str());
+ sysrepo.addData(vData);
+ }
+ sysrepo.setItemMessage();
+
+ const auto &oData = sysrepo.getOutputData();
+ for (const auto &data : oData) {
+ auto updateResult = reply->add_response();
+ updateResult->set_op(gnmi::UpdateResult::DELETE);
+ xpathTogNMIEl(data.getXPath(), *updateResult->mutable_path());
+ }
+
+ sysrepo.cleanData();
+ }
+
+ if (0 < request->replace_size()) {
+ for (const auto &replace : request->replace()) {
+ vData.clean();
+ handleUpdateMessage(replace);
+ DEBUG("Replace : %s", vData.getXPath().c_str());
+ sysrepo.addData(vData);
+ }
+ sysrepo.setItemMessage();
+
+ const auto &oData = sysrepo.getOutputData();
+ for (const auto &data : oData) {
+ auto updateResult = reply->add_response();
+ updateResult->set_op(gnmi::UpdateResult::REPLACE);
+ xpathTogNMIEl(data.getXPath(), *updateResult->mutable_path());
+ }
+
+ sysrepo.cleanData();
+ }
+
+ if (0 < request->update_size()) {
+ for (const auto &update : request->update()) {
+ vData.clean();
+ handleUpdateMessage(update);
+ DEBUG("Update: %s", vData.getXPath().c_str());
+// sysrepo.addData(vData);
+ XML2JSON json;
+ json.setData(vData.getStr());
+ json.setPrefix(vData.getXPath());
+ auto &gdatas = json.getgNMIData();
+ for (const auto &gdata : gdatas) {
+ DEBUG("Data xpath: %s, data: %s", gdata.getXPath().c_str(),
+ gdata.getStr().c_str());
+ sysrepo.addData(gdata);
+ }
+// DEBUG("%s", json.getXML().c_str());
+ }
+ sysrepo.setItemMessage();
+
+ const auto &oData = sysrepo.getOutputData();
+ for (const auto &data : oData) {
+ auto updateResult = reply->add_response();
+ updateResult->set_op(gnmi::UpdateResult::UPDATE);
+ xpathTogNMIEl(data.getXPath(), *updateResult->mutable_path());
+ }
+
+ sysrepo.cleanData();
+ }
+
+ //TODO: Need some special handling for sysrepo
+ sysrepo.commit();
+ sysrepo.closeSession();
+ } catch (...) {
+ sysrepo.cleanData();
+ sysrepo.closeSession();
+ return Status::CANCELLED;
+ }
+
+ return Status::OK;
+}
+
+grpc::Status gNMIServer::Subscribe(grpc::ServerContext* context,
+ ServerReaderWriter<gnmi::SubscribeResponse,
+ gnmi::SubscribeRequest>* stream)
+{
+ DEBUG("Subscribe message");
+
+ gnmi::SubscribeRequest request;
+
+ try {
+ sysrepo.registerReciver<gNMIServer>(this);
+ sysrepo.registerStream(stream);
+
+ sysrepo.createSession(SR_DS_RUNNING);
+ while (stream->Read(&request)) {
+ switch (request.request_case()) {
+ case gnmi::SubscribeRequest::kAliases:
+ break;
+
+ case gnmi::SubscribeRequest::kPoll:
+ break;
+
+ case gnmi::SubscribeRequest::kSubscribe:
+ subscibeList(request.subscribe());
+ break;
+
+ case gnmi::SubscribeRequest::REQUEST_NOT_SET:
+ break;
+
+ default:
+ DEBUG("Unknown request case.");
+ break;
+ }
+ }
+ } catch (...) {
+ sysrepo.cleanData();
+ sysrepo.closeSession();
+ return Status::CANCELLED;
+ }
+
+ sysrepo.cleanData();
+ sysrepo.closeSession();
+ DEBUG("Subscribe message end");
+
+ return Status::OK;
+}
+
+void gNMIServer::subscibeList(const gnmi::SubscriptionList& slist)
+{
+ std::string prefix = convertToXPath(slist.prefix());
+
+ if (0 < slist.subscription_size()) {
+ for (const auto &sub : slist.subscription()) {
+ vData.clean();
+ vData.setXPath(prefix + convertToXPath(sub.path()));
+ //TODO: Call sysrepo event
+
+ DEBUG("Subs Mode: %s",
+ gnmi::SubscriptionMode_Name(sub.mode()).c_str());
+ DEBUG("Subs xpath: %s", vData.getXPath().c_str());
+ sysrepo.addData(vData);
+ sysrepo.eventSubscribeMessage();
+ }
+ }
+
+ DEBUG("Subscribe list mode: %s", slist.Mode_Name(slist.mode()).c_str());
+
+ if (0 < slist.use_models_size()) {
+ for (const auto &umod : slist.use_models()) {
+ DEBUG("Name: %s, Organization: %s, Version: %s",
+ umod.name().c_str(), umod.organization().c_str(),
+ umod.version().c_str());
+ }
+ }
+
+ DEBUG("Encoding: %s", gnmi::Encoding_Name(slist.encoding()).c_str());
+
+ DEBUG("Update only: %d", slist.updates_only());
+}
+
+void gNMIServer::receiveWriteEvent(SysrepoAPI* psender)
+{
+ gnmi::SubscribeResponse response;
+ ServerReaderWriter<gnmi::SubscribeResponse, gnmi::SubscribeRequest>* stream;
+
+ stream = (ServerReaderWriter<gnmi::SubscribeResponse,
+ gnmi::SubscribeRequest>*) psender->getStream();
+
+ auto sData = psender->getOutputData();
+ auto update = response.mutable_update();
+
+ update->set_timestamp(getTimeNanosec());
+ auto elem = update->mutable_prefix();
+
+ xpathTogNMIEl("/", *elem);
+ for (const auto &data : sData) {
+ DEBUG("XPath: %s, value: %s",
+ data.getXPath().c_str(), data.getStr().c_str());
+ auto nupdate = update->add_update();
+ xpathTogNMIEl(data.getXPath(), *nupdate->mutable_path());
+ auto val = nupdate->mutable_val();
+ val->set_string_val(data.getStr());
+ }
+
+ psender->cleanData();
+ stream->Write(response);
+}
+
+void gNMIServer::parsePathMsg(const gnmi::Path& path)
+{
+ if (0 >= path.elem_size()) {
+ DEBUG("Path is empty");
+ return;
+ }
+
+}
+
+std::string gNMIServer::convertToXPath(const gnmi::Path& path)
+{
+ std::string str = "/";
+
+ if (0 < path.elem_size()) {
+ for (const auto &elm : path.elem()) {
+ str += elm.name();
+
+ for (const auto &el : elm.key()) {
+ str += "[" + el.first + "='" + el.second + "']";
+ }
+
+ str += "/";
+ }
+ }
+ str.pop_back();
+
+ return str;
+}
+
+void gNMIServer::printPath(const gnmi::Path& path)
+{
+ if (0 < path.element().size()) {
+ for (const auto &element : path.element()) {
+ DEBUG("Element: %s", element.c_str());
+ }
+ }
+
+ if (0 < path.elem_size()) {
+ for (const auto &elm : path.elem()) {
+ DEBUG("Elm name: %ss", elm.name().c_str());
+ for (const auto &el : elm.key()) {
+ DEBUG("El key: %s, val: %s", el.first.c_str(),
+ el.second.c_str());
+ }
+ }
+ }
+}
+
+void gNMIServer::handleUpdateMessage(const gnmi::Update& msg)
+{
+ vData.setXPath(dataPrefix + convertToXPath(msg.path()));
+
+ if (msg.has_val()) {
+ handleTypeValueMsg(msg.val());
+ }
+}
+
+void gNMIServer::handleTypeValueMsg(const gnmi::TypedValue& msg)
+{
+ DEBUG("Val case: %d", msg.value_case());
+
+ switch (msg.value_case()) {
+ case gnmi::TypedValue::ValueCase::kStringVal:
+ vData.setValue(msg.string_val());
+ break;
+
+ case gnmi::TypedValue::ValueCase::kIntVal:
+ break;
+
+ case gnmi::TypedValue::ValueCase::kUintVal:
+ break;
+
+ case gnmi::TypedValue::ValueCase::kBoolVal:
+ break;
+
+ case gnmi::TypedValue::ValueCase::kBytesVal:
+ break;
+
+ case gnmi::TypedValue::ValueCase::kFloatVal:
+ break;
+
+ case gnmi::TypedValue::ValueCase::kDecimalVal:
+ break;
+
+ case gnmi::TypedValue::ValueCase::kLeaflistVal:
+ break;
+
+ case gnmi::TypedValue::ValueCase::kAnyVal:
+ break;
+
+ case gnmi::TypedValue::ValueCase::kJsonVal:
+ break;
+
+ case gnmi::TypedValue::ValueCase::kJsonIetfVal:
+ vData.setValue(msg.json_ietf_val());
+ break;
+
+ case gnmi::TypedValue::ValueCase::kAsciiVal:
+ break;
+
+ case gnmi::TypedValue::ValueCase::kProtoBytes:
+ break;
+
+ case gnmi::TypedValue::ValueCase::VALUE_NOT_SET:
+ break;
+
+ default:
+ DEBUG("Unknown type.");
+ break;
+ }
+}
+
+void gNMIServer::xpathTogNMIEl(const std::string& str, gnmi::Path& path)
+{
+ sr_xpath_ctx_t state;
+ std::string tmp(str);
+ const char *xpath = tmp.c_str();
+ char *name;
+
+ if ((name = sr_xpath_next_node((char *)xpath, &state)) == NULL) {
+ DEBUG("Empty XPATH, xpath: %s", xpath);
+ return;
+ }
+
+ do {
+ auto pathEl = path.add_elem();
+ pathEl->set_name(name);
+
+ const char *key_name;
+ while ((key_name = sr_xpath_next_key_name(NULL, &state)) != NULL) {
+ std::string key(key_name);
+ const char *key_value = sr_xpath_next_key_value(NULL, &state);
+ (*pathEl->mutable_key())[key] = key_value;
+ }
+ } while ((name = sr_xpath_next_node(NULL, &state)) != NULL);
+
+ sr_xpath_recover(&state);
+}
+
+
+uint64_t gNMIServer::getTimeNanosec()
+{
+ using namespace std::chrono;
+
+ std::uint64_t tm = high_resolution_clock::now().time_since_epoch() /
+ nanoseconds(1);
+
+ return tm;
+}
diff --git a/src/gnmi/gnmiserver.h b/src/gnmi/gnmiserver.h
new file mode 100644
index 0000000..7dc1339
--- /dev/null
+++ b/src/gnmi/gnmiserver.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#ifndef GNMISERVER_H
+#define GNMISERVER_H
+
+#include <grpcpp/grpcpp.h>
+
+#include "gnmi.pb.h"
+#include "gnmi.grpc.pb.h"
+#include "sysrepoapi.h"
+#include "gnmidata.h"
+#include "eventreciverbase.h"
+
+#include <string>
+
+using grpc::Server;
+using grpc::ServerBuilder;
+using grpc::ServerContext;
+using grpc::ServerReaderWriter;
+using grpc::Status;
+
+using gnmi::gNMI;
+using gnmi::CapabilityRequest;
+using gnmi::CapabilityResponse;
+using gnmi::GetRequest;
+using gnmi::GetResponse;
+using gnmi::SetRequest;
+using gnmi::SetResponse;
+using gnmi::SubscribeRequest;
+using gnmi::SubscribeResponse;
+
+/**
+ * @todo write docs
+ */
+class gNMIServer final : public gNMI::Service,
+ public virtual EventReceiver<SysrepoAPI>
+{
+public:
+ gNMIServer(SysrepoAPI &sysrepo);
+
+ Status Capabilities(ServerContext* context,
+ const CapabilityRequest* request,
+ CapabilityResponse* reply) override;
+ Status Get(ServerContext* context, const GetRequest* request,
+ GetResponse* reply) override;
+ Status Set(ServerContext* context, const SetRequest* request,
+ SetResponse* reply) override;
+ Status Subscribe(ServerContext* context,
+ ServerReaderWriter<SubscribeResponse,
+ SubscribeRequest>* stream) override;
+
+ void receiveWriteEvent(SysrepoAPI * psender) override;
+
+private:
+ void parsePathMsg(const gnmi::Path &path);
+ void printPath(const gnmi::Path &path);
+ std::string convertToXPath(const gnmi::Path &path);
+
+ void subscibeList(const gnmi::SubscriptionList &slist);
+
+ void handleUpdateMessage(const gnmi::Update &msg);
+ void handleTypeValueMsg(const gnmi::TypedValue &msg);
+
+ void xpathTogNMIEl(const std::string &str, gnmi::Path &path);
+
+ uint64_t getTimeNanosec();
+
+private:
+ SysrepoAPI &sysrepo;
+ gNMIData vData;
+ std::string dataPrefix;
+};
+
+#endif // GNMISERVER_H
diff --git a/src/gnmi/log.cpp b/src/gnmi/log.cpp
new file mode 100644
index 0000000..86d26b5
--- /dev/null
+++ b/src/gnmi/log.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#include "log.h"
+
+void init_log(int option)
+{
+ openlog(NULL, option, LOG_DAEMON);
+}
+
+void close_log()
+{
+ closelog();
+}
+
diff --git a/src/gnmi/log.h b/src/gnmi/log.h
new file mode 100644
index 0000000..0bb01f0
--- /dev/null
+++ b/src/gnmi/log.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#include <stdio.h>
+#include <syslog.h>
+
+#define ERROR(fmt, ...) \
+ do { syslog(LOG_ERR, "%s:%d:%s():[ERROR]:" fmt "\n", __FILE__, \
+ __LINE__, __func__, ##__VA_ARGS__); } while (0)
+
+#define DEBUG(fmt, ...) \
+ do { syslog(LOG_DEBUG, "%s:%d:%s():[DEBUG]:" fmt "\n", __FILE__, \
+ __LINE__, __func__, ##__VA_ARGS__); } while (0)
+
+void init_log(int option);
+
+void close_log();
+
+#endif
diff --git a/src/gnmi/main.cpp b/src/gnmi/main.cpp
new file mode 100644
index 0000000..8115732
--- /dev/null
+++ b/src/gnmi/main.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#include "log.h"
+#include "gnmiserver.h"
+#include "sysrepoapipool.h"
+
+#include <argp.h>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <string>
+#include <map>
+#include <arpa/inet.h>
+
+const char *argp_program_version = "0.0.1";
+// const char *argp_program_bug_address = "<your@email.address>";
+static char doc[] = "gNMI server implementation/integration for FD.io vpp ";
+static char args_doc[] = "";
+static struct argp_option options[] = {
+ {"address", 'a', "x.x.x.x", 0, "Destination IPv4 address"},
+ {"port", 'p', "port", 0, "Destination port"},
+ {"sysrepo", 's', "name", 0, "Sysrepo name"},
+ {"ssl/tls", 't', NULL, 0, "Enable ssl/tls"},
+ {"serverKey", 'k', "file", 0, "Server Key"},
+ {"serverCerts", 'c', "file", 0, "Server Certs"},
+ {"rootCert", 'r', "file", 0, "Root Cert"},
+ { 0 }
+};
+
+struct arguments {
+ std::string ip_address;
+ std::string port;
+ std::string sysrepo_name;
+ bool enable_ssltls;
+ std::string server_key;
+ std::string server_certs;
+ std::string root_cert;
+
+ arguments() : ip_address{}, port{}, sysrepo_name{}, enable_ssltls{false},
+ server_key{}, server_certs{}, root_cert{} {}
+};
+
+static error_t check_ip(const std::string &str)
+{
+ struct sockaddr_in sa;
+ int rc = inet_pton(AF_INET, str.c_str(), &(sa.sin_addr));
+ if (1 != rc) {
+ ERROR("Wrong IPv4 address format.");
+ return ARGP_KEY_ERROR;
+ }
+
+ return ARGP_KEY_ARG;
+}
+
+static error_t parse_opt(int key, char *arg, struct argp_state *state)
+{
+ error_t rc = 0;
+ struct arguments *arguments = (struct arguments*) state->input;
+
+ switch (key) {
+ case 'a':
+ arguments->ip_address = std::string(arg);
+ rc = check_ip(arguments->ip_address);
+ break;
+
+ case 'p':
+ arguments->port = std::string(arg);
+ break;
+
+ case 't':
+ arguments->enable_ssltls = true;
+ break;
+
+ case 's':
+ arguments->sysrepo_name = std::string(arg);
+ break;
+
+ case 'k':
+ arguments->server_key = std::string(arg);
+ break;
+
+ case 'c':
+ arguments->server_certs = std::string(arg);
+ break;
+
+ case 'r':
+ arguments->root_cert = std::string(arg);
+ break;
+
+ case ARGP_KEY_ARG:
+ return rc;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return rc;
+}
+
+static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 };
+SysrepoApiPool sysrepoPoll;
+
+static std::string read_cert(const std::string &file)
+{
+ std::ifstream fo(file);
+ std::stringstream str;
+
+ str << fo.rdbuf();
+
+ return str.str();
+}
+
+void RunServer(const struct arguments &arg)
+{
+ std::string server_address = std::string(arg.ip_address) +
+ std::string(":") + std::string(arg.port);
+ gNMIServer service(sysrepoPoll.get(arg.sysrepo_name));
+ ServerBuilder builder;
+
+ try {
+ if (arg.enable_ssltls) {
+ grpc::SslServerCredentialsOptions::PemKeyCertPair pkcp;
+
+ pkcp.private_key = read_cert(arg.server_key);
+ pkcp.cert_chain = read_cert(arg.server_certs);
+
+ grpc::SslServerCredentialsOptions ssl_opts;
+ ssl_opts.pem_root_certs = "";
+ ssl_opts.pem_key_cert_pairs.push_back(pkcp);
+ ssl_opts.pem_root_certs = read_cert(arg.root_cert);
+
+ auto creds = grpc::SslServerCredentials(ssl_opts);
+
+ builder.AddListeningPort(server_address, creds);
+ } else {
+
+ // Listen on the given address without any authentication mechanism.
+ builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
+ }
+ // Register "service" as the instance through which we'll communicate with
+ // clients. In this case it corresponds to an *synchronous* service.
+ builder.RegisterService(&service);
+ // Finally assemble the server.
+ // builder.
+ std::unique_ptr<Server> server(builder.BuildAndStart());
+
+ if (server != nullptr)
+ {
+ std::cout << "Server listening on " << server_address << std::endl;
+ // Wait for the server to shutdown. Note that some other thread must be
+ // responsible for shutting down the server for this call to ever return.
+ server->Wait();
+ }
+ else
+ {
+ throw std::logic_error("Failed to create Server.");
+ }
+ } catch(...) {
+ ERROR("Failed to create Server.");
+ exit(-1);
+ }
+
+}
+
+int main(int argc, char **argv)
+{
+ struct arguments arguments;
+
+ init_log(LOG_PERROR);
+
+ error_t rc = argp_parse(&argp, argc, argv, 0, 0, &arguments);
+ if (ARGP_KEY_ARG != rc) {
+ argp_help(&argp, stdout, ARGP_HELP_SEE, argv[0]);
+ exit(-1);
+ }
+
+ if (arguments.ip_address.empty() || arguments.port.empty()) {
+ ERROR("IP address and port must be set.");
+ argp_help(&argp, stdout, ARGP_HELP_SEE, argv[0]);
+ exit(-1);
+ }
+
+ if (arguments.enable_ssltls) {
+ if (arguments.server_key.empty()) {
+ ERROR("Server key must be set.");
+ argp_help(&argp, stdout, ARGP_HELP_SEE, argv[0]);
+ exit(-1);
+ }
+
+ if (arguments.server_certs.empty()) {
+ ERROR("Server certificate must be set.");
+ argp_help(&argp, stdout, ARGP_HELP_SEE, argv[0]);
+ exit(-1);
+ }
+ }
+
+ auto &sysrepo = sysrepoPoll.get(arguments.sysrepo_name);
+ sysrepo.connect();
+
+ std::string destination = std::string(arguments.ip_address) +
+ std::string(":") + std::string(arguments.port);
+
+ RunServer(arguments);
+
+ close_log();
+
+ return 0;
+}
diff --git a/src/gnmi/proto/CMakeLists.txt b/src/gnmi/proto/CMakeLists.txt
new file mode 100644
index 0000000..8e70f77
--- /dev/null
+++ b/src/gnmi/proto/CMakeLists.txt
@@ -0,0 +1,52 @@
+set(protobuf_MODULE_COMPATIBLE TRUE)
+
+find_package(Protobuf REQUIRED)
+include_directories(${PROTOBUF_INCLUDE_DIRS})
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+message(STATUS "Using protobuf ${protobuf_VERSION}")
+
+set(_PROTOBUF_LIBPROTOBUF ${PROTOBUF_LIBRARIES})
+set(_PROTOBUF_PROTOC ${PROTOBUF_PROTOC_EXECUTABLE})
+
+find_package(gRPC CONFIG REQUIRED)
+set(_GRPC_GRPCPP gRPC::grpc++)
+set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:gRPC::grpc_cpp_plugin>)
+
+
+set(PROTOS
+ gnmi.proto
+ gnmi_ext.proto
+)
+
+get_filename_component(hw_proto "gnmi.proto" ABSOLUTE)
+get_filename_component(hw_proto_path "${hw_proto}" PATH)
+get_filename_component(gnmi_ext_proto "gnmi_ext.proto" ABSOLUTE)
+get_filename_component(gnmi_ext_proto_path "${gnmi_ext_proto}" PATH)
+
+# Generated sources
+set(hw_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/gnmi.pb.cc")
+set(hw_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/gnmi.pb.h")
+set(hw_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/gnmi.grpc.pb.cc")
+set(hw_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/gnmi.grpc.pb.h")
+set(gnmi_ext_proto_srcs "${CMAKE_CURRENT_BINARY_DIR}/gnmi_ext.pb.cc")
+set(gnmi_ext_proto_hdrs "${CMAKE_CURRENT_BINARY_DIR}/gnmi_ext.pb.h")
+set(gnmi_ext_grpc_srcs "${CMAKE_CURRENT_BINARY_DIR}/gnmi_ext.grpc.pb.cc")
+set(gnmi_ext_grpc_hdrs "${CMAKE_CURRENT_BINARY_DIR}/gnmi_ext.grpc.pb.h")
+
+add_custom_command(
+ OUTPUT "${hw_proto_srcs}" "${hw_proto_hdrs}" "${hw_grpc_srcs}" "${hw_grpc_hdrs}" "${gnmi_ext_proto_srcs}" "${gnmi_ext_proto_hdrs}" "${gnmi_ext_grpc_srcs}" "${gnmi_ext_grpc_hdrs}"
+ COMMAND ${_PROTOBUF_PROTOC}
+ ARGS --grpc_out "${CMAKE_CURRENT_BINARY_DIR}"
+ --cpp_out "${CMAKE_CURRENT_BINARY_DIR}"
+ -I "${hw_proto_path}" "${gnmi_ext_path}"
+ --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}"
+ "${hw_proto}" "${gnmi_ext_proto}"
+ DEPENDS "${hw_proto}" "${gnmi_ext_proto}")
+
+# Include generated *.pb.h files
+include_directories("${CMAKE_CURRENT_BINARY_DIR}")
+
+add_library(proto ${hw_proto_srcs} ${hw_grpc_srcs}
+ ${gnmi_ext_proto_srcs} ${gnmi_ext_grpc_srcs})
+target_link_libraries(proto ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF})
+
diff --git a/src/gnmi/proto/gnmi.proto b/src/gnmi/proto/gnmi.proto
new file mode 100644
index 0000000..99acce6
--- /dev/null
+++ b/src/gnmi/proto/gnmi.proto
@@ -0,0 +1,457 @@
+//
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+syntax = "proto3";
+
+import "google/protobuf/any.proto";
+import "google/protobuf/descriptor.proto";
+import "gnmi_ext.proto";
+
+// Package gNMI defines a service specification for the gRPC Network Management
+// Interface. This interface is defined to be a standard interface via which
+// a network management system ("client") can subscribe to state values,
+// retrieve snapshots of state information, and manipulate the state of a data
+// tree supported by a device ("target").
+//
+// This document references the gNMI Specification which can be found at
+// http://github.com/openconfig/reference/blob/master/rpc/gnmi
+package gnmi;
+
+// Define a protobuf FileOption that defines the gNMI service version.
+extend google.protobuf.FileOptions {
+ // The gNMI service semantic version.
+ string gnmi_service = 1001;
+}
+
+// gNMI_service is the current version of the gNMI service, returned through
+// the Capabilities RPC.
+option (gnmi_service) = "0.7.0";
+
+service gNMI {
+ // Capabilities allows the client to retrieve the set of capabilities that
+ // is supported by the target. This allows the target to validate the
+ // service version that is implemented and retrieve the set of models that
+ // the target supports. The models can then be specified in subsequent RPCs
+ // to restrict the set of data that is utilized.
+ // Reference: gNMI Specification Section 3.2
+ rpc Capabilities(CapabilityRequest) returns (CapabilityResponse);
+ // Retrieve a snapshot of data from the target. A Get RPC requests that the
+ // target snapshots a subset of the data tree as specified by the paths
+ // included in the message and serializes this to be returned to the
+ // client using the specified encoding.
+ // Reference: gNMI Specification Section 3.3
+ rpc Get(GetRequest) returns (GetResponse);
+ // Set allows the client to modify the state of data on the target. The
+ // paths to modified along with the new values that the client wishes
+ // to set the value to.
+ // Reference: gNMI Specification Section 3.4
+ rpc Set(SetRequest) returns (SetResponse);
+ // Subscribe allows a client to request the target to send it values
+ // of particular paths within the data tree. These values may be streamed
+ // at a particular cadence (STREAM), sent one off on a long-lived channel
+ // (POLL), or sent as a one-off retrieval (ONCE).
+ // Reference: gNMI Specification Section 3.5
+ rpc Subscribe(stream SubscribeRequest) returns (stream SubscribeResponse);
+}
+
+// Notification is a re-usable message that is used to encode data from the
+// target to the client. A Notification carries two types of changes to the data
+// tree:
+// - Deleted values (delete) - a set of paths that have been removed from the
+// data tree.
+// - Updated values (update) - a set of path-value pairs indicating the path
+// whose value has changed in the data tree.
+// Reference: gNMI Specification Section 2.1
+message Notification {
+ int64 timestamp = 1; // Timestamp in nanoseconds since Epoch.
+ Path prefix = 2; // Prefix used for paths in the message.
+ // An alias for the path specified in the prefix field.
+ // Reference: gNMI Specification Section 2.4.2
+ string alias = 3;
+ repeated Update update = 4; // Data elements that have changed values.
+ repeated Path delete = 5; // Data elements that have been deleted.
+ // This notification contains a set of paths that are always updated together
+ // referenced by a globally unique prefix.
+ bool atomic = 6;
+}
+
+// Update is a re-usable message that is used to store a particular Path,
+// Value pair.
+// Reference: gNMI Specification Section 2.1
+message Update {
+ Path path = 1; // The path (key) for the update.
+ Value value = 2 [deprecated=true]; // The value (value) for the update.
+ TypedValue val = 3; // The explicitly typed update value.
+ uint32 duplicates = 4; // Number of coalesced duplicates.
+}
+
+// TypedValue is used to encode a value being sent between the client and
+// target (originated by either entity).
+message TypedValue {
+ // One of the fields within the val oneof is populated with the value
+ // of the update. The type of the value being included in the Update
+ // determines which field should be populated. In the case that the
+ // encoding is a particular form of the base protobuf type, a specific
+ // field is used to store the value (e.g., json_val).
+ oneof value {
+ string string_val = 1; // String value.
+ int64 int_val = 2; // Integer value.
+ uint64 uint_val = 3; // Unsigned integer value.
+ bool bool_val = 4; // Bool value.
+ bytes bytes_val = 5; // Arbitrary byte sequence value.
+ float float_val = 6; // Floating point value.
+ Decimal64 decimal_val = 7; // Decimal64 encoded value.
+ ScalarArray leaflist_val = 8; // Mixed type scalar array value.
+ google.protobuf.Any any_val = 9; // protobuf.Any encoded bytes.
+ bytes json_val = 10; // JSON-encoded text.
+ bytes json_ietf_val = 11; // JSON-encoded text per RFC7951.
+ string ascii_val = 12; // Arbitrary ASCII text.
+ // Protobuf binary encoded bytes. The message type is not included.
+ // See the specification at
+ // github.com/openconfig/reference/blob/master/rpc/gnmi/protobuf-vals.md
+ // for a complete specification.
+ bytes proto_bytes = 13;
+ }
+}
+
+// Path encodes a data tree path as a series of repeated strings, with
+// each element of the path representing a data tree node name and the
+// associated attributes.
+// Reference: gNMI Specification Section 2.2.2.
+message Path {
+ // Elements of the path are no longer encoded as a string, but rather within
+ // the elem field as a PathElem message.
+ repeated string element = 1 [deprecated=true];
+ string origin = 2; // Label to disambiguate path.
+ repeated PathElem elem = 3; // Elements of the path.
+ string target = 4; // The name of the target
+ // (Sec. 2.2.2.1)
+}
+
+// PathElem encodes an element of a gNMI path, along ith any attributes (keys)
+// that may be associated with it.
+// Reference: gNMI Specification Section 2.2.2.
+message PathElem {
+ string name = 1; // The name of the element in the path.
+ map<string, string> key = 2; // Map of key (attribute) name to value.
+}
+
+// Value encodes a data tree node's value - along with the way in which
+// the value is encoded. This message is deprecated by gNMI 0.3.0.
+// Reference: gNMI Specification Section 2.2.3.
+message Value {
+ option deprecated = true;
+ bytes value = 1; // Value of the variable being transmitted.
+ Encoding type = 2; // Encoding used for the value field.
+}
+
+// Encoding defines the value encoding formats that are supported by the gNMI
+// protocol. These encodings are used by both the client (when sending Set
+// messages to modify the state of the target) and the target when serializing
+// data to be returned to the client (in both Subscribe and Get RPCs).
+// Reference: gNMI Specification Section 2.3
+enum Encoding {
+ JSON = 0; // JSON encoded text.
+ BYTES = 1; // Arbitrarily encoded bytes.
+ PROTO = 2; // Encoded according to out-of-band agreed Protobuf.
+ ASCII = 3; // ASCII text of an out-of-band agreed format.
+ JSON_IETF = 4; // JSON encoded text as per RFC7951.
+}
+
+// Error message previously utilised to return errors to the client. Deprecated
+// in favour of using the google.golang.org/genproto/googleapis/rpc/status
+// message in the RPC response.
+// Reference: gNMI Specification Section 2.5
+message Error {
+ option deprecated = true;
+ uint32 code = 1; // Canonical gRPC error code.
+ string message = 2; // Human readable error.
+ google.protobuf.Any data = 3; // Optional additional information.
+}
+
+// Decimal64 is used to encode a fixed precision decimal number. The value
+// is expressed as a set of digits with the precision specifying the
+// number of digits following the decimal point in the digit set.
+message Decimal64 {
+ int64 digits = 1; // Set of digits.
+ uint32 precision = 2; // Number of digits following the decimal point.
+}
+
+// ScalarArray is used to encode a mixed-type array of values.
+message ScalarArray {
+ // The set of elements within the array. Each TypedValue message should
+ // specify only elements that have a field identifier of 1-7 (i.e., the
+ // values are scalar values).
+ repeated TypedValue element = 1;
+}
+
+// SubscribeRequest is the message sent by the client to the target when
+// initiating a subscription to a set of paths within the data tree. The
+// request field must be populated and the initial message must specify a
+// SubscriptionList to initiate a subscription. The message is subsequently
+// used to define aliases or trigger polled data to be sent by the target.
+// Reference: gNMI Specification Section 3.5.1.1
+message SubscribeRequest {
+ oneof request {
+ SubscriptionList subscribe = 1; // Specify the paths within a subscription.
+ Poll poll = 3; // Trigger a polled update.
+ AliasList aliases = 4; // Aliases to be created.
+ }
+ // Extension messages associated with the SubscribeRequest. See the
+ // gNMI extension specification for further definition.
+ repeated gnmi_ext.Extension extension = 5;
+}
+
+// Poll is sent within a SubscribeRequest to trigger the device to
+// send telemetry updates for the paths that are associated with the
+// subscription.
+// Reference: gNMI Specification Section Section 3.5.1.4
+message Poll {
+}
+
+// SubscribeResponse is the message used by the target within a Subscribe RPC.
+// The target includes a Notification message which is used to transmit values
+// of the path(s) that are associated with the subscription. The same message
+// is to indicate that the target has sent all data values once (is
+// synchronized).
+// Reference: gNMI Specification Section 3.5.1.4
+message SubscribeResponse {
+ oneof response {
+ Notification update = 1; // Changed or sampled value for a path.
+ // Indicate target has sent all values associated with the subscription
+ // at least once.
+ bool sync_response = 3;
+ // Deprecated in favour of google.golang.org/genproto/googleapis/rpc/status
+ Error error = 4 [deprecated=true];
+ }
+ // Extension messages associated with the SubscribeResponse. See the
+ // gNMI extension specification for further definition.
+ repeated gnmi_ext.Extension extension = 5;
+}
+
+// SubscriptionList is used within a Subscribe message to specify the list of
+// paths that the client wishes to subscribe to. The message consists of a
+// list of (possibly prefixed) paths, and options that relate to the
+// subscription.
+// Reference: gNMI Specification Section 3.5.1.2
+message SubscriptionList {
+ Path prefix = 1; // Prefix used for paths.
+ repeated Subscription subscription = 2; // Set of subscriptions to create.
+ // Whether target defined aliases are allowed within the subscription.
+ bool use_aliases = 3;
+ QOSMarking qos = 4; // DSCP marking to be used.
+ // Mode of the subscription.
+ enum Mode {
+ STREAM = 0; // Values streamed by the target (Sec. 3.5.1.5.2).
+ ONCE = 1; // Values sent once-off by the target (Sec. 3.5.1.5.1).
+ POLL = 2; // Values sent in response to a poll request (Sec. 3.5.1.5.3).
+ }
+ Mode mode = 5;
+ // Whether elements of the schema that are marked as eligible for aggregation
+ // should be aggregated or not.
+ bool allow_aggregation = 6;
+ // The set of schemas that define the elements of the data tree that should
+ // be sent by the target.
+ repeated ModelData use_models = 7;
+ // The encoding that the target should use within the Notifications generated
+ // corresponding to the SubscriptionList.
+ Encoding encoding = 8;
+ // An optional field to specify that only updates to current state should be
+ // sent to a client. If set, the initial state is not sent to the client but
+ // rather only the sync message followed by any subsequent updates to the
+ // current state. For ONCE and POLL modes, this causes the server to send only
+ // the sync message (Sec. 3.5.2.3).
+ bool updates_only = 9;
+}
+
+// Subscription is a single request within a SubscriptionList. The path
+// specified is interpreted (along with the prefix) as the elements of the data
+// tree that the client is subscribing to. The mode determines how the target
+// should trigger updates to be sent.
+// Reference: gNMI Specification Section 3.5.1.3
+message Subscription {
+ Path path = 1; // The data tree path.
+ SubscriptionMode mode = 2; // Subscription mode to be used.
+ uint64 sample_interval = 3; // ns between samples in SAMPLE mode.
+ // Indicates whether values that not changed should be sent in a SAMPLE
+ // subscription.
+ bool suppress_redundant = 4;
+ // Specifies the maximum allowable silent period in nanoseconds when
+ // suppress_redundant is in use. The target should send a value at least once
+ // in the period specified.
+ uint64 heartbeat_interval = 5;
+}
+
+// SubscriptionMode is the mode of the subscription, specifying how the
+// target must return values in a subscription.
+// Reference: gNMI Specification Section 3.5.1.3
+enum SubscriptionMode {
+ TARGET_DEFINED = 0; // The target selects the relevant mode for each element.
+ ON_CHANGE = 1; // The target sends an update on element value change.
+ SAMPLE = 2; // The target samples values according to the interval.
+}
+
+// QOSMarking specifies the DSCP value to be set on transmitted telemetry
+// updates from the target.
+// Reference: gNMI Specification Section 3.5.1.2
+message QOSMarking {
+ uint32 marking = 1;
+}
+
+// Alias specifies a data tree path, and an associated string which defines an
+// alias which is to be used for this path in the context of the RPC. The alias
+// is specified as a string which is prefixed with "#" to disambiguate it from
+// data tree element paths.
+// Reference: gNMI Specification Section 2.4.2
+message Alias {
+ Path path = 1; // The path to be aliased.
+ string alias = 2; // The alias value, a string prefixed by "#".
+}
+
+// AliasList specifies a list of aliases. It is used in a SubscribeRequest for
+// a client to create a set of aliases that the target is to utilize.
+// Reference: gNMI Specification Section 3.5.1.6
+message AliasList {
+ repeated Alias alias = 1; // The set of aliases to be created.
+}
+
+// SetRequest is sent from a client to the target to update values in the data
+// tree. Paths are either deleted by the client, or modified by means of being
+// updated, or replaced. Where a replace is used, unspecified values are
+// considered to be replaced, whereas when update is used the changes are
+// considered to be incremental. The set of changes that are specified within
+// a single SetRequest are considered to be a transaction.
+// Reference: gNMI Specification Section 3.4.1
+message SetRequest {
+ Path prefix = 1; // Prefix used for paths in the message.
+ repeated Path delete = 2; // Paths to be deleted from the data tree.
+ repeated Update replace = 3; // Updates specifying elements to be replaced.
+ repeated Update update = 4; // Updates specifying elements to updated.
+ // Extension messages associated with the SetRequest. See the
+ // gNMI extension specification for further definition.
+ repeated gnmi_ext.Extension extension = 5;
+}
+
+// SetResponse is the response to a SetRequest, sent from the target to the
+// client. It reports the result of the modifications to the data tree that were
+// specified by the client. Errors for this RPC should be reported using the
+// https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto
+// message in the RPC return. The gnmi.Error message can be used to add additional
+// details where required.
+// Reference: gNMI Specification Section 3.4.2
+message SetResponse {
+ Path prefix = 1; // Prefix used for paths.
+ // A set of responses specifying the result of the operations specified in
+ // the SetRequest.
+ repeated UpdateResult response = 2;
+ Error message = 3 [deprecated=true]; // The overall status of the transaction.
+ int64 timestamp = 4; // Timestamp of transaction (ns since epoch).
+ // Extension messages associated with the SetResponse. See the
+ // gNMI extension specification for further definition.
+ repeated gnmi_ext.Extension extension = 5;
+}
+
+// UpdateResult is used within the SetResponse message to communicate the
+// result of an operation specified within a SetRequest message.
+// Reference: gNMI Specification Section 3.4.2
+message UpdateResult {
+ // The operation that was associated with the Path specified.
+ enum Operation {
+ INVALID = 0;
+ DELETE = 1; // The result relates to a delete of Path.
+ REPLACE = 2; // The result relates to a replace of Path.
+ UPDATE = 3; // The result relates to an update of Path.
+ }
+ // Deprecated timestamp for the UpdateResult, this field has been
+ // replaced by the timestamp within the SetResponse message, since
+ // all mutations effected by a set should be applied as a single
+ // transaction.
+ int64 timestamp = 1 [deprecated=true];
+ Path path = 2; // Path associated with the update.
+ Error message = 3 [deprecated=true]; // Status of the update operation.
+ Operation op = 4; // Update operation type.
+}
+
+// GetRequest is sent when a client initiates a Get RPC. It is used to specify
+// the set of data elements for which the target should return a snapshot of
+// data. The use_models field specifies the set of schema modules that are to
+// be used by the target - where use_models is not specified then the target
+// must use all schema models that it has.
+// Reference: gNMI Specification Section 3.3.1
+message GetRequest {
+ Path prefix = 1; // Prefix used for paths.
+ repeated Path path = 2; // Paths requested by the client.
+ // Type of elements within the data tree.
+ enum DataType {
+ ALL = 0; // All data elements.
+ CONFIG = 1; // Config (rw) only elements.
+ STATE = 2; // State (ro) only elements.
+ // Data elements marked in the schema as operational. This refers to data
+ // elements whose value relates to the state of processes or interactions
+ // running on the device.
+ OPERATIONAL = 3;
+ }
+ DataType type = 3; // The type of data being requested.
+ Encoding encoding = 5; // Encoding to be used.
+ repeated ModelData use_models = 6; // The schema models to be used.
+ // Extension messages associated with the GetRequest. See the
+ // gNMI extension specification for further definition.
+ repeated gnmi_ext.Extension extension = 7;
+}
+
+// GetResponse is used by the target to respond to a GetRequest from a client.
+// The set of Notifications corresponds to the data values that are requested
+// by the client in the GetRequest.
+// Reference: gNMI Specification Section 3.3.2
+message GetResponse {
+ repeated Notification notification = 1; // Data values.
+ Error error = 2 [deprecated=true]; // Errors that occurred in the Get.
+ // Extension messages associated with the GetResponse. See the
+ // gNMI extension specification for further definition.
+ repeated gnmi_ext.Extension extension = 3;
+}
+
+// CapabilityRequest is sent by the client in the Capabilities RPC to request
+// that the target reports its capabilities.
+// Reference: gNMI Specification Section 3.2.1
+message CapabilityRequest {
+ // Extension messages associated with the CapabilityRequest. See the
+ // gNMI extension specification for further definition.
+ repeated gnmi_ext.Extension extension = 1;
+}
+
+// CapabilityResponse is used by the target to report its capabilities to the
+// client within the Capabilities RPC.
+// Reference: gNMI Specification Section 3.2.2
+message CapabilityResponse {
+ repeated ModelData supported_models = 1; // Supported schema models.
+ repeated Encoding supported_encodings = 2; // Supported encodings.
+ string gNMI_version = 3; // Supported gNMI version.
+ // Extension messages associated with the CapabilityResponse. See the
+ // gNMI extension specification for further definition.
+ repeated gnmi_ext.Extension extension = 4;
+}
+
+// ModelData is used to describe a set of schema modules. It can be used in a
+// CapabilityResponse where a target reports the set of modules that it
+// supports, and within the SubscribeRequest and GetRequest messages to specify
+// the set of models from which data tree elements should be reported.
+// Reference: gNMI Specification Section 3.2.3
+message ModelData {
+ string name = 1; // Name of the model.
+ string organization = 2; // Organization publishing the model.
+ string version = 3; // Semantic version of the model.
+}
+
diff --git a/src/gnmi/proto/gnmi_ext.proto b/src/gnmi/proto/gnmi_ext.proto
new file mode 100644
index 0000000..01b2864
--- /dev/null
+++ b/src/gnmi/proto/gnmi_ext.proto
@@ -0,0 +1,74 @@
+//
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+syntax = "proto3";
+
+// Package gnmi_ext defines a set of extensions messages which can be optionally
+// included with the request and response messages of gNMI RPCs. A set of
+// well-known extensions are defined within this file, along with a registry for
+// extensions defined outside of this package.
+package gnmi_ext;
+
+// The Extension message contains a single gNMI extension.
+message Extension {
+ oneof ext {
+ RegisteredExtension registered_ext = 1; // A registered extension.
+ // Well known extensions.
+ MasterArbitration master_arbitration = 2; // Master arbitration extension.
+ }
+}
+
+// The RegisteredExtension message defines an extension which is defined outside
+// of this file.
+message RegisteredExtension {
+ ExtensionID id = 1; // The unique ID assigned to this extension.
+ bytes msg = 2; // The binary-marshalled protobuf extension payload.
+}
+
+// RegisteredExtension is an enumeration acting as a registry for extensions
+// defined by external sources.
+enum ExtensionID {
+ EID_UNSET = 0;
+ // New extensions are to be defined within this enumeration - their definition
+ // MUST link to a reference describing their implementation.
+
+ // An experimental extension that may be used during prototyping of a new
+ // extension.
+ EID_EXPERIMENTAL = 999;
+}
+
+// MasterArbitration is used to select the master among multiple gNMI clients
+// with the same Roles. The client with the largest election_id is honored as
+// the master.
+// The document about gNMI master arbitration can be found at
+// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-master-arbitration.md
+message MasterArbitration {
+ Role role = 1;
+ Uint128 election_id = 2;
+}
+
+// Representation of unsigned 128-bit integer.
+message Uint128 {
+ uint64 high = 1;
+ uint64 low = 2;
+}
+
+// There can be one master for each role. The role is identified by its id.
+message Role {
+ string id = 1;
+ // More fields can be added if needed, for example, to specify what paths the
+ // role can read/write.
+}
+
diff --git a/src/gnmi/sysrepoapi.cpp b/src/gnmi/sysrepoapi.cpp
new file mode 100644
index 0000000..dcf7668
--- /dev/null
+++ b/src/gnmi/sysrepoapi.cpp
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#include "sysrepoapi.h"
+#include "log.h"
+
+#include <exception>
+#include <iostream>
+
+void event_notif_cb(const sr_ev_notif_type_t notif_type,
+ const char *xpath, const sr_val_t *values,
+ const size_t value_cnt, time_t timestamp,
+ void *private_ct)
+{
+ if (NULL == xpath || NULL == values || NULL == private_ct) {
+ ERROR("Error, NULL detect.");
+ return;
+ }
+
+ SysrepoAPI *sapi = (SysrepoAPI*) private_ct;
+
+ for (size_t i = 0; i < value_cnt; i++ ) {
+ sapi->setOutVal(*(values + i));
+ }
+
+ sapi->sendEvent(sapi);
+}
+
+SysrepoAPI::SysrepoAPI()
+{
+ sr_log_stderr(SR_LL_DBG);
+}
+
+SysrepoAPI::~SysrepoAPI()
+{
+ if (nullptr != subscription) {
+ sr_unsubscribe(sess, subscription);
+ }
+
+ if (nullptr != sess) {
+ sr_session_stop(sess);
+ }
+
+ if (nullptr != conn) {
+ sr_disconnect(conn);
+ }
+}
+
+void SysrepoAPI::setSysrepoName(const std::string& sysrepo_name)
+{
+ this->sysrepo_name = sysrepo_name;
+}
+
+void SysrepoAPI::connect()
+{
+ int rc = sr_connect(sysrepo_name.c_str(), SR_CONN_DEFAULT, &conn);
+ if (SR_ERR_OK != rc) {
+ ERROR("Failed connect to sysrepo.");
+ throw std::runtime_error("Failed connect to sysrepo.");
+ }
+}
+
+void SysrepoAPI::createSession(sr_datastore_e sesType)
+{
+ int rc = SR_ERR_OK;
+
+ if (nullptr == conn) {
+ ERROR("Error, nullptr detect.");
+ throw std::runtime_error("Error, nullptr detect.");
+ }
+
+ closeSession();
+
+ rc = sr_session_start(conn, sesType, SR_SESS_DEFAULT, &sess);
+ if (SR_ERR_OK != rc) {
+ ERROR("Failed connect to sysrepo session.");
+ throw std::runtime_error("Failed connect to sysrepo session.");
+ }
+}
+
+void SysrepoAPI::closeSession()
+{
+ if (nullptr != sess) {
+ sr_session_stop(sess);
+ sess = nullptr;
+ }
+}
+
+void SysrepoAPI::commit()
+{
+ int rc = SR_ERR_OK;
+
+ if (nullptr == sess) {
+ ERROR("Error, nullptr detect.");
+ throw std::runtime_error("Error, nullptr detect.");
+ }
+
+ rc = sr_commit(sess);
+ if (SR_ERR_OK != rc) {
+ ERROR("Failed commit change to sysrepo.");
+ throw std::runtime_error("Failed commit change to sysrepo.");
+ }
+}
+
+void SysrepoAPI::addData(const gNMIData& data)
+{
+ inputData.push_back(data);
+}
+
+void SysrepoAPI::getItemMessage()
+{
+ if (nullptr == sess) {
+ ERROR("Error, nullptr detect.");
+ throw std::runtime_error("Error, nullptr detect.");
+ }
+
+ for (const auto &data : inputData) {
+ sr_val_t *value = nullptr;
+ sr_val_iter_t *iter = nullptr;
+// size_t count;
+ DEBUG("Path: %s", data.getXPath(gNMIData::xPathType::sysrepoPath).c_str());
+
+ int rc = sr_get_items_iter(sess, data.getXPath(gNMIData::xPathType::sysrepoPath).c_str(),
+ &iter);
+// int rc = sr_get_item(sess, data.getXPath(gNMIData::xPathType::sysrepoPath).c_str(),
+// &value);
+ if (SR_ERR_OK != rc) {
+ ERROR("Failed get item from sysrepo.");
+ throw std::runtime_error("Failed get item from sysrepo.");
+ }
+
+ while (SR_ERR_OK == sr_get_item_next(sess, iter, &value)) {
+ sr_print_val(value);
+
+// sr_print_val(value);
+
+ gNMIData oData;
+
+ oData.setXPath(value->xpath, gNMIData::xPathType::sysrepoPath);
+ oData.setValue(sr_val_to_str(value));
+
+ outpuData.push_back(oData);
+
+ sr_free_val(value);
+ }
+ }
+}
+
+void SysrepoAPI::setItemMessage()
+{
+ int rc = SR_ERR_OK;
+
+// sr_xpath_ctx_t state = {0};
+
+ if (nullptr == sess) {
+ ERROR("Error, nullptr detect.");
+ throw std::runtime_error("Error, nullptr detect.");
+ }
+
+ for (const auto &data : inputData) {
+ DEBUG("Set path: %s", data.getXPath(gNMIData::xPathType::sysrepoPath).c_str());
+ DEBUG("Val: %s", data.getStr().c_str());
+
+// if (gNMIData::ValueType::dStringVal == data.dataType()) {
+ rc = sr_set_item_str(sess,
+ data.getXPath(gNMIData::xPathType::sysrepoPath).c_str(),
+ data.getStr().c_str(), SR_EDIT_DEFAULT);
+// } else {
+// sr_val_t value = { 0 };
+// setSrVal(value, data);
+//
+// rc = sr_set_item(sess, data.getXPath(gNMIData::xPathType::sysrepoPath).c_str(),
+// &value, SR_EDIT_DEFAULT);
+// }
+ if (SR_ERR_OK != rc) {
+ ERROR("Failed set item to sysrepo.");
+ throw std::runtime_error("Failed set item to sysrepo.");
+ }
+
+ //TODO: Hmm, this is duplication copy, I`m not sure with this solution.
+ outpuData.push_back(data);
+ }
+}
+
+void SysrepoAPI::rpcSend(const std::string& xpath, sr_val_t &input)
+{
+ int rc = SR_ERR_OK;
+ sr_val_t *output = nullptr;
+ size_t output_cnt = 0;
+
+ if (nullptr == sess) {
+ ERROR("Error, nullptr detect.");
+ throw std::runtime_error("Error, nullptr detect.");
+ }
+
+ rc = sr_rpc_send(sess, xpath.c_str(), &input, 1, &output, &output_cnt);
+ if (SR_ERR_OK != rc) {
+ ERROR("RPC message failed: %s.", sr_strerror(rc));
+ throw std::runtime_error("RPC message failed.");
+ }
+}
+
+void SysrepoAPI::eventSubscribeMessage()
+{
+ int rc = SR_ERR_OK;
+
+ if (nullptr == sess) {
+ ERROR("Error, nullptr detect.");
+ throw std::runtime_error("Error, nullptr detect.");
+ }
+
+ for (const auto &data : inputData) {
+ DEBUG("Register subscribe path: %s",
+ data.getXPath(gNMIData::xPathType::sysrepoPath).c_str());
+ rc = sr_event_notif_subscribe(sess,
+ data.getXPath(gNMIData::xPathType::sysrepoPath).c_str(),
+ event_notif_cb, this, SR_SUBSCR_DEFAULT,
+ &subscription);
+ if (SR_ERR_OK != rc) {
+ ERROR("Event notification subscribe failed: %s.", sr_strerror(rc));
+ throw std::runtime_error("Event notification subscribe failed.");
+ }
+ }
+}
+
+const std::list<SysrepSchema> &SysrepoAPI::getSchemas()
+{
+ int rc = SR_ERR_OK;
+ sr_schema_t *schemas = nullptr;
+ size_t schema_cnt = 0;
+
+ if (nullptr == sess) {
+ ERROR("Error, nullptr detect.");
+ throw std::runtime_error("Error, nullptr detect.");
+ }
+
+ rc = sr_list_schemas(sess, &schemas, &schema_cnt);
+ if (SR_ERR_OK != rc) {
+ ERROR("List schemas failed: %s.", sr_strerror(rc));
+ throw std::runtime_error("List schemas failed failed.");
+ }
+
+ for (size_t i = 0; i < schema_cnt; i++) {
+// DEBUG("Module: %s, xpath: %s, prefix: %s, r yang: %s, revison: %s",
+// schemas[i].module_name, schemas[i].ns, schemas[i].prefix,
+// schemas[i].revision.file_path_yang, schemas[i].revision.revision);
+ SysrepSchema schema;
+
+ if (NULL != schemas[i].module_name) {
+ schema.moduleName = std::string(schemas[i].module_name);
+ }
+
+ if (NULL != schemas[i].revision.revision) {
+ schema.revision = std::string(schemas[i].revision.revision);
+ }
+ schemaList.push_back(schema);
+ }
+
+ sr_free_schemas(schemas, schema_cnt);
+
+ return schemaList;
+}
+
+std::list<gNMIData> SysrepoAPI::getOutputData() const
+{
+ return outpuData;
+}
+
+void SysrepoAPI::cleanData()
+{
+ inputData.clear();
+ outpuData.clear();
+ schemaList.clear();
+}
+
+enum sr_type_e SysrepoAPI::convergNMITypeToSysrepo(gNMIData::ValueType type)
+{
+ switch (type) {
+ case gNMIData::ValueType::dStringVal:
+ return SR_STRING_T;
+
+ case gNMIData::ValueType::dIntVal:
+ return SR_INT32_T;
+
+ case gNMIData::ValueType::UnknownVal:
+ default:
+ return SR_UNKNOWN_T;
+ }
+
+ return SR_UNKNOWN_T;
+}
+
+gNMIData::ValueType SysrepoAPI::convergSysrepoTypeTogNMI(sr_type_e type)
+{
+ switch (type) {
+ case SR_STRING_T:
+ return gNMIData::ValueType::dStringVal;
+
+ case SR_INT8_T:
+ case SR_INT16_T:
+ case SR_INT32_T:
+ case SR_INT64_T:
+ case SR_UINT8_T:
+ case SR_UINT16_T:
+ case SR_UINT32_T:
+ case SR_UINT64_T:
+ return gNMIData::ValueType::dIntVal;
+
+ case SR_ANYDATA_T:
+ case SR_ANYXML_T:
+ case SR_BINARY_T:
+ case SR_BITS_T:
+ case SR_BOOL_T:
+ case SR_CONTAINER_PRESENCE_T:
+ case SR_CONTAINER_T:
+ case SR_DECIMAL64_T:
+ case SR_ENUM_T:
+ case SR_IDENTITYREF_T:
+ case SR_INSTANCEID_T:
+ case SR_LEAF_EMPTY_T:
+ case SR_LIST_T:
+ case SR_TREE_ITERATOR_T:
+ case SR_UNKNOWN_T:
+ return gNMIData::ValueType::UnknownVal;
+
+ default:
+ return gNMIData::ValueType::UnknownVal;
+ }
+
+ return gNMIData::ValueType::UnknownVal;
+}
+
+void SysrepoAPI::setSrVal(sr_val_t& value, const gNMIData& gData)
+{
+ value.type = convergNMITypeToSysrepo(gData.dataType());
+
+ switch (gData.dataType()) {
+ case gNMIData::ValueType::dStringVal:
+ value.data.string_val = (char *) gData.getStr().c_str();
+ DEBUG("Val: %s", value.data.string_val);
+ break;
+
+ case gNMIData::ValueType::dIntVal:
+ value.data.int32_val = gData.getInt();
+ DEBUG("Val: %d", value.data.int32_val);
+ break;
+
+ case gNMIData::ValueType::UnknownVal:
+ default:
+ DEBUG("Unknown value.");
+ //TODO:
+ break;
+ }
+}
+
+void SysrepoAPI::setOutVal(const sr_val_t& value)
+{
+// auto key = convergSysrepoTypeTogNMI(value.type);
+ std::string str;
+
+ //TODO: Need rewrite, only for test
+ switch (value.type) {
+ case SR_STRING_T:
+ str = std::string(value.data.string_val);
+ break;
+
+ case SR_INT8_T:
+ str = std::to_string(value.data.int8_val);
+ break;
+
+ case SR_INT16_T:
+ str = std::to_string(value.data.int16_val);
+ break;
+
+ case SR_INT32_T:
+ str = std::to_string(value.data.int32_val);
+ break;
+
+ case SR_INT64_T:
+ str = std::to_string(value.data.int64_val);
+ break;
+
+ case SR_UINT8_T:
+ str = std::to_string(value.data.uint8_val);
+ break;
+ case SR_UINT16_T:
+ str = std::to_string(value.data.uint16_val);
+ break;
+ case SR_UINT32_T:
+ str = std::to_string(value.data.uint32_val);
+ break;
+ case SR_UINT64_T:
+ str = std::to_string(value.data.uint64_val);
+ break;
+
+ default:
+ //TODO: Need implement.
+ break;
+ }
+
+ gNMIData sData;
+ sData.setXPath(value.xpath, gNMIData::xPathType::sysrepoPath);
+ sData.setValue(str);
+
+ outpuData.push_back(sData);
+}
diff --git a/src/gnmi/sysrepoapi.h b/src/gnmi/sysrepoapi.h
new file mode 100644
index 0000000..8ebcf95
--- /dev/null
+++ b/src/gnmi/sysrepoapi.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#ifndef SYSREPOAPI_H
+#define SYSREPOAPI_H
+
+#include "gnmidata.h"
+#include "eventreciverbase.h"
+
+#include <sysrepo.h>
+#include <sysrepo/values.h>
+#include <string>
+#include <list>
+
+#include <iostream>
+
+struct SysrepoValue
+{
+ void setXPath(const std::string &str) {
+ std::string tmp = str;
+ std::size_t pos = 0;
+
+ while (std::string::npos != (pos = tmp.find(":", pos))) {
+ tmp.replace(pos, std::string(":").length(), "/");
+ }
+
+ xpath = tmp;
+ }
+
+ std::string xpath;
+ std::string value;
+};
+
+struct SysrepSchema
+{
+ std::string moduleName;
+ std::string revision;
+};
+
+/**
+ * @todo write docs
+ */
+class SysrepoAPI : public virtual BaseSender<SysrepoAPI>
+{
+public:
+ /**
+ * Default constructor
+ */
+ SysrepoAPI();
+
+ /**
+ * Destructor
+ */
+ ~SysrepoAPI();
+
+ void setSysrepoName(const std::string &sysrepo_name);
+
+ void connect();
+ void createSession(sr_datastore_e sesType = SR_DS_RUNNING);
+ void closeSession();
+ void commit();
+
+ void addData(const gNMIData &data);
+ void getItemMessage();
+ void setItemMessage();
+ void rpcSend(const std::string &xpath, sr_val_t &input);
+ void eventSubscribeMessage();
+ const std::list<SysrepSchema> &getSchemas();
+// void print_value();
+
+ std::list<gNMIData> getOutputData() const;
+ void cleanData();
+
+private:
+ friend void event_notif_cb(const sr_ev_notif_type_t notif_type,
+ const char *xpath, const sr_val_t *values,
+ const size_t value_cnt, time_t timestamp,
+ void *private_ct);
+ enum sr_type_e convergNMITypeToSysrepo(gNMIData::ValueType type);
+ gNMIData::ValueType convergSysrepoTypeTogNMI(sr_type_e type);
+ void setSrVal(sr_val_t &value, const gNMIData &gData);
+ void setOutVal(const sr_val_t &value);
+
+private:
+ std::string sysrepo_name = "app";
+ sr_conn_ctx_t *conn = nullptr;
+ sr_session_ctx_t *sess = nullptr;
+ sr_subscription_ctx_t *subscription = nullptr;
+
+ std::list<gNMIData> inputData;
+ std::list<gNMIData> outpuData;
+ std::list<SysrepSchema> schemaList;
+
+};
+
+#endif // SYSREPOAPI_H
diff --git a/src/gnmi/sysrepoapipool.cpp b/src/gnmi/sysrepoapipool.cpp
new file mode 100644
index 0000000..7e94a56
--- /dev/null
+++ b/src/gnmi/sysrepoapipool.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#include "sysrepoapipool.h"
+
+SysrepoApiPool::SysrepoApiPool()
+ : pool{}
+{
+
+}
+
+SysrepoApiPool::~SysrepoApiPool()
+{
+
+}
+
+SysrepoAPI & SysrepoApiPool::get(const std::string &name)
+{
+ auto &sapi = pool[name];
+ sapi.setSysrepoName(name);
+
+ return sapi;
+}
+
+void SysrepoApiPool::remove(const std::string &name)
+{
+ pool.erase(name);
+}
+
diff --git a/src/gnmi/sysrepoapipool.h b/src/gnmi/sysrepoapipool.h
new file mode 100644
index 0000000..ddc3260
--- /dev/null
+++ b/src/gnmi/sysrepoapipool.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#ifndef SYSREPOAPIPOOL_H
+#define SYSREPOAPIPOOL_H
+
+#include "sysrepoapi.h"
+
+#include <map>
+#include <string>
+
+/**
+ * @todo write docs
+ */
+class SysrepoApiPool
+{
+public:
+ /**
+ * Default constructor
+ */
+ SysrepoApiPool();
+
+ /**
+ * Destructor
+ */
+ ~SysrepoApiPool();
+
+ SysrepoAPI &get(const std::string &name);
+ void remove(const std::string &name);
+
+private:
+ std::map<std::string, SysrepoAPI> pool;
+};
+
+#endif // SYSREPOAPIPOOL_H
diff --git a/src/gnmi/xml2json.cpp b/src/gnmi/xml2json.cpp
new file mode 100644
index 0000000..15dac41
--- /dev/null
+++ b/src/gnmi/xml2json.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#include "xml2json.h"
+#include "log.h"
+
+#include <exception>
+#include <sstream>
+#include <iostream>
+
+XML2JSON::XML2JSON(const XML2JSON& other)
+{
+
+}
+
+XML2JSON::~XML2JSON()
+{
+
+}
+
+void XML2JSON::setData(const std::string& data, XML2JSON::string_type_t type)
+{
+ switch (type) {
+ case string_type_t::JSONSTRING:
+ {
+ Json::Reader read;
+ DEBUG("JSONSTRING: %s", data.c_str());
+
+ read.parse(data, jsonRoot);
+ DEBUG("Root size: %d", jsonRoot.size());
+ }
+ break;
+
+ case string_type_t::XMLSTRING:
+ {
+ int rc = 0;
+ rc = xmlDoc.load_string(data.c_str(),
+ pugi::parse_default | pugi::parse_comments);
+ if (0 != rc) {
+ ERROR("Error parse xml string.");
+ throw std::logic_error("Error parse xml string.");
+ }
+ }
+ break;
+
+ default:
+ throw std::logic_error("Unknown type.");
+ break;
+ }
+}
+
+void XML2JSON::setPrefix(const std::string& prefix)
+{
+ this->prefix = prefix;
+}
+
+std::string XML2JSON::getJson()
+{
+ switch (type) {
+ case string_type_t::JSONSTRING:
+ //TODO:
+ return std::string("");
+
+ case string_type_t::XMLSTRING:
+ //TODO:
+ return std::string("");
+
+ default:
+ break;
+ }
+
+ throw std::logic_error("Unknown type.");
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+std::string XML2JSON::getXML()
+{
+ std::ostringstream stream;
+
+ switch (type) {
+ case string_type_t::JSONSTRING:
+ createXML();
+
+ case string_type_t::XMLSTRING:
+ xmlDoc.print(stream);
+ DEBUG("XML doc: %s", xmlDoc.path().c_str());
+ return stream.str();
+
+ default:
+ break;
+ }
+
+ throw std::logic_error("Unknown type.");
+}
+#pragma GCC diagnostic pop
+
+const std::list<gNMIData> & XML2JSON::getgNMIData()
+{
+ createGNMIData();
+
+ return gnmiData;
+}
+
+void XML2JSON::parseJSON()
+{
+// parseJSONValueType(jsonRoot);
+}
+
+void XML2JSON::parseJSONValueType(const Json::Value& value,
+ pugi::xml_node &node)
+{
+ switch (value.type()) {
+ case Json::nullValue:
+ break;
+
+ case Json::intValue:
+ DEBUG("Value: %d", value.asInt());
+ node.append_child(pugi::node_pcdata).set_value(
+ std::to_string(value.asInt()).c_str());
+ break;
+
+ case Json::uintValue:
+ DEBUG("Value: %d", value.asUInt());
+ node.append_child(pugi::node_pcdata).set_value(
+ std::to_string(value.asUInt()).c_str());
+ break;
+
+ case Json::realValue:
+ DEBUG("Value: %f", value.asFloat());
+ node.append_child(pugi::node_pcdata).set_value(
+ std::to_string(value.asFloat()).c_str());
+ break;
+
+ case Json::stringValue:
+ DEBUG("Value: %s", value.asString().c_str());
+ node.append_child(pugi::node_pcdata).set_value(
+ value.asString().c_str());
+ break;
+
+ case Json::booleanValue:
+ DEBUG("Value: %d", value.asBool());
+ node.append_child(pugi::node_pcdata).set_value(
+ std::to_string(value.asBool()).c_str());
+ break;
+
+ case Json::arrayValue:
+ parseJSONArray(value, node);
+ break;
+
+ case Json::objectValue:
+ parseJSONObject(value, node);
+ break;
+
+ default:
+ DEBUG("Unknown value type.");
+ break;
+ }
+}
+
+void XML2JSON::parseJSONValueType(const Json::Value& value,
+ const std::string& prefix)
+{
+ switch (value.type()) {
+ case Json::nullValue:
+ {
+ gNMIData data;
+ std::string tmp(prefix);
+ tmp.pop_back();
+ data.setXPath(tmp);
+ gnmiData.push_back(data);
+ }
+ break;
+
+ case Json::intValue:
+ // DEBUG("Value: %d", value.asInt());
+ {
+ gNMIData data;
+ std::string tmp(prefix);
+ tmp.pop_back();
+ data.setXPath(tmp);
+ data.setValue(value.asInt());
+ gnmiData.push_back(data);
+ }
+ break;
+
+ case Json::uintValue:
+ // DEBUG("Value: %d", value.asUInt());
+ {
+ gNMIData data;
+ std::string tmp(prefix);
+ tmp.pop_back();
+ data.setXPath(tmp);
+ data.setValue(value.asUInt());
+ gnmiData.push_back(data);
+ }
+ break;
+
+ case Json::realValue:
+ // DEBUG("Value: %f", value.asFloat());
+ {
+ gNMIData data;
+ std::string tmp(prefix);
+ tmp.pop_back();
+ data.setXPath(tmp);
+ data.setValue(value.asFloat());
+ gnmiData.push_back(data);
+ }
+ break;
+
+ case Json::stringValue:
+ // DEBUG("Value: %s", value.asString().c_str());
+ {
+ gNMIData data;
+ std::string tmp(prefix);
+ tmp.pop_back();
+ data.setXPath(tmp);
+ data.setValue(value.asString());
+ gnmiData.push_back(data);
+ }
+ break;
+
+ case Json::booleanValue:
+ // DEBUG("Value: %d", value.asBool());
+ {
+ gNMIData data;
+ std::string tmp(prefix);
+ tmp.pop_back();
+ data.setXPath(tmp);
+ data.setValue(value.asBool() ? "true" : "false");
+ gnmiData.push_back(data);
+ }
+ break;
+
+ case Json::arrayValue:
+ parseJSONArray(value, prefix);
+ break;
+
+ case Json::objectValue:
+ parseJSONObject(value, prefix);
+ break;
+
+ default:
+ DEBUG("Unknown value type.");
+ break;
+ }
+}
+
+void XML2JSON::parseJSONArray(const Json::Value& value, pugi::xml_node &node)
+{
+ if (Json::arrayValue != value.type()) {
+ ERROR("Wrong JSON Value type");
+ throw std::logic_error("Wrong JSON Value type");
+ }
+
+ for (const auto &elm : value) {
+ parseJSONValueType(elm, node);
+ }
+}
+
+void XML2JSON::parseJSONArray(const Json::Value& value,
+ const std::string& prefix)
+{
+ if (Json::arrayValue != value.type()) {
+ ERROR("Wrong JSON Value type");
+ throw std::logic_error("Wrong JSON Value type");
+ }
+
+ for (const auto &elm : value) {
+ parseJSONValueType(elm, prefix);
+ }
+}
+
+void XML2JSON::parseJSONObject(const Json::Value& value, pugi::xml_node &node)
+{
+ if (Json::objectValue != value.type()) {
+ ERROR("Wrong JSON Value type");
+ throw std::logic_error("Wrong JSON Value type");
+ }
+
+ for (const auto &name : value.getMemberNames()) {
+ DEBUG("Name: %s", name.c_str());
+ auto child = node.append_child(name.c_str());
+ parseJSONValueType(value.get(name, value), child);
+ }
+}
+
+void XML2JSON::parseJSONObject(const Json::Value& value,
+ const std::string& prefix)
+{
+ if (Json::objectValue != value.type()) {
+ ERROR("Wrong JSON Value type");
+ throw std::logic_error("Wrong JSON Value type");
+ }
+
+ for (const auto &name : value.getMemberNames()) {
+ DEBUG("Name: %s", name.c_str());
+ std::string tmp = prefix + name + "/";
+ parseJSONValueType(value.get(name, value), tmp);
+ }
+}
+
+void XML2JSON::createXML()
+{
+// auto elm = xmlDoc.append_child(pugi::xml_node_type::node_document);
+ parseJSONValueType(jsonRoot, xmlDoc);
+}
+
+void XML2JSON::createGNMIData()
+{
+ std::string prefix = this->prefix + "/";
+ parseJSONValueType(jsonRoot, prefix);
+}
diff --git a/src/gnmi/xml2json.h b/src/gnmi/xml2json.h
new file mode 100644
index 0000000..fd99a2d
--- /dev/null
+++ b/src/gnmi/xml2json.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech.
+ *
+ * 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.
+ */
+
+#ifndef XML2JSON_H
+#define XML2JSON_H
+
+#include <string>
+#include <list>
+
+#include <json/json.h>
+#include <pugixml.hpp>
+
+#include "gnmidata.h"
+
+/**
+ * @todo write docs
+ */
+class XML2JSON
+{
+public:
+ enum class string_type_t {
+ XMLSTRING,
+ JSONSTRING
+ };
+
+public:
+ /**
+ * Default constructor
+ */
+ XML2JSON() = default;
+
+ /**
+ * Copy Constructor
+ *
+ * @param other TODO
+ */
+ XML2JSON(const XML2JSON& other);
+
+ /**
+ * Destructor
+ */
+ ~XML2JSON();
+
+ /**
+ * @todo write docs
+ *
+ * @param other TODO
+ * @return TODO
+ */
+// XML2JSON& operator=(const XML2JSON& other);
+
+ void setData(const std::string &data, string_type_t type = string_type_t::JSONSTRING);
+ void setPrefix(const std::string &prefix);
+ std::string getJson();
+ std::string getXML();
+ const std::list<gNMIData> &getgNMIData();
+
+private:
+ void parseJSON();
+ void parseJSONValueType(const Json::Value &value, pugi::xml_node &node);
+ void parseJSONValueType(const Json::Value &value, const std::string &prefix);
+ void parseJSONArray(const Json::Value &value, pugi::xml_node &node);
+ void parseJSONArray(const Json::Value &value, const std::string &prefix);
+ void parseJSONObject(const Json::Value &value, pugi::xml_node &node);
+ void parseJSONObject(const Json::Value &value, const std::string &prefix);
+
+ void createXML();
+ void createGNMIData();
+
+private:
+ std::string prefix = "/";
+ string_type_t type = string_type_t::JSONSTRING;
+ Json::Value jsonRoot;
+ pugi::xml_document xmlDoc;
+ std::list<gNMIData> gnmiData;
+};
+
+#endif // XML2JSON_H