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