2 * Copyright (c) 2019 PANTHEON.tech.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "gnmiserver.h"
29 #include <sysrepo/xpath.h>
31 gNMIServer::gNMIServer(SysrepoAPI& sysrepo)
36 grpc::Status gNMIServer::Capabilities(grpc::ServerContext* context,
37 const gnmi::CapabilityRequest* request,
38 gnmi::CapabilityResponse* reply)
40 DEBUG("Capabilities message");
42 if (0 < request->extension_size()) {
46 sysrepo.createSession();
47 const auto &ss = sysrepo.getSchemas();
49 for (const auto schema : ss) {
50 auto model = reply->add_supported_models();
51 model->set_name(schema.moduleName);
52 model->set_version(schema.revision);
55 reply->add_supported_encodings(gnmi::Encoding::ASCII);
57 //FIXME: I`m not sure, if this is a correct version of gnmi.
58 reply->set_gnmi_version(std::to_string(gnmi::kGnmiServiceFieldNumber));
60 sysrepo.closeSession();
65 grpc::Status gNMIServer::Get(grpc::ServerContext* context,
66 const gnmi::GetRequest* request,
67 gnmi::GetResponse* reply)
71 std::cout << gnmi::GetRequest_DataType_Name(request->type()) << std::endl;
74 sysrepo.createSession();
76 auto &prefix = request->prefix();
77 dataPrefix = convertToXPath(prefix);
78 DEBUG("Prefix: %s", dataPrefix.c_str());
80 DEBUG("Data type: %s",
81 request->DataType_Name(request->type()).c_str());
82 DEBUG("Encoding: %s", gnmi::Encoding_Name(request->encoding()).c_str());
84 for (const auto &model : request->use_models()) {
85 DEBUG("Model, name: %s, organization: %s, version: %s",
86 model.name().c_str(), model.organization().c_str(),
87 model.version().c_str());
90 // for (const auto &extension : request->extension()) {
94 if (0 < request->path().size()) {
95 for (const auto &path : request->path()) {
96 std::string strPath = convertToXPath(path);
98 vData.setXPath(dataPrefix + strPath);
99 sysrepo.addData(vData);
100 sysrepo.getItemMessage();
102 auto notification = reply->add_notification();
104 notification->set_timestamp(getTimeNanosec());
105 auto elem = notification->mutable_prefix();
106 xpathTogNMIEl(dataPrefix, *elem);
108 const auto &oData = sysrepo.getOutputData();
110 for (const auto &data : oData) {
111 auto nupdate = notification->add_update();
112 xpathTogNMIEl(data.getXPath(), *nupdate->mutable_path());
113 auto uval = nupdate->mutable_val();
114 uval->set_string_val(data.getStr());
120 sysrepo.closeSession();
123 sysrepo.closeSession();
124 return Status::CANCELLED;
130 //TODO: Need handle INVALID operation, somehow, but how????
131 grpc::Status gNMIServer::Set(grpc::ServerContext* context,
132 const gnmi::SetRequest* request,
133 gnmi::SetResponse* reply)
135 DEBUG("Set message");
138 reply->set_timestamp(getTimeNanosec());
140 auto prefix = reply->mutable_prefix();
144 sysrepo.createSession(SR_DS_RUNNING);
146 dataPrefix = convertToXPath(request->prefix());
148 if (0 < request->delete__size()) {
149 for (const auto &del : request->delete_()) {
151 path = convertToXPath(del);
152 vData.setXPath(dataPrefix + path);
153 DEBUG("Delete: %s", vData.getXPath().c_str());
154 sysrepo.addData(vData);
156 sysrepo.setItemMessage();
158 const auto &oData = sysrepo.getOutputData();
159 for (const auto &data : oData) {
160 auto updateResult = reply->add_response();
161 updateResult->set_op(gnmi::UpdateResult::DELETE);
162 xpathTogNMIEl(data.getXPath(), *updateResult->mutable_path());
168 if (0 < request->replace_size()) {
169 for (const auto &replace : request->replace()) {
171 handleUpdateMessage(replace);
172 DEBUG("Replace : %s", vData.getXPath().c_str());
173 sysrepo.addData(vData);
175 sysrepo.setItemMessage();
177 const auto &oData = sysrepo.getOutputData();
178 for (const auto &data : oData) {
179 auto updateResult = reply->add_response();
180 updateResult->set_op(gnmi::UpdateResult::REPLACE);
181 xpathTogNMIEl(data.getXPath(), *updateResult->mutable_path());
187 if (0 < request->update_size()) {
188 for (const auto &update : request->update()) {
190 handleUpdateMessage(update);
191 DEBUG("Update: %s", vData.getXPath().c_str());
192 // sysrepo.addData(vData);
194 json.setData(vData.getStr());
195 json.setPrefix(vData.getXPath());
196 auto &gdatas = json.getgNMIData();
197 for (const auto &gdata : gdatas) {
198 DEBUG("Data xpath: %s, data: %s", gdata.getXPath().c_str(),
199 gdata.getStr().c_str());
200 sysrepo.addData(gdata);
202 // DEBUG("%s", json.getXML().c_str());
204 sysrepo.setItemMessage();
206 const auto &oData = sysrepo.getOutputData();
207 for (const auto &data : oData) {
208 auto updateResult = reply->add_response();
209 updateResult->set_op(gnmi::UpdateResult::UPDATE);
210 xpathTogNMIEl(data.getXPath(), *updateResult->mutable_path());
216 //TODO: Need some special handling for sysrepo
218 sysrepo.closeSession();
221 sysrepo.closeSession();
222 return Status::CANCELLED;
228 grpc::Status gNMIServer::Subscribe(grpc::ServerContext* context,
229 ServerReaderWriter<gnmi::SubscribeResponse,
230 gnmi::SubscribeRequest>* stream)
232 DEBUG("Subscribe message");
234 gnmi::SubscribeRequest request;
237 sysrepo.registerReciver<gNMIServer>(this);
238 sysrepo.registerStream(stream);
240 sysrepo.createSession(SR_DS_RUNNING);
241 while (stream->Read(&request)) {
242 switch (request.request_case()) {
243 case gnmi::SubscribeRequest::kAliases:
246 case gnmi::SubscribeRequest::kPoll:
249 case gnmi::SubscribeRequest::kSubscribe:
250 subscibeList(request.subscribe());
253 case gnmi::SubscribeRequest::REQUEST_NOT_SET:
257 DEBUG("Unknown request case.");
263 sysrepo.closeSession();
264 return Status::CANCELLED;
268 sysrepo.closeSession();
269 DEBUG("Subscribe message end");
274 void gNMIServer::subscibeList(const gnmi::SubscriptionList& slist)
276 std::string prefix = convertToXPath(slist.prefix());
278 if (0 < slist.subscription_size()) {
279 for (const auto &sub : slist.subscription()) {
281 vData.setXPath(prefix + convertToXPath(sub.path()));
282 //TODO: Call sysrepo event
284 DEBUG("Subs Mode: %s",
285 gnmi::SubscriptionMode_Name(sub.mode()).c_str());
286 DEBUG("Subs xpath: %s", vData.getXPath().c_str());
287 sysrepo.addData(vData);
288 sysrepo.eventSubscribeMessage();
292 DEBUG("Subscribe list mode: %s", slist.Mode_Name(slist.mode()).c_str());
294 if (0 < slist.use_models_size()) {
295 for (const auto &umod : slist.use_models()) {
296 DEBUG("Name: %s, Organization: %s, Version: %s",
297 umod.name().c_str(), umod.organization().c_str(),
298 umod.version().c_str());
302 DEBUG("Encoding: %s", gnmi::Encoding_Name(slist.encoding()).c_str());
304 DEBUG("Update only: %d", slist.updates_only());
307 void gNMIServer::receiveWriteEvent(SysrepoAPI* psender)
309 gnmi::SubscribeResponse response;
310 ServerReaderWriter<gnmi::SubscribeResponse, gnmi::SubscribeRequest>* stream;
312 stream = (ServerReaderWriter<gnmi::SubscribeResponse,
313 gnmi::SubscribeRequest>*) psender->getStream();
315 auto sData = psender->getOutputData();
316 auto update = response.mutable_update();
318 update->set_timestamp(getTimeNanosec());
319 auto elem = update->mutable_prefix();
321 xpathTogNMIEl("/", *elem);
322 for (const auto &data : sData) {
323 DEBUG("XPath: %s, value: %s",
324 data.getXPath().c_str(), data.getStr().c_str());
325 auto nupdate = update->add_update();
326 xpathTogNMIEl(data.getXPath(), *nupdate->mutable_path());
327 auto val = nupdate->mutable_val();
328 val->set_string_val(data.getStr());
331 psender->cleanData();
332 stream->Write(response);
335 void gNMIServer::parsePathMsg(const gnmi::Path& path)
337 if (0 >= path.elem_size()) {
338 DEBUG("Path is empty");
344 std::string gNMIServer::convertToXPath(const gnmi::Path& path)
346 std::string str = "/";
348 if (0 < path.elem_size()) {
349 for (const auto &elm : path.elem()) {
352 for (const auto &el : elm.key()) {
353 str += "[" + el.first + "='" + el.second + "']";
364 void gNMIServer::printPath(const gnmi::Path& path)
366 if (0 < path.element().size()) {
367 for (const auto &element : path.element()) {
368 DEBUG("Element: %s", element.c_str());
372 if (0 < path.elem_size()) {
373 for (const auto &elm : path.elem()) {
374 DEBUG("Elm name: %ss", elm.name().c_str());
375 for (const auto &el : elm.key()) {
376 DEBUG("El key: %s, val: %s", el.first.c_str(),
383 void gNMIServer::handleUpdateMessage(const gnmi::Update& msg)
385 vData.setXPath(dataPrefix + convertToXPath(msg.path()));
388 handleTypeValueMsg(msg.val());
392 void gNMIServer::handleTypeValueMsg(const gnmi::TypedValue& msg)
394 DEBUG("Val case: %d", msg.value_case());
396 switch (msg.value_case()) {
397 case gnmi::TypedValue::ValueCase::kStringVal:
398 vData.setValue(msg.string_val());
401 case gnmi::TypedValue::ValueCase::kIntVal:
404 case gnmi::TypedValue::ValueCase::kUintVal:
407 case gnmi::TypedValue::ValueCase::kBoolVal:
410 case gnmi::TypedValue::ValueCase::kBytesVal:
413 case gnmi::TypedValue::ValueCase::kFloatVal:
416 case gnmi::TypedValue::ValueCase::kDecimalVal:
419 case gnmi::TypedValue::ValueCase::kLeaflistVal:
422 case gnmi::TypedValue::ValueCase::kAnyVal:
425 case gnmi::TypedValue::ValueCase::kJsonVal:
428 case gnmi::TypedValue::ValueCase::kJsonIetfVal:
429 vData.setValue(msg.json_ietf_val());
432 case gnmi::TypedValue::ValueCase::kAsciiVal:
435 case gnmi::TypedValue::ValueCase::kProtoBytes:
438 case gnmi::TypedValue::ValueCase::VALUE_NOT_SET:
442 DEBUG("Unknown type.");
447 void gNMIServer::xpathTogNMIEl(const std::string& str, gnmi::Path& path)
449 sr_xpath_ctx_t state;
450 std::string tmp(str);
451 const char *xpath = tmp.c_str();
454 if ((name = sr_xpath_next_node((char *)xpath, &state)) == NULL) {
455 DEBUG("Empty XPATH, xpath: %s", xpath);
460 auto pathEl = path.add_elem();
461 pathEl->set_name(name);
463 const char *key_name;
464 while ((key_name = sr_xpath_next_key_name(NULL, &state)) != NULL) {
465 std::string key(key_name);
466 const char *key_value = sr_xpath_next_key_value(NULL, &state);
467 (*pathEl->mutable_key())[key] = key_value;
469 } while ((name = sr_xpath_next_node(NULL, &state)) != NULL);
471 sr_xpath_recover(&state);
475 uint64_t gNMIServer::getTimeNanosec()
477 using namespace std::chrono;
479 std::uint64_t tm = high_resolution_clock::now().time_since_epoch() /