]> git.proxmox.com Git - mirror_frr.git/blob - lib/northbound_cli.c
lib, tests: major rework in the operational-data callbacks
[mirror_frr.git] / lib / northbound_cli.c
1 /*
2 * Copyright (C) 2018 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 "libfrr.h"
23 #include "version.h"
24 #include "log.h"
25 #include "lib_errors.h"
26 #include "command.h"
27 #include "termtable.h"
28 #include "db.h"
29 #include "yang_translator.h"
30 #include "northbound.h"
31 #include "northbound_cli.h"
32 #include "northbound_db.h"
33 #ifndef VTYSH_EXTRACT_PL
34 #include "lib/northbound_cli_clippy.c"
35 #endif
36
37 int debug_northbound;
38 struct nb_config *vty_shared_candidate_config;
39
40 static void vty_show_libyang_errors(struct vty *vty, struct ly_ctx *ly_ctx)
41 {
42 struct ly_err_item *ei;
43 const char *path;
44
45 ei = ly_err_first(ly_ctx);
46 if (!ei)
47 return;
48
49 for (; ei; ei = ei->next)
50 vty_out(vty, "%s\n", ei->msg);
51
52 path = ly_errpath(ly_ctx);
53 if (path)
54 vty_out(vty, "YANG path: %s\n", path);
55
56 ly_err_clean(ly_ctx, NULL);
57 }
58
59 int nb_cli_cfg_change(struct vty *vty, char *xpath_base,
60 struct cli_config_change changes[], size_t size)
61 {
62 struct nb_config *candidate_transitory;
63 bool error = false;
64 int ret;
65
66 VTY_CHECK_XPATH;
67
68 /*
69 * Create a copy of the candidate configuration. For consistency, we
70 * need to ensure that either all changes made by the command are
71 * accepted or none are.
72 */
73 candidate_transitory = nb_config_dup(vty->candidate_config);
74
75 /* Edit candidate configuration. */
76 for (size_t i = 0; i < size; i++) {
77 struct cli_config_change *change = &changes[i];
78 struct nb_node *nb_node;
79 char xpath[XPATH_MAXLEN];
80 struct yang_data *data;
81
82 /* Handle relative XPaths. */
83 memset(xpath, 0, sizeof(xpath));
84 if (vty->xpath_index > 0
85 && ((xpath_base && xpath_base[0] == '.')
86 || change->xpath[0] == '.'))
87 strlcpy(xpath, VTY_CURR_XPATH, sizeof(xpath));
88 if (xpath_base) {
89 if (xpath_base[0] == '.')
90 xpath_base++;
91 strlcat(xpath, xpath_base, sizeof(xpath));
92 }
93 if (change->xpath[0] == '.')
94 strlcat(xpath, change->xpath + 1, sizeof(xpath));
95 else
96 strlcpy(xpath, change->xpath, sizeof(xpath));
97
98 nb_node = nb_node_find(xpath);
99 if (!nb_node) {
100 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
101 "%s: unknown data path: %s", __func__, xpath);
102 error = true;
103 break;
104 }
105
106 /* If the value is not set, get the default if it exists. */
107 if (change->value == NULL)
108 change->value = yang_snode_get_default(nb_node->snode);
109 data = yang_data_new(xpath, change->value);
110
111 /*
112 * Ignore "not found" errors when editing the candidate
113 * configuration.
114 */
115 ret = nb_candidate_edit(candidate_transitory, nb_node,
116 change->operation, xpath, NULL, data);
117 yang_data_free(data);
118 if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) {
119 flog_warn(
120 EC_LIB_NB_CANDIDATE_EDIT_ERROR,
121 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
122 __func__, nb_operation_name(change->operation),
123 xpath);
124 error = true;
125 break;
126 }
127 }
128
129 if (error) {
130 nb_config_free(candidate_transitory);
131
132 switch (frr_get_cli_mode()) {
133 case FRR_CLI_CLASSIC:
134 vty_out(vty, "%% Configuration failed.\n\n");
135 break;
136 case FRR_CLI_TRANSACTIONAL:
137 vty_out(vty,
138 "%% Failed to edit candidate configuration.\n\n");
139 break;
140 }
141 vty_show_libyang_errors(vty, ly_native_ctx);
142
143 return CMD_WARNING_CONFIG_FAILED;
144 }
145
146 nb_config_replace(vty->candidate_config, candidate_transitory, false);
147
148 /* Do an implicit "commit" when using the classic CLI mode. */
149 if (frr_get_cli_mode() == FRR_CLI_CLASSIC) {
150 ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI,
151 false, NULL, NULL);
152 if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
153 vty_out(vty, "%% Configuration failed: %s.\n\n",
154 nb_err_name(ret));
155 vty_out(vty,
156 "Please check the logs for more details.\n");
157
158 /* Regenerate candidate for consistency. */
159 nb_config_replace(vty->candidate_config, running_config,
160 true);
161 return CMD_WARNING_CONFIG_FAILED;
162 }
163 }
164
165 return CMD_SUCCESS;
166 }
167
168 int nb_cli_rpc(const char *xpath, struct list *input, struct list *output)
169 {
170 struct nb_node *nb_node;
171 int ret;
172
173 nb_node = nb_node_find(xpath);
174 if (!nb_node) {
175 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
176 "%s: unknown data path: %s", __func__, xpath);
177 return CMD_WARNING;
178 }
179
180 ret = nb_node->cbs.rpc(xpath, input, output);
181 switch (ret) {
182 case NB_OK:
183 return CMD_SUCCESS;
184 default:
185 return CMD_WARNING;
186 }
187 }
188
189 static int nb_cli_commit(struct vty *vty, bool force, char *comment)
190 {
191 uint32_t transaction_id;
192 int ret;
193
194 if (vty_exclusive_lock != NULL && vty_exclusive_lock != vty) {
195 vty_out(vty, "%% Configuration is locked by another VTY.\n\n");
196 return CMD_WARNING;
197 }
198
199 if (!force && nb_candidate_needs_update(vty->candidate_config)) {
200 vty_out(vty,
201 "%% Candidate configuration needs to be updated before commit.\n\n");
202 vty_out(vty,
203 "Use the \"update\" command or \"commit force\".\n");
204 return CMD_WARNING;
205 }
206
207 ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, true,
208 comment, &transaction_id);
209
210 /* Map northbound return code to CLI return code. */
211 switch (ret) {
212 case NB_OK:
213 nb_config_replace(vty->candidate_config_base, running_config,
214 true);
215 vty_out(vty,
216 "%% Configuration committed successfully (Transaction ID #%u).\n\n",
217 transaction_id);
218 return CMD_SUCCESS;
219 case NB_ERR_NO_CHANGES:
220 vty_out(vty, "%% No configuration changes to commit.\n\n");
221 return CMD_SUCCESS;
222 default:
223 vty_out(vty,
224 "%% Failed to commit candidate configuration: %s.\n\n",
225 nb_err_name(ret));
226 vty_out(vty, "Please check the logs for more details.\n");
227 return CMD_WARNING;
228 }
229 }
230
231 static int nb_cli_candidate_load_file(struct vty *vty,
232 enum nb_cfg_format format,
233 struct yang_translator *translator,
234 const char *path, bool replace)
235 {
236 struct nb_config *loaded_config = NULL;
237 struct lyd_node *dnode;
238 struct ly_ctx *ly_ctx;
239 int ly_format;
240
241 switch (format) {
242 case NB_CFG_FMT_CMDS:
243 loaded_config = nb_config_new(NULL);
244 if (!vty_read_config(loaded_config, path, config_default)) {
245 vty_out(vty, "%% Failed to load configuration.\n\n");
246 vty_out(vty,
247 "Please check the logs for more details.\n");
248 nb_config_free(loaded_config);
249 return CMD_WARNING;
250 }
251 break;
252 case NB_CFG_FMT_JSON:
253 case NB_CFG_FMT_XML:
254 ly_format = (format == NB_CFG_FMT_JSON) ? LYD_JSON : LYD_XML;
255
256 ly_ctx = translator ? translator->ly_ctx : ly_native_ctx;
257 dnode = lyd_parse_path(ly_ctx, path, ly_format, LYD_OPT_EDIT);
258 if (!dnode) {
259 flog_warn(EC_LIB_LIBYANG, "%s: lyd_parse_path() failed",
260 __func__);
261 vty_out(vty, "%% Failed to load configuration:\n\n");
262 vty_show_libyang_errors(vty, ly_ctx);
263 return CMD_WARNING;
264 }
265 if (translator
266 && yang_translate_dnode(translator,
267 YANG_TRANSLATE_TO_NATIVE, &dnode)
268 != YANG_TRANSLATE_SUCCESS) {
269 vty_out(vty, "%% Failed to translate configuration\n");
270 yang_dnode_free(dnode);
271 return CMD_WARNING;
272 }
273 loaded_config = nb_config_new(dnode);
274 break;
275 }
276
277 if (replace)
278 nb_config_replace(vty->candidate_config, loaded_config, false);
279 else if (nb_config_merge(vty->candidate_config, loaded_config, false)
280 != NB_OK) {
281 vty_out(vty,
282 "%% Failed to merge the loaded configuration:\n\n");
283 vty_show_libyang_errors(vty, ly_native_ctx);
284 return CMD_WARNING;
285 }
286
287 return CMD_SUCCESS;
288 }
289
290 static int nb_cli_candidate_load_transaction(struct vty *vty,
291 uint32_t transaction_id,
292 bool replace)
293 {
294 struct nb_config *loaded_config;
295
296 loaded_config = nb_db_transaction_load(transaction_id);
297 if (!loaded_config) {
298 vty_out(vty, "%% Transaction %u does not exist.\n\n",
299 transaction_id);
300 return CMD_WARNING;
301 }
302
303 if (replace)
304 nb_config_replace(vty->candidate_config, loaded_config, false);
305 else if (nb_config_merge(vty->candidate_config, loaded_config, false)
306 != NB_OK) {
307 vty_out(vty,
308 "%% Failed to merge the loaded configuration:\n\n");
309 vty_show_libyang_errors(vty, ly_native_ctx);
310 return CMD_WARNING;
311 }
312
313 return CMD_SUCCESS;
314 }
315
316 void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *root,
317 bool with_defaults)
318 {
319 struct lyd_node *next, *child;
320
321 LY_TREE_DFS_BEGIN (root, next, child) {
322 struct nb_node *nb_node;
323
324 nb_node = child->schema->priv;
325 if (!nb_node->cbs.cli_show)
326 goto next;
327
328 /* Skip default values. */
329 if (!with_defaults && yang_dnode_is_default_recursive(child))
330 goto next;
331
332 (*nb_node->cbs.cli_show)(vty, child, with_defaults);
333 next:
334 LY_TREE_DFS_END(root, next, child);
335 }
336 }
337
338 static void nb_cli_show_config_cmds(struct vty *vty, struct nb_config *config,
339 bool with_defaults)
340 {
341 struct lyd_node *root;
342
343 vty_out(vty, "Configuration:\n");
344 vty_out(vty, "!\n");
345 vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
346 vty_out(vty, "frr defaults %s\n", DFLT_NAME);
347
348 LY_TREE_FOR (config->dnode, root)
349 nb_cli_show_dnode_cmds(vty, root, with_defaults);
350
351 vty_out(vty, "!\n");
352 vty_out(vty, "end\n");
353 }
354
355 static int nb_cli_show_config_libyang(struct vty *vty, LYD_FORMAT format,
356 struct nb_config *config,
357 struct yang_translator *translator,
358 bool with_defaults)
359 {
360 struct lyd_node *dnode;
361 char *strp;
362 int options = 0;
363
364 dnode = yang_dnode_dup(config->dnode);
365 if (translator
366 && yang_translate_dnode(translator, YANG_TRANSLATE_FROM_NATIVE,
367 &dnode)
368 != YANG_TRANSLATE_SUCCESS) {
369 vty_out(vty, "%% Failed to translate configuration\n");
370 yang_dnode_free(dnode);
371 return CMD_WARNING;
372 }
373
374 SET_FLAG(options, LYP_FORMAT | LYP_WITHSIBLINGS);
375 if (with_defaults)
376 SET_FLAG(options, LYP_WD_ALL);
377 else
378 SET_FLAG(options, LYP_WD_TRIM);
379
380 if (lyd_print_mem(&strp, dnode, format, options) == 0 && strp) {
381 vty_out(vty, "%s", strp);
382 free(strp);
383 }
384
385 yang_dnode_free(dnode);
386
387 return CMD_SUCCESS;
388 }
389
390 static int nb_cli_show_config(struct vty *vty, struct nb_config *config,
391 enum nb_cfg_format format,
392 struct yang_translator *translator,
393 bool with_defaults)
394 {
395 switch (format) {
396 case NB_CFG_FMT_CMDS:
397 nb_cli_show_config_cmds(vty, config, with_defaults);
398 break;
399 case NB_CFG_FMT_JSON:
400 return nb_cli_show_config_libyang(vty, LYD_JSON, config,
401 translator, with_defaults);
402 case NB_CFG_FMT_XML:
403 return nb_cli_show_config_libyang(vty, LYD_XML, config,
404 translator, with_defaults);
405 }
406
407 return CMD_SUCCESS;
408 }
409
410 static int nb_write_config(struct nb_config *config, enum nb_cfg_format format,
411 struct yang_translator *translator, char *path,
412 size_t pathlen)
413 {
414 int fd;
415 struct vty *file_vty;
416 int ret = 0;
417
418 snprintf(path, pathlen, "/tmp/frr.tmp.XXXXXXXX");
419 fd = mkstemp(path);
420 if (fd < 0) {
421 flog_warn(EC_LIB_SYSTEM_CALL, "%s: mkstemp() failed: %s",
422 __func__, safe_strerror(errno));
423 return -1;
424 }
425
426 /* Make vty for configuration file. */
427 file_vty = vty_new();
428 file_vty->wfd = fd;
429 file_vty->type = VTY_FILE;
430 if (config)
431 ret = nb_cli_show_config(file_vty, config, format, translator,
432 false);
433 vty_close(file_vty);
434
435 return ret;
436 }
437
438 static int nb_cli_show_config_compare(struct vty *vty,
439 struct nb_config *config1,
440 struct nb_config *config2,
441 enum nb_cfg_format format,
442 struct yang_translator *translator)
443 {
444 char config1_path[256];
445 char config2_path[256];
446 char command[BUFSIZ];
447 FILE *fp;
448 char line[1024];
449 int lineno = 0;
450
451 if (nb_write_config(config1, format, translator, config1_path,
452 sizeof(config1_path))
453 != 0) {
454 vty_out(vty, "%% Failed to process configurations.\n\n");
455 return CMD_WARNING;
456 }
457 if (nb_write_config(config2, format, translator, config2_path,
458 sizeof(config2_path))
459 != 0) {
460 vty_out(vty, "%% Failed to process configurations.\n\n");
461 unlink(config1_path);
462 return CMD_WARNING;
463 }
464
465 snprintf(command, sizeof(command), "diff -u %s %s", config1_path,
466 config2_path);
467 fp = popen(command, "r");
468 if (!fp) {
469 vty_out(vty, "%% Failed to generate configuration diff.\n\n");
470 unlink(config1_path);
471 unlink(config2_path);
472 return CMD_WARNING;
473 }
474 /* Print diff line by line. */
475 while (fgets(line, sizeof(line), fp) != NULL) {
476 if (lineno++ < 2)
477 continue;
478 vty_out(vty, "%s", line);
479 }
480 pclose(fp);
481
482 unlink(config1_path);
483 unlink(config2_path);
484
485 return CMD_SUCCESS;
486 }
487
488 /* Configure exclusively from this terminal. */
489 DEFUN (config_exclusive,
490 config_exclusive_cmd,
491 "configure exclusive",
492 "Configuration from vty interface\n"
493 "Configure exclusively from this terminal\n")
494 {
495 if (vty_config_exclusive_lock(vty))
496 vty->node = CONFIG_NODE;
497 else {
498 vty_out(vty, "VTY configuration is locked by other VTY\n");
499 return CMD_WARNING_CONFIG_FAILED;
500 }
501
502 vty->private_config = true;
503 vty->candidate_config = nb_config_dup(running_config);
504 vty->candidate_config_base = nb_config_dup(running_config);
505 vty_out(vty,
506 "Warning: uncommitted changes will be discarded on exit.\n\n");
507
508 return CMD_SUCCESS;
509 }
510
511 /* Configure using a private candidate configuration. */
512 DEFUN (config_private,
513 config_private_cmd,
514 "configure private",
515 "Configuration from vty interface\n"
516 "Configure using a private candidate configuration\n")
517 {
518 if (vty_config_lock(vty))
519 vty->node = CONFIG_NODE;
520 else {
521 vty_out(vty, "VTY configuration is locked by other VTY\n");
522 return CMD_WARNING_CONFIG_FAILED;
523 }
524
525 vty->private_config = true;
526 vty->candidate_config = nb_config_dup(running_config);
527 vty->candidate_config_base = nb_config_dup(running_config);
528 vty_out(vty,
529 "Warning: uncommitted changes will be discarded on exit.\n\n");
530
531 return CMD_SUCCESS;
532 }
533
534 DEFPY (config_commit,
535 config_commit_cmd,
536 "commit [force$force]",
537 "Commit changes into the running configuration\n"
538 "Force commit even if the candidate is outdated\n")
539 {
540 return nb_cli_commit(vty, !!force, NULL);
541 }
542
543 DEFPY (config_commit_comment,
544 config_commit_comment_cmd,
545 "commit [force$force] comment LINE...",
546 "Commit changes into the running configuration\n"
547 "Force commit even if the candidate is outdated\n"
548 "Assign a comment to this commit\n"
549 "Comment for this commit (Max 80 characters)\n")
550 {
551 char *comment;
552 int idx = 0;
553 int ret;
554
555 argv_find(argv, argc, "LINE", &idx);
556 comment = argv_concat(argv, argc, idx);
557 ret = nb_cli_commit(vty, !!force, comment);
558 XFREE(MTYPE_TMP, comment);
559
560 return ret;
561 }
562
563 DEFPY (config_commit_check,
564 config_commit_check_cmd,
565 "commit check",
566 "Commit changes into the running configuration\n"
567 "Check if the configuration changes are valid\n")
568 {
569 int ret;
570
571 ret = nb_candidate_validate(vty->candidate_config);
572 if (ret != NB_OK) {
573 vty_out(vty,
574 "%% Failed to validate candidate configuration.\n\n");
575 vty_show_libyang_errors(vty, ly_native_ctx);
576 return CMD_WARNING;
577 }
578
579 vty_out(vty, "%% Candidate configuration validated successfully.\n\n");
580
581 return CMD_SUCCESS;
582 }
583
584 DEFPY (config_update,
585 config_update_cmd,
586 "update",
587 "Update candidate configuration\n")
588 {
589 if (!nb_candidate_needs_update(vty->candidate_config)) {
590 vty_out(vty, "%% Update is not necessary.\n\n");
591 return CMD_SUCCESS;
592 }
593
594 if (nb_candidate_update(vty->candidate_config) != NB_OK) {
595 vty_out(vty,
596 "%% Failed to update the candidate configuration.\n\n");
597 vty_out(vty, "Please check the logs for more details.\n");
598 return CMD_WARNING;
599 }
600
601 nb_config_replace(vty->candidate_config_base, running_config, true);
602
603 vty_out(vty, "%% Candidate configuration updated successfully.\n\n");
604
605 return CMD_SUCCESS;
606 }
607
608 DEFPY (config_discard,
609 config_discard_cmd,
610 "discard",
611 "Discard changes in the candidate configuration\n")
612 {
613 nb_config_replace(vty->candidate_config, vty->candidate_config_base,
614 true);
615
616 return CMD_SUCCESS;
617 }
618
619 DEFPY (config_load,
620 config_load_cmd,
621 "configuration load\
622 <\
623 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
624 |transaction (1-4294967296)$tid\
625 >\
626 [replace$replace]",
627 "Configuration related settings\n"
628 "Load configuration into candidate\n"
629 "Load configuration file into candidate\n"
630 "Load configuration file in JSON format\n"
631 "Load configuration file in XML format\n"
632 "Translate configuration file\n"
633 "YANG module translator\n"
634 "Configuration file name (full path)\n"
635 "Load configuration from transaction into candidate\n"
636 "Transaction ID\n"
637 "Replace instead of merge\n")
638 {
639 if (filename) {
640 enum nb_cfg_format format;
641 struct yang_translator *translator = NULL;
642
643 if (json)
644 format = NB_CFG_FMT_JSON;
645 else if (xml)
646 format = NB_CFG_FMT_XML;
647 else
648 format = NB_CFG_FMT_CMDS;
649
650 if (translator_family) {
651 translator = yang_translator_find(translator_family);
652 if (!translator) {
653 vty_out(vty,
654 "%% Module translator \"%s\" not found\n",
655 translator_family);
656 return CMD_WARNING;
657 }
658 }
659
660 return nb_cli_candidate_load_file(vty, format, translator,
661 filename, !!replace);
662 }
663
664 return nb_cli_candidate_load_transaction(vty, tid, !!replace);
665 }
666
667 DEFPY (show_config_running,
668 show_config_running_cmd,
669 "show configuration running\
670 [<json$json|xml$xml> [translate WORD$translator_family]]\
671 [with-defaults$with_defaults]",
672 SHOW_STR
673 "Configuration information\n"
674 "Running configuration\n"
675 "Change output format to JSON\n"
676 "Change output format to XML\n"
677 "Translate output\n"
678 "YANG module translator\n"
679 "Show default values\n")
680
681 {
682 enum nb_cfg_format format;
683 struct yang_translator *translator = NULL;
684
685 if (json)
686 format = NB_CFG_FMT_JSON;
687 else if (xml)
688 format = NB_CFG_FMT_XML;
689 else
690 format = NB_CFG_FMT_CMDS;
691
692 if (translator_family) {
693 translator = yang_translator_find(translator_family);
694 if (!translator) {
695 vty_out(vty, "%% Module translator \"%s\" not found\n",
696 translator_family);
697 return CMD_WARNING;
698 }
699 }
700
701 nb_cli_show_config(vty, running_config, format, translator,
702 !!with_defaults);
703
704 return CMD_SUCCESS;
705 }
706
707 DEFPY (show_config_candidate,
708 show_config_candidate_cmd,
709 "show configuration candidate\
710 [<json$json|xml$xml> [translate WORD$translator_family]]\
711 [<\
712 with-defaults$with_defaults\
713 |changes$changes\
714 >]",
715 SHOW_STR
716 "Configuration information\n"
717 "Candidate configuration\n"
718 "Change output format to JSON\n"
719 "Change output format to XML\n"
720 "Translate output\n"
721 "YANG module translator\n"
722 "Show default values\n"
723 "Show changes applied in the candidate configuration\n")
724
725 {
726 enum nb_cfg_format format;
727 struct yang_translator *translator = NULL;
728
729 if (json)
730 format = NB_CFG_FMT_JSON;
731 else if (xml)
732 format = NB_CFG_FMT_XML;
733 else
734 format = NB_CFG_FMT_CMDS;
735
736 if (translator_family) {
737 translator = yang_translator_find(translator_family);
738 if (!translator) {
739 vty_out(vty, "%% Module translator \"%s\" not found\n",
740 translator_family);
741 return CMD_WARNING;
742 }
743 }
744
745 if (changes)
746 return nb_cli_show_config_compare(
747 vty, vty->candidate_config_base, vty->candidate_config,
748 format, translator);
749
750 nb_cli_show_config(vty, vty->candidate_config, format, translator,
751 !!with_defaults);
752
753 return CMD_SUCCESS;
754 }
755
756 DEFPY (show_config_compare,
757 show_config_compare_cmd,
758 "show configuration compare\
759 <\
760 candidate$c1_candidate\
761 |running$c1_running\
762 |transaction (1-4294967296)$c1_tid\
763 >\
764 <\
765 candidate$c2_candidate\
766 |running$c2_running\
767 |transaction (1-4294967296)$c2_tid\
768 >\
769 [<json$json|xml$xml> [translate WORD$translator_family]]",
770 SHOW_STR
771 "Configuration information\n"
772 "Compare two different configurations\n"
773 "Candidate configuration\n"
774 "Running configuration\n"
775 "Configuration transaction\n"
776 "Transaction ID\n"
777 "Candidate configuration\n"
778 "Running configuration\n"
779 "Configuration transaction\n"
780 "Transaction ID\n"
781 "Change output format to JSON\n"
782 "Change output format to XML\n"
783 "Translate output\n"
784 "YANG module translator\n")
785 {
786 enum nb_cfg_format format;
787 struct yang_translator *translator = NULL;
788 struct nb_config *config1, *config_transaction1 = NULL;
789 struct nb_config *config2, *config_transaction2 = NULL;
790 int ret = CMD_WARNING;
791
792 if (c1_candidate)
793 config1 = vty->candidate_config;
794 else if (c1_running)
795 config1 = running_config;
796 else {
797 config_transaction1 = nb_db_transaction_load(c1_tid);
798 if (!config_transaction1) {
799 vty_out(vty, "%% Transaction %u does not exist\n\n",
800 (unsigned int)c1_tid);
801 goto exit;
802 }
803 config1 = config_transaction1;
804 }
805
806 if (c2_candidate)
807 config2 = vty->candidate_config;
808 else if (c2_running)
809 config2 = running_config;
810 else {
811 config_transaction2 = nb_db_transaction_load(c2_tid);
812 if (!config_transaction2) {
813 vty_out(vty, "%% Transaction %u does not exist\n\n",
814 (unsigned int)c2_tid);
815 goto exit;
816 }
817 config2 = config_transaction2;
818 }
819
820 if (json)
821 format = NB_CFG_FMT_JSON;
822 else if (xml)
823 format = NB_CFG_FMT_XML;
824 else
825 format = NB_CFG_FMT_CMDS;
826
827 if (translator_family) {
828 translator = yang_translator_find(translator_family);
829 if (!translator) {
830 vty_out(vty, "%% Module translator \"%s\" not found\n",
831 translator_family);
832 goto exit;
833 }
834 }
835
836 ret = nb_cli_show_config_compare(vty, config1, config2, format,
837 translator);
838 exit:
839 if (config_transaction1)
840 nb_config_free(config_transaction1);
841 if (config_transaction2)
842 nb_config_free(config_transaction2);
843
844 return ret;
845 }
846
847 /*
848 * Stripped down version of the "show configuration compare" command.
849 * The "candidate" option is not present so the command can be installed in
850 * the enable node.
851 */
852 ALIAS (show_config_compare,
853 show_config_compare_without_candidate_cmd,
854 "show configuration compare\
855 <\
856 running$c1_running\
857 |transaction (1-4294967296)$c1_tid\
858 >\
859 <\
860 running$c2_running\
861 |transaction (1-4294967296)$c2_tid\
862 >\
863 [<json$json|xml$xml> [translate WORD$translator_family]]",
864 SHOW_STR
865 "Configuration information\n"
866 "Compare two different configurations\n"
867 "Running configuration\n"
868 "Configuration transaction\n"
869 "Transaction ID\n"
870 "Running configuration\n"
871 "Configuration transaction\n"
872 "Transaction ID\n"
873 "Change output format to JSON\n"
874 "Change output format to XML\n"
875 "Translate output\n"
876 "YANG module translator\n")
877
878 DEFPY (clear_config_transactions,
879 clear_config_transactions_cmd,
880 "clear configuration transactions oldest (1-100)$n",
881 CLEAR_STR
882 "Configuration activity\n"
883 "Delete transactions from the transactions log\n"
884 "Delete oldest <n> transactions\n"
885 "Number of transactions to delete\n")
886 {
887 #ifdef HAVE_CONFIG_ROLLBACKS
888 if (nb_db_clear_transactions(n) != NB_OK) {
889 vty_out(vty, "%% Failed to delete transactions.\n\n");
890 return CMD_WARNING;
891 }
892 #else
893 vty_out(vty,
894 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
895 #endif /* HAVE_CONFIG_ROLLBACKS */
896
897 return CMD_SUCCESS;
898 }
899
900 DEFPY (config_database_max_transactions,
901 config_database_max_transactions_cmd,
902 "configuration database max-transactions (1-100)$max",
903 "Configuration related settings\n"
904 "Configuration database\n"
905 "Set the maximum number of transactions to store\n"
906 "Number of transactions\n")
907 {
908 #ifdef HAVE_CONFIG_ROLLBACKS
909 if (nb_db_set_max_transactions(max) != NB_OK) {
910 vty_out(vty,
911 "%% Failed to update the maximum number of transactions.\n\n");
912 return CMD_WARNING;
913 }
914 vty_out(vty,
915 "%% Maximum number of transactions updated successfully.\n\n");
916 #else
917 vty_out(vty,
918 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
919 #endif /* HAVE_CONFIG_ROLLBACKS */
920
921 return CMD_SUCCESS;
922 }
923
924 DEFPY (yang_module_translator_load,
925 yang_module_translator_load_cmd,
926 "yang module-translator load FILENAME$filename",
927 "YANG related settings\n"
928 "YANG module translator\n"
929 "Load YANG module translator\n"
930 "File name (full path)\n")
931 {
932 struct yang_translator *translator;
933
934 translator = yang_translator_load(filename);
935 if (!translator) {
936 vty_out(vty, "%% Failed to load \"%s\"\n\n", filename);
937 vty_out(vty, "Please check the logs for more details.\n");
938 return CMD_WARNING;
939 }
940
941 vty_out(vty, "%% Module translator \"%s\" loaded successfully.\n\n",
942 translator->family);
943
944 return CMD_SUCCESS;
945 }
946
947 DEFPY (yang_module_translator_unload_family,
948 yang_module_translator_unload_cmd,
949 "yang module-translator unload WORD$translator_family",
950 "YANG related settings\n"
951 "YANG module translator\n"
952 "Unload YANG module translator\n"
953 "Name of the module translator\n")
954 {
955 struct yang_translator *translator;
956
957 translator = yang_translator_find(translator_family);
958 if (!translator) {
959 vty_out(vty, "%% Module translator \"%s\" not found\n",
960 translator_family);
961 return CMD_WARNING;
962 }
963
964 yang_translator_unload(translator);
965
966 return CMD_SUCCESS;
967 }
968
969 #ifdef HAVE_CONFIG_ROLLBACKS
970 static void nb_cli_show_transactions_cb(void *arg, int transaction_id,
971 const char *client_name,
972 const char *date, const char *comment)
973 {
974 struct ttable *tt = arg;
975
976 ttable_add_row(tt, "%d|%s|%s|%s", transaction_id, client_name, date,
977 comment);
978 }
979
980 static int nb_cli_show_transactions(struct vty *vty)
981 {
982 struct ttable *tt;
983
984 /* Prepare table. */
985 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
986 ttable_add_row(tt, "Transaction ID|Client|Date|Comment");
987 tt->style.cell.rpad = 2;
988 tt->style.corner = '+';
989 ttable_restyle(tt);
990 ttable_rowseps(tt, 0, BOTTOM, true, '-');
991
992 /* Fetch transactions from the northbound database. */
993 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb, tt)
994 != NB_OK) {
995 vty_out(vty,
996 "%% Failed to fetch configuration transactions.\n");
997 return CMD_WARNING;
998 }
999
1000 /* Dump the generated table. */
1001 if (tt->nrows > 1) {
1002 char *table;
1003
1004 table = ttable_dump(tt, "\n");
1005 vty_out(vty, "%s\n", table);
1006 XFREE(MTYPE_TMP, table);
1007 } else
1008 vty_out(vty, "No configuration transactions to display.\n\n");
1009
1010 ttable_del(tt);
1011
1012 return CMD_SUCCESS;
1013 }
1014 #endif /* HAVE_CONFIG_ROLLBACKS */
1015
1016 DEFPY (show_config_transaction,
1017 show_config_transaction_cmd,
1018 "show configuration transaction\
1019 [\
1020 (1-4294967296)$transaction_id\
1021 [<json$json|xml$xml> [translate WORD$translator_family]]\
1022 [<\
1023 with-defaults$with_defaults\
1024 |changes$changes\
1025 >]\
1026 ]",
1027 SHOW_STR
1028 "Configuration information\n"
1029 "Configuration transaction\n"
1030 "Transaction ID\n"
1031 "Change output format to JSON\n"
1032 "Change output format to XML\n"
1033 "Translate output\n"
1034 "YANG module translator\n"
1035 "Show default values\n"
1036 "Show changes compared to the previous transaction\n")
1037 {
1038 #ifdef HAVE_CONFIG_ROLLBACKS
1039 if (transaction_id) {
1040 struct nb_config *config;
1041 enum nb_cfg_format format;
1042 struct yang_translator *translator = NULL;
1043
1044 if (json)
1045 format = NB_CFG_FMT_JSON;
1046 else if (xml)
1047 format = NB_CFG_FMT_XML;
1048 else
1049 format = NB_CFG_FMT_CMDS;
1050
1051 if (translator_family) {
1052 translator = yang_translator_find(translator_family);
1053 if (!translator) {
1054 vty_out(vty,
1055 "%% Module translator \"%s\" not found\n",
1056 translator_family);
1057 return CMD_WARNING;
1058 }
1059 }
1060
1061 config = nb_db_transaction_load(transaction_id);
1062 if (!config) {
1063 vty_out(vty, "%% Transaction %u does not exist.\n\n",
1064 (unsigned int)transaction_id);
1065 return CMD_WARNING;
1066 }
1067
1068 if (changes) {
1069 struct nb_config *prev_config;
1070 int ret;
1071
1072 /* NOTE: this can be NULL. */
1073 prev_config =
1074 nb_db_transaction_load(transaction_id - 1);
1075
1076 ret = nb_cli_show_config_compare(
1077 vty, prev_config, config, format, translator);
1078 if (prev_config)
1079 nb_config_free(prev_config);
1080 nb_config_free(config);
1081
1082 return ret;
1083 }
1084
1085 nb_cli_show_config(vty, config, format, translator,
1086 !!with_defaults);
1087 nb_config_free(config);
1088
1089 return CMD_SUCCESS;
1090 }
1091
1092 return nb_cli_show_transactions(vty);
1093 #else
1094 vty_out(vty,
1095 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1096 return CMD_WARNING;
1097 #endif /* HAVE_CONFIG_ROLLBACKS */
1098 }
1099
1100 static int nb_cli_oper_data_cb(const struct lys_node *snode,
1101 struct yang_translator *translator,
1102 struct yang_data *data, void *arg)
1103 {
1104 struct lyd_node *dnode = arg;
1105 struct ly_ctx *ly_ctx;
1106
1107 if (translator) {
1108 int ret;
1109
1110 ret = yang_translate_xpath(translator,
1111 YANG_TRANSLATE_FROM_NATIVE,
1112 data->xpath, sizeof(data->xpath));
1113 switch (ret) {
1114 case YANG_TRANSLATE_SUCCESS:
1115 break;
1116 case YANG_TRANSLATE_NOTFOUND:
1117 goto exit;
1118 case YANG_TRANSLATE_FAILURE:
1119 goto error;
1120 }
1121
1122 ly_ctx = translator->ly_ctx;
1123 } else
1124 ly_ctx = ly_native_ctx;
1125
1126 ly_errno = 0;
1127 dnode = lyd_new_path(dnode, ly_ctx, data->xpath, (void *)data->value, 0,
1128 LYD_PATH_OPT_UPDATE);
1129 if (!dnode && ly_errno) {
1130 flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
1131 __func__);
1132 goto error;
1133 }
1134
1135 exit:
1136 yang_data_free(data);
1137 return NB_OK;
1138
1139 error:
1140 yang_data_free(data);
1141 return NB_ERR;
1142 }
1143
1144 DEFPY (show_yang_operational_data,
1145 show_yang_operational_data_cmd,
1146 "show yang operational-data XPATH$xpath\
1147 [{\
1148 format <json$json|xml$xml>\
1149 |translate WORD$translator_family\
1150 }]",
1151 SHOW_STR
1152 "YANG information\n"
1153 "Show YANG operational data\n"
1154 "XPath expression specifying the YANG data path\n"
1155 "Set the output format\n"
1156 "JavaScript Object Notation\n"
1157 "Extensible Markup Language\n"
1158 "Translate operational data\n"
1159 "YANG module translator\n")
1160 {
1161 LYD_FORMAT format;
1162 struct yang_translator *translator = NULL;
1163 struct ly_ctx *ly_ctx;
1164 struct lyd_node *dnode;
1165 char *strp;
1166
1167 if (xml)
1168 format = LYD_XML;
1169 else
1170 format = LYD_JSON;
1171
1172 if (translator_family) {
1173 translator = yang_translator_find(translator_family);
1174 if (!translator) {
1175 vty_out(vty, "%% Module translator \"%s\" not found\n",
1176 translator_family);
1177 return CMD_WARNING;
1178 }
1179
1180 ly_ctx = translator->ly_ctx;
1181 } else
1182 ly_ctx = ly_native_ctx;
1183
1184 /* Obtain data. */
1185 dnode = yang_dnode_new(ly_ctx, false);
1186 if (nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb,
1187 dnode)
1188 != NB_OK) {
1189 vty_out(vty, "%% Failed to fetch operational data.\n");
1190 yang_dnode_free(dnode);
1191 return CMD_WARNING;
1192 }
1193 lyd_validate(&dnode, LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB, ly_ctx);
1194
1195 /* Display the data. */
1196 if (lyd_print_mem(&strp, dnode, format,
1197 LYP_FORMAT | LYP_WITHSIBLINGS | LYP_WD_ALL)
1198 != 0
1199 || !strp) {
1200 vty_out(vty, "%% Failed to display operational data.\n");
1201 yang_dnode_free(dnode);
1202 return CMD_WARNING;
1203 }
1204 vty_out(vty, "%s", strp);
1205 free(strp);
1206 yang_dnode_free(dnode);
1207
1208 return CMD_SUCCESS;
1209 }
1210
1211 DEFPY (show_yang_module,
1212 show_yang_module_cmd,
1213 "show yang module [module-translator WORD$translator_family]",
1214 SHOW_STR
1215 "YANG information\n"
1216 "Show loaded modules\n"
1217 "YANG module translator\n"
1218 "YANG module translator\n")
1219 {
1220 struct ly_ctx *ly_ctx;
1221 struct yang_translator *translator = NULL;
1222 const struct lys_module *module;
1223 struct ttable *tt;
1224 uint32_t idx = 0;
1225
1226 if (translator_family) {
1227 translator = yang_translator_find(translator_family);
1228 if (!translator) {
1229 vty_out(vty, "%% Module translator \"%s\" not found\n",
1230 translator_family);
1231 return CMD_WARNING;
1232 }
1233 ly_ctx = translator->ly_ctx;
1234 } else
1235 ly_ctx = ly_native_ctx;
1236
1237 /* Prepare table. */
1238 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
1239 ttable_add_row(tt, "Module|Version|Revision|Flags|Namespace");
1240 tt->style.cell.rpad = 2;
1241 tt->style.corner = '+';
1242 ttable_restyle(tt);
1243 ttable_rowseps(tt, 0, BOTTOM, true, '-');
1244
1245 while ((module = ly_ctx_get_module_iter(ly_ctx, &idx))) {
1246 char flags[8];
1247
1248 snprintf(flags, sizeof(flags), "%c%c",
1249 module->implemented ? 'I' : ' ',
1250 (module->deviated == 1) ? 'D' : ' ');
1251
1252 ttable_add_row(tt, "%s|%s|%s|%s|%s", module->name,
1253 (module->version == 2) ? "1.1" : "1.0",
1254 (module->rev_size > 0) ? module->rev[0].date
1255 : "-",
1256 flags, module->ns);
1257 }
1258
1259 /* Dump the generated table. */
1260 if (tt->nrows > 1) {
1261 char *table;
1262
1263 vty_out(vty, " Flags: I - Implemented, D - Deviated\n\n");
1264
1265 table = ttable_dump(tt, "\n");
1266 vty_out(vty, "%s\n", table);
1267 XFREE(MTYPE_TMP, table);
1268 } else
1269 vty_out(vty, "No YANG modules to display.\n\n");
1270
1271 ttable_del(tt);
1272
1273 return CMD_SUCCESS;
1274 }
1275
1276 DEFPY (show_yang_module_detail,
1277 show_yang_module_detail_cmd,
1278 "show yang module\
1279 [module-translator WORD$translator_family]\
1280 WORD$module_name <summary|tree$tree|yang$yang|yin$yin>",
1281 SHOW_STR
1282 "YANG information\n"
1283 "Show loaded modules\n"
1284 "YANG module translator\n"
1285 "YANG module translator\n"
1286 "Module name\n"
1287 "Display summary information about the module\n"
1288 "Display module in the tree (RFC 8340) format\n"
1289 "Display module in the YANG format\n"
1290 "Display module in the YIN format\n")
1291 {
1292 struct ly_ctx *ly_ctx;
1293 struct yang_translator *translator = NULL;
1294 const struct lys_module *module;
1295 LYS_OUTFORMAT format;
1296 char *strp;
1297
1298 if (translator_family) {
1299 translator = yang_translator_find(translator_family);
1300 if (!translator) {
1301 vty_out(vty, "%% Module translator \"%s\" not found\n",
1302 translator_family);
1303 return CMD_WARNING;
1304 }
1305 ly_ctx = translator->ly_ctx;
1306 } else
1307 ly_ctx = ly_native_ctx;
1308
1309 module = ly_ctx_get_module(ly_ctx, module_name, NULL, 0);
1310 if (!module) {
1311 vty_out(vty, "%% Module \"%s\" not found\n", module_name);
1312 return CMD_WARNING;
1313 }
1314
1315 if (yang)
1316 format = LYS_OUT_YANG;
1317 else if (yin)
1318 format = LYS_OUT_YIN;
1319 else if (tree)
1320 format = LYS_OUT_TREE;
1321 else
1322 format = LYS_OUT_INFO;
1323
1324 if (lys_print_mem(&strp, module, format, NULL, 0, 0) == 0) {
1325 vty_out(vty, "%s\n", strp);
1326 free(strp);
1327 } else {
1328 /* Unexpected. */
1329 vty_out(vty, "%% Error generating module information\n");
1330 return CMD_WARNING;
1331 }
1332
1333 return CMD_SUCCESS;
1334 }
1335
1336 DEFPY (show_yang_module_translator,
1337 show_yang_module_translator_cmd,
1338 "show yang module-translator",
1339 SHOW_STR
1340 "YANG information\n"
1341 "Show loaded YANG module translators\n")
1342 {
1343 struct yang_translator *translator;
1344 struct ttable *tt;
1345
1346 /* Prepare table. */
1347 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
1348 ttable_add_row(tt, "Family|Module|Deviations|Coverage (%%)");
1349 tt->style.cell.rpad = 2;
1350 tt->style.corner = '+';
1351 ttable_restyle(tt);
1352 ttable_rowseps(tt, 0, BOTTOM, true, '-');
1353
1354 RB_FOREACH (translator, yang_translators, &yang_translators) {
1355 struct yang_tmodule *tmodule;
1356 struct listnode *ln;
1357
1358 for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
1359 ttable_add_row(tt, "%s|%s|%s|%.2f", translator->family,
1360 tmodule->module->name,
1361 tmodule->deviations->name,
1362 tmodule->coverage);
1363 }
1364 }
1365
1366 /* Dump the generated table. */
1367 if (tt->nrows > 1) {
1368 char *table;
1369
1370 table = ttable_dump(tt, "\n");
1371 vty_out(vty, "%s\n", table);
1372 XFREE(MTYPE_TMP, table);
1373 } else
1374 vty_out(vty, "No YANG module translators to display.\n\n");
1375
1376 ttable_del(tt);
1377
1378 return CMD_SUCCESS;
1379 }
1380
1381 #ifdef HAVE_CONFIG_ROLLBACKS
1382 static int nb_cli_rollback_configuration(struct vty *vty,
1383 uint32_t transaction_id)
1384 {
1385 struct nb_config *candidate;
1386 char comment[80];
1387 int ret;
1388
1389 candidate = nb_db_transaction_load(transaction_id);
1390 if (!candidate) {
1391 vty_out(vty, "%% Transaction %u does not exist.\n\n",
1392 transaction_id);
1393 return CMD_WARNING;
1394 }
1395
1396 snprintf(comment, sizeof(comment), "Rollback to transaction %u",
1397 transaction_id);
1398
1399 ret = nb_candidate_commit(candidate, NB_CLIENT_CLI, true, comment,
1400 NULL);
1401 nb_config_free(candidate);
1402 switch (ret) {
1403 case NB_OK:
1404 vty_out(vty,
1405 "%% Configuration was successfully rolled back.\n\n");
1406 return CMD_SUCCESS;
1407 case NB_ERR_NO_CHANGES:
1408 vty_out(vty,
1409 "%% Aborting - no configuration changes detected.\n\n");
1410 return CMD_WARNING;
1411 default:
1412 vty_out(vty, "%% Rollback failed.\n\n");
1413 vty_out(vty, "Please check the logs for more details.\n");
1414 return CMD_WARNING;
1415 }
1416 }
1417 #endif /* HAVE_CONFIG_ROLLBACKS */
1418
1419 DEFPY (rollback_config,
1420 rollback_config_cmd,
1421 "rollback configuration (1-4294967296)$transaction_id",
1422 "Rollback to a previous state\n"
1423 "Running configuration\n"
1424 "Transaction ID\n")
1425 {
1426 #ifdef HAVE_CONFIG_ROLLBACKS
1427 return nb_cli_rollback_configuration(vty, transaction_id);
1428 #else
1429 vty_out(vty,
1430 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1431 return CMD_SUCCESS;
1432 #endif /* HAVE_CONFIG_ROLLBACKS */
1433 }
1434
1435 /* Debug CLI commands. */
1436 DEFUN (debug_nb,
1437 debug_nb_cmd,
1438 "debug northbound",
1439 DEBUG_STR
1440 "Northbound Debugging\n")
1441 {
1442 debug_northbound = 1;
1443
1444 return CMD_SUCCESS;
1445 }
1446
1447 DEFUN (no_debug_nb,
1448 no_debug_nb_cmd,
1449 "no debug northbound",
1450 NO_STR DEBUG_STR
1451 "Northbound Debugging\n")
1452 {
1453 debug_northbound = 0;
1454
1455 return CMD_SUCCESS;
1456 }
1457
1458 static int nb_debug_config_write(struct vty *vty)
1459 {
1460 if (debug_northbound)
1461 vty_out(vty, "debug northbound\n");
1462
1463 return 1;
1464 }
1465
1466 static struct cmd_node nb_debug_node = {NORTHBOUND_DEBUG_NODE, "", 1};
1467
1468 void nb_cli_install_default(int node)
1469 {
1470 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL)
1471 return;
1472
1473 install_element(node, &config_commit_cmd);
1474 install_element(node, &config_commit_comment_cmd);
1475 install_element(node, &config_commit_check_cmd);
1476 install_element(node, &config_update_cmd);
1477 install_element(node, &config_discard_cmd);
1478 install_element(node, &show_config_running_cmd);
1479 install_element(node, &show_config_candidate_cmd);
1480 install_element(node, &show_config_compare_cmd);
1481 install_element(node, &show_config_transaction_cmd);
1482 }
1483
1484 /* YANG module autocomplete. */
1485 static void yang_module_autocomplete(vector comps, struct cmd_token *token)
1486 {
1487 const struct lys_module *module;
1488 struct yang_translator *module_tr;
1489 uint32_t idx;
1490
1491 idx = 0;
1492 while ((module = ly_ctx_get_module_iter(ly_native_ctx, &idx)))
1493 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, module->name));
1494
1495 RB_FOREACH (module_tr, yang_translators, &yang_translators) {
1496 idx = 0;
1497 while ((module = ly_ctx_get_module_iter(module_tr->ly_ctx,
1498 &idx)))
1499 vector_set(comps,
1500 XSTRDUP(MTYPE_COMPLETION, module->name));
1501 }
1502 }
1503
1504 /* YANG module translator autocomplete. */
1505 static void yang_translator_autocomplete(vector comps, struct cmd_token *token)
1506 {
1507 struct yang_translator *module_tr;
1508
1509 RB_FOREACH (module_tr, yang_translators, &yang_translators)
1510 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, module_tr->family));
1511 }
1512
1513 static const struct cmd_variable_handler yang_var_handlers[] = {
1514 {.varname = "module_name", .completions = yang_module_autocomplete},
1515 {.varname = "translator_family",
1516 .completions = yang_translator_autocomplete},
1517 {.completions = NULL}};
1518
1519 void nb_cli_init(void)
1520 {
1521 /* Initialize the shared candidate configuration. */
1522 vty_shared_candidate_config = nb_config_new(NULL);
1523
1524 /* Install debug commands */
1525 install_node(&nb_debug_node, nb_debug_config_write);
1526 install_element(ENABLE_NODE, &debug_nb_cmd);
1527 install_element(ENABLE_NODE, &no_debug_nb_cmd);
1528 install_element(CONFIG_NODE, &debug_nb_cmd);
1529 install_element(CONFIG_NODE, &no_debug_nb_cmd);
1530
1531 /* Install commands specific to the transaction-base mode. */
1532 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) {
1533 install_element(ENABLE_NODE, &config_exclusive_cmd);
1534 install_element(ENABLE_NODE, &config_private_cmd);
1535 install_element(ENABLE_NODE, &show_config_running_cmd);
1536 install_element(ENABLE_NODE,
1537 &show_config_compare_without_candidate_cmd);
1538 install_element(ENABLE_NODE, &show_config_transaction_cmd);
1539 install_element(ENABLE_NODE, &rollback_config_cmd);
1540 install_element(ENABLE_NODE, &clear_config_transactions_cmd);
1541
1542 install_element(CONFIG_NODE, &config_load_cmd);
1543 install_element(CONFIG_NODE,
1544 &config_database_max_transactions_cmd);
1545 }
1546
1547 /* Other commands. */
1548 install_element(CONFIG_NODE, &yang_module_translator_load_cmd);
1549 install_element(CONFIG_NODE, &yang_module_translator_unload_cmd);
1550 install_element(ENABLE_NODE, &show_yang_operational_data_cmd);
1551 install_element(ENABLE_NODE, &show_yang_module_cmd);
1552 install_element(ENABLE_NODE, &show_yang_module_detail_cmd);
1553 install_element(ENABLE_NODE, &show_yang_module_translator_cmd);
1554 cmd_variable_handler_register(yang_var_handlers);
1555 }
1556
1557 void nb_cli_terminate(void)
1558 {
1559 nb_config_free(vty_shared_candidate_config);
1560 }