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