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