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