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