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