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