]> git.proxmox.com Git - mirror_frr.git/blob - lib/northbound_grpc.cpp
Merge pull request #4272 from opensourcerouting/isis-prefix-sid-fix
[mirror_frr.git] / lib / northbound_grpc.cpp
1 //
2 // Copyright (C) 2019 NetDEF, Inc.
3 // Renato Westphal
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 2 of the License, or (at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 // more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; see the file COPYING; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 //
19
20 #include <zebra.h>
21
22 #include "log.h"
23 #include "libfrr.h"
24 #include "version.h"
25 #include "command.h"
26 #include "lib_errors.h"
27 #include "northbound.h"
28 #include "northbound_db.h"
29
30 #include <iostream>
31 #include <sstream>
32 #include <memory>
33 #include <string>
34
35 #include <grpcpp/grpcpp.h>
36 #include "grpc/frr-northbound.grpc.pb.h"
37
38 #define GRPC_DEFAULT_PORT 50051
39
40 /*
41 * NOTE: we can't use the FRR debugging infrastructure here since it uses
42 * atomics and C++ has a different atomics API. Enable gRPC debugging
43 * unconditionally until we figure out a way to solve this problem.
44 */
45 static bool nb_dbg_client_grpc = 1;
46
47 static pthread_t grpc_pthread;
48
49 class NorthboundImpl final : public frr::Northbound::Service
50 {
51 public:
52 NorthboundImpl(void)
53 {
54 _nextCandidateId = 0;
55 }
56
57 ~NorthboundImpl(void)
58 {
59 // Delete candidates.
60 for (auto it = _candidates.begin(); it != _candidates.end();
61 it++)
62 delete_candidate(&it->second);
63 }
64
65 grpc::Status
66 GetCapabilities(grpc::ServerContext *context,
67 frr::GetCapabilitiesRequest const *request,
68 frr::GetCapabilitiesResponse *response) override
69 {
70 if (nb_dbg_client_grpc)
71 zlog_debug("received RPC GetCapabilities()");
72
73 // Response: string frr_version = 1;
74 response->set_frr_version(FRR_VERSION);
75
76 // Response: bool rollback_support = 2;
77 #ifdef HAVE_CONFIG_ROLLBACKS
78 response->set_rollback_support(true);
79 #else
80 response->set_rollback_support(false);
81 #endif
82
83 // Response: repeated ModuleData supported_modules = 3;
84 struct yang_module *module;
85 RB_FOREACH (module, yang_modules, &yang_modules) {
86 auto m = response->add_supported_modules();
87
88 m->set_name(module->name);
89 if (module->info->rev_size)
90 m->set_revision(module->info->rev[0].date);
91 m->set_organization(module->info->org);
92 }
93
94 // Response: repeated Encoding supported_encodings = 4;
95 response->add_supported_encodings(frr::JSON);
96 response->add_supported_encodings(frr::XML);
97
98 return grpc::Status::OK;
99 }
100
101 grpc::Status Get(grpc::ServerContext *context,
102 frr::GetRequest const *request,
103 grpc::ServerWriter<frr::GetResponse> *writer) override
104 {
105 // Request: DataType type = 1;
106 int type = request->type();
107 // Request: Encoding encoding = 2;
108 frr::Encoding encoding = request->encoding();
109 // Request: bool with_defaults = 3;
110 bool with_defaults = request->with_defaults();
111
112 if (nb_dbg_client_grpc)
113 zlog_debug(
114 "received RPC Get(type: %u, encoding: %u, with_defaults: %u)",
115 type, encoding, with_defaults);
116
117 // Request: repeated string path = 4;
118 auto paths = request->path();
119 for (const std::string &path : paths) {
120 frr::GetResponse response;
121 grpc::Status status;
122
123 // Response: int64 timestamp = 1;
124 response.set_timestamp(time(NULL));
125
126 // Response: DataTree data = 2;
127 auto *data = response.mutable_data();
128 data->set_encoding(request->encoding());
129 status = get_path(data, path, type,
130 encoding2lyd_format(encoding),
131 with_defaults);
132
133 // Something went wrong...
134 if (!status.ok())
135 return status;
136
137 writer->Write(response);
138 }
139
140 if (nb_dbg_client_grpc)
141 zlog_debug("received RPC Get() end");
142
143 return grpc::Status::OK;
144 }
145
146 grpc::Status
147 CreateCandidate(grpc::ServerContext *context,
148 frr::CreateCandidateRequest const *request,
149 frr::CreateCandidateResponse *response) override
150 {
151 if (nb_dbg_client_grpc)
152 zlog_debug("received RPC CreateCandidate()");
153
154 struct candidate *candidate = create_candidate();
155 if (!candidate)
156 return grpc::Status(
157 grpc::StatusCode::RESOURCE_EXHAUSTED,
158 "Can't create candidate configuration");
159
160 // Response: uint32 candidate_id = 1;
161 response->set_candidate_id(candidate->id);
162
163 return grpc::Status::OK;
164 }
165
166 grpc::Status
167 DeleteCandidate(grpc::ServerContext *context,
168 frr::DeleteCandidateRequest const *request,
169 frr::DeleteCandidateResponse *response) override
170 {
171 // Request: uint32 candidate_id = 1;
172 uint32_t candidate_id = request->candidate_id();
173
174 if (nb_dbg_client_grpc)
175 zlog_debug(
176 "received RPC DeleteCandidate(candidate_id: %u)",
177 candidate_id);
178
179 struct candidate *candidate = get_candidate(candidate_id);
180 if (!candidate)
181 return grpc::Status(
182 grpc::StatusCode::NOT_FOUND,
183 "candidate configuration not found");
184
185 delete_candidate(candidate);
186
187 return grpc::Status::OK;
188 }
189
190 grpc::Status
191 UpdateCandidate(grpc::ServerContext *context,
192 frr::UpdateCandidateRequest const *request,
193 frr::UpdateCandidateResponse *response) override
194 {
195 // Request: uint32 candidate_id = 1;
196 uint32_t candidate_id = request->candidate_id();
197
198 if (nb_dbg_client_grpc)
199 zlog_debug(
200 "received RPC UpdateCandidate(candidate_id: %u)",
201 candidate_id);
202
203 struct candidate *candidate = get_candidate(candidate_id);
204 if (!candidate)
205 return grpc::Status(
206 grpc::StatusCode::NOT_FOUND,
207 "candidate configuration not found");
208
209 if (candidate->transaction)
210 return grpc::Status(
211 grpc::StatusCode::FAILED_PRECONDITION,
212 "candidate is in the middle of a transaction");
213
214 if (nb_candidate_update(candidate->config) != NB_OK)
215 return grpc::Status(
216 grpc::StatusCode::INTERNAL,
217 "failed to update candidate configuration");
218
219 return grpc::Status::OK;
220 }
221
222 grpc::Status
223 EditCandidate(grpc::ServerContext *context,
224 frr::EditCandidateRequest const *request,
225 frr::EditCandidateResponse *response) override
226 {
227 // Request: uint32 candidate_id = 1;
228 uint32_t candidate_id = request->candidate_id();
229
230 if (nb_dbg_client_grpc)
231 zlog_debug(
232 "received RPC EditCandidate(candidate_id: %u)",
233 candidate_id);
234
235 struct candidate *candidate = get_candidate(candidate_id);
236 if (!candidate)
237 return grpc::Status(
238 grpc::StatusCode::NOT_FOUND,
239 "candidate configuration not found");
240
241 // Create a copy of the candidate. For consistency, we need to
242 // ensure that either all changes are accepted or none are (in
243 // the event of an error).
244 struct nb_config *candidate_tmp =
245 nb_config_dup(candidate->config);
246
247 auto pvs = request->update();
248 for (const frr::PathValue &pv : pvs) {
249 if (yang_dnode_edit(candidate_tmp->dnode, pv.path(),
250 pv.value())
251 != 0) {
252 nb_config_free(candidate_tmp);
253 return grpc::Status(
254 grpc::StatusCode::INVALID_ARGUMENT,
255 "Failed to update \"" + pv.path()
256 + "\"");
257 }
258 }
259
260 pvs = request->delete_();
261 for (const frr::PathValue &pv : pvs) {
262 if (yang_dnode_delete(candidate_tmp->dnode, pv.path())
263 != 0) {
264 nb_config_free(candidate_tmp);
265 return grpc::Status(
266 grpc::StatusCode::INVALID_ARGUMENT,
267 "Failed to remove \"" + pv.path()
268 + "\"");
269 }
270 }
271
272 // No errors, accept all changes.
273 nb_config_replace(candidate->config, candidate_tmp, false);
274
275 return grpc::Status::OK;
276 }
277
278 grpc::Status
279 LoadToCandidate(grpc::ServerContext *context,
280 frr::LoadToCandidateRequest const *request,
281 frr::LoadToCandidateResponse *response) override
282 {
283 // Request: uint32 candidate_id = 1;
284 uint32_t candidate_id = request->candidate_id();
285 // Request: LoadType type = 2;
286 int load_type = request->type();
287 // Request: DataTree config = 3;
288 auto config = request->config();
289
290 if (nb_dbg_client_grpc)
291 zlog_debug(
292 "received RPC LoadToCandidate(candidate_id: %u)",
293 candidate_id);
294
295 struct candidate *candidate = get_candidate(candidate_id);
296 if (!candidate)
297 return grpc::Status(
298 grpc::StatusCode::NOT_FOUND,
299 "candidate configuration not found");
300
301 struct lyd_node *dnode = dnode_from_data_tree(&config, true);
302 if (!dnode)
303 return grpc::Status(
304 grpc::StatusCode::INTERNAL,
305 "Failed to parse the configuration");
306
307 struct nb_config *loaded_config = nb_config_new(dnode);
308
309 if (load_type == frr::LoadToCandidateRequest::REPLACE)
310 nb_config_replace(candidate->config, loaded_config,
311 false);
312 else if (nb_config_merge(candidate->config, loaded_config,
313 false)
314 != NB_OK)
315 return grpc::Status(
316 grpc::StatusCode::INTERNAL,
317 "Failed to merge the loaded configuration");
318
319 return grpc::Status::OK;
320 }
321
322 grpc::Status Commit(grpc::ServerContext *context,
323 frr::CommitRequest const *request,
324 frr::CommitResponse *response) override
325 {
326 // Request: uint32 candidate_id = 1;
327 uint32_t candidate_id = request->candidate_id();
328 // Request: Phase phase = 2;
329 int phase = request->phase();
330 // Request: string comment = 3;
331 const std::string comment = request->comment();
332
333 if (nb_dbg_client_grpc)
334 zlog_debug("received RPC Commit(candidate_id: %u)",
335 candidate_id);
336
337 // Find candidate configuration.
338 struct candidate *candidate = get_candidate(candidate_id);
339 if (!candidate)
340 return grpc::Status(
341 grpc::StatusCode::NOT_FOUND,
342 "candidate configuration not found");
343
344 int ret = NB_OK;
345 uint32_t transaction_id = 0;
346
347 // Check for misuse of the two-phase commit protocol.
348 switch (phase) {
349 case frr::CommitRequest::PREPARE:
350 case frr::CommitRequest::ALL:
351 if (candidate->transaction)
352 return grpc::Status(
353 grpc::StatusCode::FAILED_PRECONDITION,
354 "pending transaction in progress");
355 break;
356 case frr::CommitRequest::ABORT:
357 case frr::CommitRequest::APPLY:
358 if (!candidate->transaction)
359 return grpc::Status(
360 grpc::StatusCode::FAILED_PRECONDITION,
361 "no transaction in progress");
362 break;
363 default:
364 break;
365 }
366
367 // Execute the user request.
368 switch (phase) {
369 case frr::CommitRequest::VALIDATE:
370 ret = nb_candidate_validate(candidate->config);
371 break;
372 case frr::CommitRequest::PREPARE:
373 ret = nb_candidate_commit_prepare(
374 candidate->config, NB_CLIENT_GRPC, NULL,
375 comment.c_str(), &candidate->transaction);
376 break;
377 case frr::CommitRequest::ABORT:
378 nb_candidate_commit_abort(candidate->transaction);
379 break;
380 case frr::CommitRequest::APPLY:
381 nb_candidate_commit_apply(candidate->transaction, true,
382 &transaction_id);
383 break;
384 case frr::CommitRequest::ALL:
385 ret = nb_candidate_commit(
386 candidate->config, NB_CLIENT_GRPC, NULL, true,
387 comment.c_str(), &transaction_id);
388 break;
389 }
390
391 // Map northbound error codes to gRPC error codes.
392 switch (ret) {
393 case NB_ERR_NO_CHANGES:
394 return grpc::Status(
395 grpc::StatusCode::ABORTED,
396 "No configuration changes detected");
397 case NB_ERR_LOCKED:
398 return grpc::Status(
399 grpc::StatusCode::UNAVAILABLE,
400 "There's already a transaction in progress");
401 case NB_ERR_VALIDATION:
402 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
403 "Validation error");
404 case NB_ERR_RESOURCE:
405 return grpc::Status(
406 grpc::StatusCode::RESOURCE_EXHAUSTED,
407 "Failed do allocate resources");
408 case NB_ERR:
409 return grpc::Status(grpc::StatusCode::INTERNAL,
410 "Internal error");
411 default:
412 break;
413 }
414
415 // Response: uint32 transaction_id = 1;
416 if (transaction_id)
417 response->set_transaction_id(transaction_id);
418
419 return grpc::Status::OK;
420 }
421
422 grpc::Status
423 ListTransactions(grpc::ServerContext *context,
424 frr::ListTransactionsRequest const *request,
425 grpc::ServerWriter<frr::ListTransactionsResponse>
426 *writer) override
427 {
428 if (nb_dbg_client_grpc)
429 zlog_debug("received RPC ListTransactions()");
430
431 nb_db_transactions_iterate(list_transactions_cb, writer);
432
433 return grpc::Status::OK;
434 }
435
436 grpc::Status
437 GetTransaction(grpc::ServerContext *context,
438 frr::GetTransactionRequest const *request,
439 frr::GetTransactionResponse *response) override
440 {
441 struct nb_config *nb_config;
442
443 // Request: uint32 transaction_id = 1;
444 uint32_t transaction_id = request->transaction_id();
445 // Request: Encoding encoding = 2;
446 frr::Encoding encoding = request->encoding();
447 // Request: bool with_defaults = 3;
448 bool with_defaults = request->with_defaults();
449
450 if (nb_dbg_client_grpc)
451 zlog_debug(
452 "received RPC GetTransaction(transaction_id: %u, encoding: %u)",
453 transaction_id, encoding);
454
455 // Load configuration from the transactions database.
456 nb_config = nb_db_transaction_load(transaction_id);
457 if (!nb_config)
458 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
459 "Transaction not found");
460
461 // Response: DataTree config = 1;
462 auto config = response->mutable_config();
463 config->set_encoding(encoding);
464
465 // Dump data using the requested format.
466 if (data_tree_from_dnode(config, nb_config->dnode,
467 encoding2lyd_format(encoding),
468 with_defaults)
469 != 0) {
470 nb_config_free(nb_config);
471 return grpc::Status(grpc::StatusCode::INTERNAL,
472 "Failed to dump data");
473 }
474
475 nb_config_free(nb_config);
476
477 return grpc::Status::OK;
478 }
479
480 grpc::Status LockConfig(grpc::ServerContext *context,
481 frr::LockConfigRequest const *request,
482 frr::LockConfigResponse *response) override
483 {
484 if (nb_dbg_client_grpc)
485 zlog_debug("received RPC LockConfig()");
486
487 if (nb_running_lock(NB_CLIENT_GRPC, NULL))
488 return grpc::Status(
489 grpc::StatusCode::FAILED_PRECONDITION,
490 "running configuration is locked already");
491
492 return grpc::Status::OK;
493 }
494
495 grpc::Status UnlockConfig(grpc::ServerContext *context,
496 frr::UnlockConfigRequest const *request,
497 frr::UnlockConfigResponse *response) override
498 {
499 if (nb_dbg_client_grpc)
500 zlog_debug("received RPC UnlockConfig()");
501
502 if (nb_running_unlock(NB_CLIENT_GRPC, NULL))
503 return grpc::Status(
504 grpc::StatusCode::FAILED_PRECONDITION,
505 "failed to unlock the running configuration");
506
507 return grpc::Status::OK;
508 }
509
510 grpc::Status Execute(grpc::ServerContext *context,
511 frr::ExecuteRequest const *request,
512 frr::ExecuteResponse *response) override
513 {
514 struct nb_node *nb_node;
515 struct list *input_list;
516 struct list *output_list;
517 struct listnode *node;
518 struct yang_data *data;
519 const char *xpath;
520
521 // Request: string path = 1;
522 xpath = request->path().c_str();
523
524 if (nb_dbg_client_grpc)
525 zlog_debug("received RPC Execute(path: \"%s\")", xpath);
526
527 if (request->path().empty())
528 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
529 "Data path is empty");
530
531 nb_node = nb_node_find(xpath);
532 if (!nb_node)
533 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
534 "Unknown data path");
535
536 input_list = yang_data_list_new();
537 output_list = yang_data_list_new();
538
539 // Read input parameters.
540 auto input = request->input();
541 for (const frr::PathValue &pv : input) {
542 // Request: repeated PathValue input = 2;
543 data = yang_data_new(pv.path().c_str(),
544 pv.value().c_str());
545 listnode_add(input_list, data);
546 }
547
548 // Execute callback registered for this XPath.
549 if (nb_node->cbs.rpc(xpath, input_list, output_list) != NB_OK) {
550 flog_warn(EC_LIB_NB_CB_RPC,
551 "%s: rpc callback failed: %s", __func__,
552 xpath);
553 list_delete(&input_list);
554 list_delete(&output_list);
555 return grpc::Status(grpc::StatusCode::INTERNAL,
556 "RPC failed");
557 }
558
559 // Process output parameters.
560 for (ALL_LIST_ELEMENTS_RO(output_list, node, data)) {
561 // Response: repeated PathValue output = 1;
562 frr::PathValue *pv = response->add_output();
563 pv->set_path(data->xpath);
564 pv->set_value(data->value);
565 }
566
567 // Release memory.
568 list_delete(&input_list);
569 list_delete(&output_list);
570
571 return grpc::Status::OK;
572 }
573
574 private:
575 struct candidate {
576 uint32_t id;
577 struct nb_config *config;
578 struct nb_transaction *transaction;
579 };
580 std::map<uint32_t, struct candidate> _candidates;
581 uint32_t _nextCandidateId;
582
583 static int yang_dnode_edit(struct lyd_node *dnode,
584 const std::string &path,
585 const std::string &value)
586 {
587 ly_errno = LY_SUCCESS;
588 dnode = lyd_new_path(dnode, ly_native_ctx, path.c_str(),
589 (void *)value.c_str(),
590 (LYD_ANYDATA_VALUETYPE)0,
591 LYD_PATH_OPT_UPDATE);
592 if (!dnode && ly_errno != LY_SUCCESS) {
593 flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
594 __func__);
595 return -1;
596 }
597
598 return 0;
599 }
600
601 static int yang_dnode_delete(struct lyd_node *dnode,
602 const std::string &path)
603 {
604 dnode = yang_dnode_get(dnode, path.c_str());
605 if (!dnode)
606 return -1;
607
608 lyd_free(dnode);
609
610 return 0;
611 }
612
613 static LYD_FORMAT encoding2lyd_format(enum frr::Encoding encoding)
614 {
615 switch (encoding) {
616 case frr::JSON:
617 return LYD_JSON;
618 case frr::XML:
619 return LYD_XML;
620 }
621 }
622
623 static int get_oper_data_cb(const struct lys_node *snode,
624 struct yang_translator *translator,
625 struct yang_data *data, void *arg)
626 {
627 struct lyd_node *dnode = static_cast<struct lyd_node *>(arg);
628 int ret = yang_dnode_edit(dnode, data->xpath, data->value);
629 yang_data_free(data);
630
631 return (ret == 0) ? NB_OK : NB_ERR;
632 }
633
634 static void list_transactions_cb(void *arg, int transaction_id,
635 const char *client_name,
636 const char *date, const char *comment)
637 {
638 grpc::ServerWriter<frr::ListTransactionsResponse> *writer =
639 static_cast<grpc::ServerWriter<
640 frr::ListTransactionsResponse> *>(arg);
641 frr::ListTransactionsResponse response;
642
643 // Response: uint32 id = 1;
644 response.set_id(transaction_id);
645
646 // Response: string client = 2;
647 response.set_client(client_name);
648
649 // Response: string date = 3;
650 response.set_date(date);
651
652 // Response: string comment = 4;
653 response.set_comment(comment);
654
655 writer->Write(response);
656 }
657
658 static int data_tree_from_dnode(frr::DataTree *dt,
659 const struct lyd_node *dnode,
660 LYD_FORMAT lyd_format,
661 bool with_defaults)
662 {
663 char *strp;
664 int options = 0;
665
666 SET_FLAG(options, LYP_FORMAT | LYP_WITHSIBLINGS);
667 if (with_defaults)
668 SET_FLAG(options, LYP_WD_ALL);
669 else
670 SET_FLAG(options, LYP_WD_TRIM);
671
672 if (lyd_print_mem(&strp, dnode, lyd_format, options) == 0) {
673 if (strp) {
674 dt->set_data(strp);
675 free(strp);
676 }
677 return 0;
678 }
679
680 return -1;
681 }
682
683 static struct lyd_node *dnode_from_data_tree(const frr::DataTree *dt,
684 bool config_only)
685 {
686 struct lyd_node *dnode;
687 int options;
688
689 if (config_only)
690 options = LYD_OPT_CONFIG;
691 else
692 options = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB;
693
694 dnode = lyd_parse_mem(ly_native_ctx, dt->data().c_str(),
695 encoding2lyd_format(dt->encoding()),
696 options);
697
698 return dnode;
699 }
700
701 static struct lyd_node *get_dnode_config(const std::string &path)
702 {
703 struct lyd_node *dnode;
704
705 pthread_rwlock_rdlock(&running_config->lock);
706 {
707 dnode = yang_dnode_get(running_config->dnode,
708 path.empty() ? NULL
709 : path.c_str());
710 if (dnode)
711 dnode = yang_dnode_dup(dnode);
712 }
713 pthread_rwlock_unlock(&running_config->lock);
714
715 return dnode;
716 }
717
718 static struct lyd_node *get_dnode_state(const std::string &path)
719 {
720 struct lyd_node *dnode;
721
722 dnode = yang_dnode_new(ly_native_ctx, false);
723 if (nb_oper_data_iterate(path.c_str(), NULL, 0,
724 get_oper_data_cb, dnode)
725 != NB_OK) {
726 yang_dnode_free(dnode);
727 return NULL;
728 }
729
730 return dnode;
731 }
732
733 static grpc::Status get_path(frr::DataTree *dt, const std::string &path,
734 int type, LYD_FORMAT lyd_format,
735 bool with_defaults)
736 {
737 struct lyd_node *dnode_config = NULL;
738 struct lyd_node *dnode_state = NULL;
739 struct lyd_node *dnode_final;
740
741 // Configuration data.
742 if (type == frr::GetRequest_DataType_ALL
743 || type == frr::GetRequest_DataType_CONFIG) {
744 dnode_config = get_dnode_config(path);
745 if (!dnode_config)
746 return grpc::Status(
747 grpc::StatusCode::INVALID_ARGUMENT,
748 "Data path not found");
749 }
750
751 // Operational data.
752 if (type == frr::GetRequest_DataType_ALL
753 || type == frr::GetRequest_DataType_STATE) {
754 dnode_state = get_dnode_state(path);
755 if (!dnode_state) {
756 if (dnode_config)
757 yang_dnode_free(dnode_config);
758 return grpc::Status(
759 grpc::StatusCode::INVALID_ARGUMENT,
760 "Failed to fetch operational data");
761 }
762 }
763
764 switch (type) {
765 case frr::GetRequest_DataType_ALL:
766 //
767 // Combine configuration and state data into a single
768 // dnode.
769 //
770 if (lyd_merge(dnode_state, dnode_config,
771 LYD_OPT_EXPLICIT)
772 != 0) {
773 yang_dnode_free(dnode_state);
774 yang_dnode_free(dnode_config);
775 return grpc::Status(
776 grpc::StatusCode::INTERNAL,
777 "Failed to merge configuration and state data");
778 }
779
780 dnode_final = dnode_state;
781 break;
782 case frr::GetRequest_DataType_CONFIG:
783 dnode_final = dnode_config;
784 break;
785 case frr::GetRequest_DataType_STATE:
786 dnode_final = dnode_state;
787 break;
788 }
789
790 // Validate data to create implicit default nodes if necessary.
791 int validate_opts = 0;
792 if (type == frr::GetRequest_DataType_CONFIG)
793 validate_opts = LYD_OPT_CONFIG;
794 else
795 validate_opts = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB;
796 lyd_validate(&dnode_final, validate_opts, ly_native_ctx);
797
798 // Dump data using the requested format.
799 int ret = data_tree_from_dnode(dt, dnode_final, lyd_format,
800 with_defaults);
801 yang_dnode_free(dnode_final);
802 if (ret != 0)
803 return grpc::Status(grpc::StatusCode::INTERNAL,
804 "Failed to dump data");
805
806 return grpc::Status::OK;
807 }
808
809 struct candidate *create_candidate(void)
810 {
811 uint32_t candidate_id = ++_nextCandidateId;
812
813 // Check for overflow.
814 // TODO: implement an algorithm for unique reusable IDs.
815 if (candidate_id == 0)
816 return NULL;
817
818 struct candidate *candidate = &_candidates[candidate_id];
819 candidate->id = candidate_id;
820 pthread_rwlock_rdlock(&running_config->lock);
821 {
822 candidate->config = nb_config_dup(running_config);
823 }
824 pthread_rwlock_unlock(&running_config->lock);
825 candidate->transaction = NULL;
826
827 return candidate;
828 }
829
830 void delete_candidate(struct candidate *candidate)
831 {
832 _candidates.erase(candidate->id);
833 nb_config_free(candidate->config);
834 if (candidate->transaction)
835 nb_candidate_commit_abort(candidate->transaction);
836 }
837
838 struct candidate *get_candidate(uint32_t candidate_id)
839 {
840 struct candidate *candidate;
841
842 if (_candidates.count(candidate_id) == 0)
843 return NULL;
844
845 return &_candidates[candidate_id];
846 }
847 };
848
849 static void *grpc_pthread_start(void *arg)
850 {
851 unsigned long *port = static_cast<unsigned long *>(arg);
852 NorthboundImpl service;
853 std::stringstream server_address;
854
855 server_address << "0.0.0.0:" << *port;
856
857 grpc::ServerBuilder builder;
858 builder.AddListeningPort(server_address.str(),
859 grpc::InsecureServerCredentials());
860 builder.RegisterService(&service);
861
862 std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
863
864 zlog_notice("gRPC server listening on %s",
865 server_address.str().c_str());
866
867 server->Wait();
868
869 return NULL;
870 }
871
872 static int frr_grpc_init(unsigned long *port)
873 {
874 /* Create a pthread for gRPC since it runs its own event loop. */
875 if (pthread_create(&grpc_pthread, NULL, grpc_pthread_start, port)) {
876 flog_err(EC_LIB_SYSTEM_CALL, "%s: error creating pthread: %s",
877 __func__, safe_strerror(errno));
878 return -1;
879 }
880 pthread_detach(grpc_pthread);
881
882 return 0;
883 }
884
885 static int frr_grpc_finish(void)
886 {
887 // TODO: cancel the gRPC pthreads gracefully.
888
889 return 0;
890 }
891
892 static int frr_grpc_module_late_init(struct thread_master *tm)
893 {
894 static unsigned long port = GRPC_DEFAULT_PORT;
895 const char *args = THIS_MODULE->load_args;
896
897 // Parse port number.
898 if (args) {
899 try {
900 port = std::stoul(args);
901 if (port < 1024)
902 throw std::invalid_argument(
903 "can't use privileged port");
904 if (port > UINT16_MAX)
905 throw std::invalid_argument(
906 "port number is too big");
907 } catch (std::exception &e) {
908 flog_err(EC_LIB_GRPC_INIT,
909 "%s: failed to parse port number: %s",
910 __func__, e.what());
911 goto error;
912 }
913 }
914
915 if (frr_grpc_init(&port) < 0)
916 goto error;
917
918 hook_register(frr_fini, frr_grpc_finish);
919
920 return 0;
921
922 error:
923 flog_err(EC_LIB_GRPC_INIT, "failed to initialize the gRPC module");
924 return -1;
925 }
926
927 static int frr_grpc_module_init(void)
928 {
929 hook_register(frr_late_init, frr_grpc_module_late_init);
930
931 return 0;
932 }
933
934 FRR_MODULE_SETUP(.name = "frr_grpc", .version = FRR_VERSION,
935 .description = "FRR gRPC northbound module",
936 .init = frr_grpc_module_init, )