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