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