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