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