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