2 * Copyright (C) 2018 NetDEF, Inc.
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)
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
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
23 #include "lib/version.h"
26 #include "lib_errors.h"
28 #include "termtable.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"
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"};
44 struct nb_config
*vty_shared_candidate_config
;
45 static struct thread_master
*master
;
47 static void vty_show_nb_errors(struct vty
*vty
, int error
, const char *errmsg
)
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
);
54 static int nb_cli_classic_commit(struct vty
*vty
)
56 struct nb_context context
= {};
57 char errmsg
[BUFSIZ
] = {0};
60 context
.client
= NB_CLIENT_CLI
;
62 ret
= nb_candidate_commit(&context
, vty
->candidate_config
, true, NULL
,
63 NULL
, errmsg
, sizeof(errmsg
));
66 /* Successful commit. Print warnings (if any). */
67 if (strlen(errmsg
) > 0)
68 vty_out(vty
, "%s\n", errmsg
);
70 case NB_ERR_NO_CHANGES
:
73 vty_out(vty
, "%% Configuration failed.\n\n");
74 vty_show_nb_errors(vty
, ret
, errmsg
);
75 if (vty
->pending_commit
)
77 "The following commands were dynamically grouped into the same transaction and rejected:\n%s",
78 vty
->pending_cmds_buf
);
80 /* Regenerate candidate for consistency. */
81 nb_config_replace(vty
->candidate_config
, running_config
, true);
82 return CMD_WARNING_CONFIG_FAILED
;
88 static void nb_cli_pending_commit_clear(struct vty
*vty
)
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;
96 int nb_cli_pending_commit_check(struct vty
*vty
)
98 int ret
= CMD_SUCCESS
;
100 if (vty
->pending_commit
) {
101 ret
= nb_cli_classic_commit(vty
);
102 nb_cli_pending_commit_clear(vty
);
108 static int nb_cli_schedule_command(struct vty
*vty
)
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
);
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
);
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
);
127 /* Schedule the commit operation. */
128 vty
->pending_commit
= 1;
133 void nb_cli_enqueue_change(struct vty
*vty
, const char *xpath
,
134 enum nb_operation operation
, const char *value
)
136 struct vty_cfg_change
*change
;
138 if (vty
->num_cfg_changes
== VTY_MAXCFGCHANGES
) {
139 /* Not expected to happen. */
141 "%% Exceeded the maximum number of changes (%u) for a single command\n\n",
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
;
152 static int nb_cli_apply_changes_internal(struct vty
*vty
,
153 const char *xpath_base
,
158 if (xpath_base
== NULL
)
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
;
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
));
177 if (xpath_base
[0] == '.')
178 strlcat(xpath
, xpath_base
+ 1, sizeof(xpath
));
180 strlcat(xpath
, xpath_base
, sizeof(xpath
));
182 if (change
->xpath
[0] == '.')
183 strlcat(xpath
, change
->xpath
+ 1, sizeof(xpath
));
185 strlcpy(xpath
, change
->xpath
, sizeof(xpath
));
187 /* Find the northbound node associated to the data path. */
188 nb_node
= nb_node_find(xpath
);
190 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
191 "%s: unknown data path: %s", __func__
, xpath
);
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
);
202 * Ignore "not found" errors when editing the candidate
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
) {
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
),
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.
227 vty_out(vty
, "%% Failed to edit configuration.\n\n");
229 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
233 * Maybe do an implicit commit when using the classic CLI mode.
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
241 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
) {
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
);
254 int nb_cli_apply_changes(struct vty
*vty
, const char *xpath_base_fmt
, ...)
256 char xpath_base
[XPATH_MAXLEN
] = {};
258 /* Parse the base XPath format string. */
259 if (xpath_base_fmt
) {
262 va_start(ap
, xpath_base_fmt
);
263 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
266 return nb_cli_apply_changes_internal(vty
, xpath_base
, false);
269 int nb_cli_apply_changes_clear_pending(struct vty
*vty
,
270 const char *xpath_base_fmt
, ...)
272 char xpath_base
[XPATH_MAXLEN
] = {};
274 /* Parse the base XPath format string. */
275 if (xpath_base_fmt
) {
278 va_start(ap
, xpath_base_fmt
);
279 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
282 return nb_cli_apply_changes_internal(vty
, xpath_base
, true);
285 int nb_cli_rpc(struct vty
*vty
, const char *xpath
, struct list
*input
,
288 struct nb_node
*nb_node
;
290 char errmsg
[BUFSIZ
] = {0};
292 nb_node
= nb_node_find(xpath
);
294 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
295 "%s: unknown data path: %s", __func__
, xpath
);
299 ret
= nb_callback_rpc(nb_node
, xpath
, input
, output
, errmsg
,
306 vty_show_nb_errors(vty
, ret
, errmsg
);
311 void nb_cli_confirmed_commit_clean(struct vty
*vty
)
313 thread_cancel(&vty
->t_confirmed_commit_timeout
);
314 nb_config_free(vty
->confirmed_commit_rollback
);
315 vty
->confirmed_commit_rollback
= NULL
;
318 int nb_cli_confirmed_commit_rollback(struct vty
*vty
)
320 struct nb_context context
= {};
321 uint32_t transaction_id
;
322 char errmsg
[BUFSIZ
] = {0};
325 /* Perform the rollback. */
326 context
.client
= NB_CLIENT_CLI
;
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
));
334 "Rollback performed successfully (Transaction ID #%u).\n",
336 /* Print warnings (if any). */
337 if (strlen(errmsg
) > 0)
338 vty_out(vty
, "%s\n", errmsg
);
341 "Failed to rollback to previous configuration.\n\n");
342 vty_show_nb_errors(vty
, ret
, errmsg
);
348 static void nb_cli_confirmed_commit_timeout(struct thread
*thread
)
350 struct vty
*vty
= THREAD_ARG(thread
);
352 /* XXX: broadcast this message to all logged-in users? */
354 "\nConfirmed commit has timed out, rolling back to previous configuration.\n\n");
356 nb_cli_confirmed_commit_rollback(vty
);
357 nb_cli_confirmed_commit_clean(vty
);
360 static int nb_cli_commit(struct vty
*vty
, bool force
,
361 unsigned int confirmed_timeout
, char *comment
)
363 struct nb_context context
= {};
364 uint32_t transaction_id
= 0;
365 char errmsg
[BUFSIZ
] = {0};
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. */
373 "%% Resetting confirmed-commit timeout to %u minute(s)\n\n",
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
);
382 /* Accept commit confirmation. */
383 vty_out(vty
, "%% Commit complete.\n\n");
384 nb_cli_confirmed_commit_clean(vty
);
389 /* "force" parameter. */
390 if (!force
&& nb_candidate_needs_update(vty
->candidate_config
)) {
392 "%% Candidate configuration needs to be updated before commit.\n\n");
394 "Use the \"update\" command or \"commit force\".\n");
398 /* "confirm" parameter. */
399 if (confirmed_timeout
) {
400 vty
->confirmed_commit_rollback
= nb_config_dup(running_config
);
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
);
408 context
.client
= NB_CLIENT_CLI
;
410 ret
= nb_candidate_commit(&context
, vty
->candidate_config
, true,
411 comment
, &transaction_id
, errmsg
,
414 /* Map northbound return code to CLI return code. */
417 nb_config_replace(vty
->candidate_config_base
, running_config
,
420 "%% Configuration committed successfully (Transaction ID #%u).\n\n",
422 /* Print warnings (if any). */
423 if (strlen(errmsg
) > 0)
424 vty_out(vty
, "%s\n", errmsg
);
426 case NB_ERR_NO_CHANGES
:
427 vty_out(vty
, "%% No configuration changes to commit.\n\n");
431 "%% Failed to commit candidate configuration.\n\n");
432 vty_show_nb_errors(vty
, ret
, errmsg
);
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
)
442 struct nb_config
*loaded_config
= NULL
;
443 struct lyd_node
*dnode
;
444 struct ly_ctx
*ly_ctx
;
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");
455 "Please check the logs for more details.\n");
456 nb_config_free(loaded_config
);
460 case NB_CFG_FMT_JSON
:
462 ly_format
= (format
== NB_CFG_FMT_JSON
) ? LYD_JSON
: LYD_XML
;
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
,
469 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_path() failed",
471 vty_out(vty
, "%% Failed to load configuration:\n\n");
473 yang_print_errors(ly_native_ctx
, buf
,
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
);
485 loaded_config
= nb_config_new(dnode
);
490 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
491 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
494 "%% Failed to merge the loaded configuration:\n\n");
496 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
503 static int nb_cli_candidate_load_transaction(struct vty
*vty
,
504 uint32_t transaction_id
,
507 struct nb_config
*loaded_config
;
510 loaded_config
= nb_db_transaction_load(transaction_id
);
511 if (!loaded_config
) {
512 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
518 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
519 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
522 "%% Failed to merge the loaded configuration:\n\n");
524 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
531 /* Prepare the configuration for display. */
532 void nb_cli_show_config_prepare(struct nb_config
*config
, bool with_defaults
)
534 /* Nothing to do for daemons that don't implement any YANG module. */
535 if (config
->dnode
== NULL
)
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
544 if (config
!= running_config
)
545 (void)lyd_validate_all(&config
->dnode
, ly_native_ctx
,
546 LYD_VALIDATE_NO_STATE
, NULL
);
549 static int lyd_node_cmp(const struct lyd_node
**dnode1
,
550 const struct lyd_node
**dnode2
)
552 struct nb_node
*nb_node
= (*dnode1
)->schema
->priv
;
554 return nb_node
->cbs
.cli_cmp(*dnode1
, *dnode2
);
557 static void show_dnode_children_cmds(struct vty
*vty
,
558 const struct lyd_node
*root
,
561 struct nb_node
*nb_node
, *sort_node
= NULL
;
562 struct listnode
*listnode
;
563 struct lyd_node
*child
;
564 struct list
*sort_list
;
567 LY_LIST_FOR (lyd_child(root
), child
) {
568 nb_node
= child
->schema
->priv
;
571 * We finished processing current list,
572 * it's time to print the config.
574 if (sort_node
&& nb_node
!= sort_node
) {
576 (int (*)(const void **,
577 const void **))lyd_node_cmp
);
579 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
580 nb_cli_show_dnode_cmds(vty
, data
,
583 list_delete(&sort_list
);
588 * If the config needs to be sorted,
589 * then add the dnode to the sorting
590 * list for later processing.
592 if (nb_node
&& nb_node
->cbs
.cli_cmp
) {
595 sort_list
= list_new();
598 listnode_add(sort_list
, child
);
602 nb_cli_show_dnode_cmds(vty
, child
, with_defaults
);
607 (int (*)(const void **, const void **))lyd_node_cmp
);
609 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
610 nb_cli_show_dnode_cmds(vty
, data
, with_defaults
);
612 list_delete(&sort_list
);
617 void nb_cli_show_dnode_cmds(struct vty
*vty
, const struct lyd_node
*root
,
620 struct nb_node
*nb_node
;
622 if (!with_defaults
&& yang_dnode_is_default_recursive(root
))
625 nb_node
= root
->schema
->priv
;
627 if (nb_node
&& nb_node
->cbs
.cli_show
)
628 (*nb_node
->cbs
.cli_show
)(vty
, root
, with_defaults
);
630 if (!(root
->schema
->nodetype
& (LYS_LEAF
| LYS_LEAFLIST
| LYS_ANYDATA
)))
631 show_dnode_children_cmds(vty
, root
, with_defaults
);
633 if (nb_node
&& nb_node
->cbs
.cli_show_end
)
634 (*nb_node
->cbs
.cli_show_end
)(vty
, root
);
637 static void nb_cli_show_config_cmds(struct vty
*vty
, struct nb_config
*config
,
640 struct lyd_node
*root
;
642 vty_out(vty
, "Configuration:\n");
644 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
645 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
647 LY_LIST_FOR (config
->dnode
, root
) {
648 nb_cli_show_dnode_cmds(vty
, root
, with_defaults
);
652 vty_out(vty
, "end\n");
655 static int nb_cli_show_config_libyang(struct vty
*vty
, LYD_FORMAT format
,
656 struct nb_config
*config
,
657 struct yang_translator
*translator
,
660 struct lyd_node
*dnode
;
664 dnode
= yang_dnode_dup(config
->dnode
);
666 && yang_translate_dnode(translator
, YANG_TRANSLATE_FROM_NATIVE
,
668 != YANG_TRANSLATE_SUCCESS
) {
669 vty_out(vty
, "%% Failed to translate configuration\n");
670 yang_dnode_free(dnode
);
674 SET_FLAG(options
, LYD_PRINT_WITHSIBLINGS
);
676 SET_FLAG(options
, LYD_PRINT_WD_ALL
);
678 SET_FLAG(options
, LYD_PRINT_WD_TRIM
);
680 if (lyd_print_mem(&strp
, dnode
, format
, options
) == 0 && strp
) {
681 vty_out(vty
, "%s", strp
);
685 yang_dnode_free(dnode
);
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
,
695 nb_cli_show_config_prepare(config
, with_defaults
);
698 case NB_CFG_FMT_CMDS
:
699 nb_cli_show_config_cmds(vty
, config
, with_defaults
);
701 case NB_CFG_FMT_JSON
:
702 return nb_cli_show_config_libyang(vty
, LYD_JSON
, config
,
703 translator
, with_defaults
);
705 return nb_cli_show_config_libyang(vty
, LYD_XML
, config
,
706 translator
, with_defaults
);
712 static int nb_write_config(struct nb_config
*config
, enum nb_cfg_format format
,
713 struct yang_translator
*translator
, char *path
,
717 struct vty
*file_vty
;
720 snprintf(path
, pathlen
, "/tmp/frr.tmp.XXXXXXXX");
723 flog_warn(EC_LIB_SYSTEM_CALL
, "%s: mkstemp() failed: %s",
724 __func__
, safe_strerror(errno
));
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
);
734 /* Make vty for configuration file. */
735 file_vty
= vty_new();
737 file_vty
->type
= VTY_FILE
;
739 ret
= nb_cli_show_config(file_vty
, config
, format
, translator
,
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
)
752 char config1_path
[256];
753 char config2_path
[256];
754 char command
[BUFSIZ
];
759 if (nb_write_config(config1
, format
, translator
, config1_path
,
760 sizeof(config1_path
))
762 vty_out(vty
, "%% Failed to process configurations.\n\n");
765 if (nb_write_config(config2
, format
, translator
, config2_path
,
766 sizeof(config2_path
))
768 vty_out(vty
, "%% Failed to process configurations.\n\n");
769 unlink(config1_path
);
773 snprintf(command
, sizeof(command
), "diff -u %s %s", config1_path
,
775 fp
= popen(command
, "r");
777 vty_out(vty
, "%% Failed to generate configuration diff.\n\n");
778 unlink(config1_path
);
779 unlink(config2_path
);
782 /* Print diff line by line. */
783 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
786 vty_out(vty
, "%s", line
);
790 unlink(config1_path
);
791 unlink(config2_path
);
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")
803 return vty_config_enter(vty
, true, true);
806 /* Configure using a private candidate configuration. */
807 DEFUN (config_private
,
810 "Configuration from vty interface\n"
811 "Configure using a private candidate configuration\n")
813 return vty_config_enter(vty
, true, false);
816 DEFPY (config_commit
,
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")
824 return nb_cli_commit(vty
, !!force
, confirmed
, NULL
);
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")
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
);
849 DEFPY (config_commit_check
,
850 config_commit_check_cmd
,
852 "Commit changes into the running configuration\n"
853 "Check if the configuration changes are valid\n")
855 struct nb_context context
= {};
856 char errmsg
[BUFSIZ
] = {0};
859 context
.client
= NB_CLIENT_CLI
;
861 ret
= nb_candidate_validate(&context
, vty
->candidate_config
, errmsg
,
865 "%% Failed to validate candidate configuration.\n\n");
866 vty_show_nb_errors(vty
, ret
, errmsg
);
870 vty_out(vty
, "%% Candidate configuration validated successfully.\n\n");
875 DEFPY (config_update
,
878 "Update candidate configuration\n")
880 if (!nb_candidate_needs_update(vty
->candidate_config
)) {
881 vty_out(vty
, "%% Update is not necessary.\n\n");
885 if (nb_candidate_update(vty
->candidate_config
) != NB_OK
) {
887 "%% Failed to update the candidate configuration.\n\n");
888 vty_out(vty
, "Please check the logs for more details.\n");
892 nb_config_replace(vty
->candidate_config_base
, running_config
, true);
894 vty_out(vty
, "%% Candidate configuration updated successfully.\n\n");
899 DEFPY (config_discard
,
902 "Discard changes in the candidate configuration\n")
904 nb_config_replace(vty
->candidate_config
, vty
->candidate_config_base
,
914 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
915 |transaction (1-4294967295)$tid\
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"
928 "Replace instead of merge\n")
931 enum nb_cfg_format format
;
932 struct yang_translator
*translator
= NULL
;
935 format
= NB_CFG_FMT_JSON
;
937 format
= NB_CFG_FMT_XML
;
939 format
= NB_CFG_FMT_CMDS
;
941 if (translator_family
) {
942 translator
= yang_translator_find(translator_family
);
945 "%% Module translator \"%s\" not found\n",
951 return nb_cli_candidate_load_file(vty
, format
, translator
,
952 filename
, !!replace
);
955 return nb_cli_candidate_load_transaction(vty
, tid
, !!replace
);
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]",
964 "Configuration information\n"
965 "Running configuration\n"
966 "Change output format to JSON\n"
967 "Change output format to XML\n"
969 "YANG module translator\n"
970 "Show default values\n")
973 enum nb_cfg_format format
;
974 struct yang_translator
*translator
= NULL
;
977 format
= NB_CFG_FMT_JSON
;
979 format
= NB_CFG_FMT_XML
;
981 format
= NB_CFG_FMT_CMDS
;
983 if (translator_family
) {
984 translator
= yang_translator_find(translator_family
);
986 vty_out(vty
, "%% Module translator \"%s\" not found\n",
992 nb_cli_show_config(vty
, running_config
, format
, translator
,
998 DEFPY (show_config_candidate
,
999 show_config_candidate_cmd
,
1000 "show configuration candidate\
1001 [<json$json|xml$xml> [translate WORD$translator_family]]\
1003 with-defaults$with_defaults\
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")
1017 enum nb_cfg_format format
;
1018 struct yang_translator
*translator
= NULL
;
1021 format
= NB_CFG_FMT_JSON
;
1023 format
= NB_CFG_FMT_XML
;
1025 format
= NB_CFG_FMT_CMDS
;
1027 if (translator_family
) {
1028 translator
= yang_translator_find(translator_family
);
1030 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1037 return nb_cli_show_config_compare(
1038 vty
, vty
->candidate_config_base
, vty
->candidate_config
,
1039 format
, translator
);
1041 nb_cli_show_config(vty
, vty
->candidate_config
, format
, translator
,
1047 DEFPY (show_config_candidate_section
,
1048 show_config_candidate_section_cmd
,
1052 struct lyd_node
*dnode
;
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);
1059 /* Display only the current section of the candidate configuration. */
1060 dnode
= yang_dnode_get(vty
->candidate_config
->dnode
, VTY_CURR_XPATH
);
1062 /* Shouldn't happen. */
1065 nb_cli_show_dnode_cmds(vty
, dnode
, 0);
1066 vty_out(vty
, "!\n");
1071 DEFPY (show_config_compare
,
1072 show_config_compare_cmd
,
1073 "show configuration compare\
1075 candidate$c1_candidate\
1076 |running$c1_running\
1077 |transaction (1-4294967295)$c1_tid\
1080 candidate$c2_candidate\
1081 |running$c2_running\
1082 |transaction (1-4294967295)$c2_tid\
1084 [<json$json|xml$xml> [translate WORD$translator_family]]",
1086 "Configuration information\n"
1087 "Compare two different configurations\n"
1088 "Candidate configuration\n"
1089 "Running configuration\n"
1090 "Configuration transaction\n"
1092 "Candidate configuration\n"
1093 "Running configuration\n"
1094 "Configuration transaction\n"
1096 "Change output format to JSON\n"
1097 "Change output format to XML\n"
1098 "Translate output\n"
1099 "YANG module translator\n")
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
;
1108 config1
= vty
->candidate_config
;
1109 else if (c1_running
)
1110 config1
= running_config
;
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
);
1118 config1
= config_transaction1
;
1122 config2
= vty
->candidate_config
;
1123 else if (c2_running
)
1124 config2
= running_config
;
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
);
1132 config2
= config_transaction2
;
1136 format
= NB_CFG_FMT_JSON
;
1138 format
= NB_CFG_FMT_XML
;
1140 format
= NB_CFG_FMT_CMDS
;
1142 if (translator_family
) {
1143 translator
= yang_translator_find(translator_family
);
1145 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1151 ret
= nb_cli_show_config_compare(vty
, config1
, config2
, format
,
1154 if (config_transaction1
)
1155 nb_config_free(config_transaction1
);
1156 if (config_transaction2
)
1157 nb_config_free(config_transaction2
);
1163 * Stripped down version of the "show configuration compare" command.
1164 * The "candidate" option is not present so the command can be installed in
1167 ALIAS (show_config_compare
,
1168 show_config_compare_without_candidate_cmd
,
1169 "show configuration compare\
1172 |transaction (1-4294967295)$c1_tid\
1176 |transaction (1-4294967295)$c2_tid\
1178 [<json$json|xml$xml> [translate WORD$translator_family]]",
1180 "Configuration information\n"
1181 "Compare two different configurations\n"
1182 "Running configuration\n"
1183 "Configuration transaction\n"
1185 "Running configuration\n"
1186 "Configuration transaction\n"
1188 "Change output format to JSON\n"
1189 "Change output format to XML\n"
1190 "Translate output\n"
1191 "YANG module translator\n")
1193 DEFPY (clear_config_transactions
,
1194 clear_config_transactions_cmd
,
1195 "clear configuration transactions oldest (1-100)$n",
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")
1202 #ifdef HAVE_CONFIG_ROLLBACKS
1203 if (nb_db_clear_transactions(n
) != NB_OK
) {
1204 vty_out(vty
, "%% Failed to delete transactions.\n\n");
1209 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1210 #endif /* HAVE_CONFIG_ROLLBACKS */
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")
1223 #ifdef HAVE_CONFIG_ROLLBACKS
1224 if (nb_db_set_max_transactions(max
) != NB_OK
) {
1226 "%% Failed to update the maximum number of transactions.\n\n");
1230 "%% Maximum number of transactions updated successfully.\n\n");
1233 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1234 #endif /* HAVE_CONFIG_ROLLBACKS */
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")
1247 struct yang_translator
*translator
;
1249 translator
= yang_translator_load(filename
);
1251 vty_out(vty
, "%% Failed to load \"%s\"\n\n", filename
);
1252 vty_out(vty
, "Please check the logs for more details.\n");
1256 vty_out(vty
, "%% Module translator \"%s\" loaded successfully.\n\n",
1257 translator
->family
);
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")
1270 struct yang_translator
*translator
;
1272 translator
= yang_translator_find(translator_family
);
1274 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1279 yang_translator_unload(translator
);
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
)
1289 struct ttable
*tt
= arg
;
1291 ttable_add_row(tt
, "%d|%s|%s|%s", transaction_id
, client_name
, date
,
1295 static int nb_cli_show_transactions(struct vty
*vty
)
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
= '+';
1305 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1307 /* Fetch transactions from the northbound database. */
1308 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb
, tt
)
1311 "%% Failed to fetch configuration transactions.\n");
1315 /* Dump the generated table. */
1316 if (tt
->nrows
> 1) {
1319 table
= ttable_dump(tt
, "\n");
1320 vty_out(vty
, "%s\n", table
);
1321 XFREE(MTYPE_TMP
, table
);
1323 vty_out(vty
, "No configuration transactions to display.\n\n");
1329 #endif /* HAVE_CONFIG_ROLLBACKS */
1331 DEFPY (show_config_transaction
,
1332 show_config_transaction_cmd
,
1333 "show configuration transaction\
1335 (1-4294967295)$transaction_id\
1336 [<json$json|xml$xml> [translate WORD$translator_family]]\
1338 with-defaults$with_defaults\
1343 "Configuration information\n"
1344 "Configuration transaction\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")
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
;
1360 format
= NB_CFG_FMT_JSON
;
1362 format
= NB_CFG_FMT_XML
;
1364 format
= NB_CFG_FMT_CMDS
;
1366 if (translator_family
) {
1367 translator
= yang_translator_find(translator_family
);
1370 "%% Module translator \"%s\" not found\n",
1376 config
= nb_db_transaction_load(transaction_id
);
1378 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1379 (unsigned int)transaction_id
);
1384 struct nb_config
*prev_config
;
1387 /* NOTE: this can be NULL. */
1389 nb_db_transaction_load(transaction_id
- 1);
1391 ret
= nb_cli_show_config_compare(
1392 vty
, prev_config
, config
, format
, translator
);
1394 nb_config_free(prev_config
);
1395 nb_config_free(config
);
1400 nb_cli_show_config(vty
, config
, format
, translator
,
1402 nb_config_free(config
);
1407 return nb_cli_show_transactions(vty
);
1410 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1412 #endif /* HAVE_CONFIG_ROLLBACKS */
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
)
1419 struct lyd_node
*dnode
= arg
;
1420 struct ly_ctx
*ly_ctx
;
1425 ret
= yang_translate_xpath(translator
,
1426 YANG_TRANSLATE_FROM_NATIVE
,
1427 data
->xpath
, sizeof(data
->xpath
));
1429 case YANG_TRANSLATE_SUCCESS
:
1431 case YANG_TRANSLATE_NOTFOUND
:
1433 case YANG_TRANSLATE_FAILURE
:
1437 ly_ctx
= translator
->ly_ctx
;
1439 ly_ctx
= ly_native_ctx
;
1442 lyd_new_path(dnode
, ly_ctx
, data
->xpath
, (void *)data
->value
,
1443 LYD_NEW_PATH_UPDATE
, &dnode
);
1445 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_new_path(%s) failed: %s",
1446 __func__
, data
->xpath
, ly_errmsg(ly_native_ctx
));
1451 yang_data_free(data
);
1455 yang_data_free(data
);
1459 DEFPY (show_yang_operational_data
,
1460 show_yang_operational_data_cmd
,
1461 "show yang operational-data XPATH$xpath\
1463 format <json$json|xml$xml>\
1464 |translate WORD$translator_family\
1465 |with-config$with_config\
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")
1479 struct yang_translator
*translator
= NULL
;
1480 struct ly_ctx
*ly_ctx
;
1481 struct lyd_node
*dnode
;
1483 uint32_t print_options
= LYD_PRINT_WITHSIBLINGS
;
1490 if (translator_family
) {
1491 translator
= yang_translator_find(translator_family
);
1493 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1498 ly_ctx
= translator
->ly_ctx
;
1500 ly_ctx
= ly_native_ctx
;
1503 dnode
= yang_dnode_new(ly_ctx
, false);
1504 if (nb_oper_data_iterate(xpath
, translator
, 0, nb_cli_oper_data_cb
,
1507 vty_out(vty
, "%% Failed to fetch operational data.\n");
1508 yang_dnode_free(dnode
);
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
;
1522 (void)lyd_validate_all(&dnode
, ly_ctx
, 0, NULL
);
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
);
1530 vty_out(vty
, "%s", strp
);
1532 yang_dnode_free(dnode
);
1537 DEFPY (show_yang_module
,
1538 show_yang_module_cmd
,
1539 "show yang module [module-translator WORD$translator_family]",
1541 "YANG information\n"
1542 "Show loaded modules\n"
1543 "YANG module translator\n"
1544 "YANG module translator\n")
1546 struct ly_ctx
*ly_ctx
;
1547 struct yang_translator
*translator
= NULL
;
1548 const struct lys_module
*module
;
1552 if (translator_family
) {
1553 translator
= yang_translator_find(translator_family
);
1555 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1559 ly_ctx
= translator
->ly_ctx
;
1561 ly_ctx
= ly_native_ctx
;
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
= '+';
1569 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1571 while ((module
= ly_ctx_get_module_iter(ly_ctx
, &idx
))) {
1574 snprintf(flags
, sizeof(flags
), "%c%c",
1575 module
->implemented
? 'I' : ' ',
1576 LY_ARRAY_COUNT(module
->deviated_by
) ? 'D' : ' ');
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
,
1584 /* Dump the generated table. */
1585 if (tt
->nrows
> 1) {
1588 vty_out(vty
, " Flags: I - Implemented, D - Deviated\n\n");
1590 table
= ttable_dump(tt
, "\n");
1591 vty_out(vty
, "%s\n", table
);
1592 XFREE(MTYPE_TMP
, table
);
1594 vty_out(vty
, "No YANG modules to display.\n\n");
1601 DEFPY(show_yang_module_detail
, show_yang_module_detail_cmd
,
1603 [module-translator WORD$translator_family]\
1604 WORD$module_name <compiled$compiled|summary|tree$tree|yang$yang|yin$yin>",
1606 "YANG information\n"
1607 "Show loaded modules\n"
1608 "YANG module translator\n"
1609 "YANG module translator\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")
1617 struct ly_ctx
*ly_ctx
;
1618 struct yang_translator
*translator
= NULL
;
1619 const struct lys_module
*module
;
1620 LYS_OUTFORMAT format
;
1623 if (translator_family
) {
1624 translator
= yang_translator_find(translator_family
);
1626 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1630 ly_ctx
= translator
->ly_ctx
;
1632 ly_ctx
= ly_native_ctx
;
1634 module
= ly_ctx_get_module_latest(ly_ctx
, module_name
);
1636 vty_out(vty
, "%% Module \"%s\" not found\n", module_name
);
1641 format
= LYS_OUT_YANG
;
1643 format
= LYS_OUT_YIN
;
1645 format
= LYS_OUT_YANG_COMPILED
;
1647 format
= LYS_OUT_TREE
;
1650 "%% libyang v2 does not currently support summary\n");
1654 if (lys_print_mem(&strp
, module
, format
, 0) == 0) {
1655 vty_out(vty
, "%s\n", strp
);
1659 vty_out(vty
, "%% Error generating module information\n");
1666 DEFPY (show_yang_module_translator
,
1667 show_yang_module_translator_cmd
,
1668 "show yang module-translator",
1670 "YANG information\n"
1671 "Show loaded YANG module translators\n")
1673 struct yang_translator
*translator
;
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
= '+';
1682 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1684 RB_FOREACH (translator
, yang_translators
, &yang_translators
) {
1685 struct yang_tmodule
*tmodule
;
1686 struct listnode
*ln
;
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
,
1696 /* Dump the generated table. */
1697 if (tt
->nrows
> 1) {
1700 table
= ttable_dump(tt
, "\n");
1701 vty_out(vty
, "%s\n", table
);
1702 XFREE(MTYPE_TMP
, table
);
1704 vty_out(vty
, "No YANG module translators to display.\n\n");
1711 #ifdef HAVE_CONFIG_ROLLBACKS
1712 static int nb_cli_rollback_configuration(struct vty
*vty
,
1713 uint32_t transaction_id
)
1715 struct nb_context context
= {};
1716 struct nb_config
*candidate
;
1718 char errmsg
[BUFSIZ
] = {0};
1721 candidate
= nb_db_transaction_load(transaction_id
);
1723 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1728 snprintf(comment
, sizeof(comment
), "Rollback to transaction %u",
1731 context
.client
= NB_CLIENT_CLI
;
1733 ret
= nb_candidate_commit(&context
, candidate
, true, comment
, NULL
,
1734 errmsg
, sizeof(errmsg
));
1735 nb_config_free(candidate
);
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
);
1744 case NB_ERR_NO_CHANGES
:
1746 "%% Aborting - no configuration changes detected.\n\n");
1749 vty_out(vty
, "%% Rollback failed.\n\n");
1750 vty_show_nb_errors(vty
, ret
, errmsg
);
1754 #endif /* HAVE_CONFIG_ROLLBACKS */
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"
1763 #ifdef HAVE_CONFIG_ROLLBACKS
1764 return nb_cli_rollback_configuration(vty
, transaction_id
);
1767 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1769 #endif /* HAVE_CONFIG_ROLLBACKS */
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
,
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",
1787 DEFINE_HOOK(nb_client_debug_set_all
, (uint32_t flags
, bool set
), (flags
, set
));
1789 static void nb_debug_set_all(uint32_t flags
, bool set
)
1791 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++) {
1792 DEBUG_FLAGS_SET(nb_debugs
[i
], flags
, set
);
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
]);
1799 hook_call(nb_client_debug_set_all
, flags
, set
);
1804 "[no] debug northbound\
1806 callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\
1807 |notifications$notifications\
1813 "Northbound debugging\n"
1820 "libyang debugging\n")
1822 uint32_t mode
= DEBUG_NODE2MODE(vty
->node
);
1825 bool none
= (!cbs_cfg
&& !cbs_state
&& !cbs_rpc
);
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
);
1835 DEBUG_MODE_SET(&nb_dbg_notif
, mode
, !no
);
1837 DEBUG_MODE_SET(&nb_dbg_events
, mode
, !no
);
1839 DEBUG_MODE_SET(&nb_dbg_libyang
, mode
, !no
);
1840 yang_debugging_set(!no
);
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
);
1852 DEFINE_HOOK(nb_client_debug_config_write
, (struct vty
*vty
), (vty
));
1854 static int nb_debug_config_write(struct vty
*vty
)
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
]);
1860 hook_call(nb_client_debug_config_write
, vty
);
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
,
1870 .config_write
= nb_debug_config_write
,
1873 void nb_cli_install_default(int node
)
1875 _install_element(node
, &show_config_candidate_section_cmd
);
1877 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL
)
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
);
1891 /* YANG module autocomplete. */
1892 static void yang_module_autocomplete(vector comps
, struct cmd_token
*token
)
1894 const struct lys_module
*module
;
1895 struct yang_translator
*module_tr
;
1899 while ((module
= ly_ctx_get_module_iter(ly_native_ctx
, &idx
)))
1900 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1902 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
) {
1904 while ((module
= ly_ctx_get_module_iter(module_tr
->ly_ctx
,
1907 XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1911 /* YANG module translator autocomplete. */
1912 static void yang_translator_autocomplete(vector comps
, struct cmd_token
*token
)
1914 struct yang_translator
*module_tr
;
1916 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
)
1917 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module_tr
->family
));
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
}};
1926 void nb_cli_init(struct thread_master
*tm
)
1930 /* Initialize the shared candidate configuration. */
1931 vty_shared_candidate_config
= nb_config_new(NULL
);
1933 debug_init(&nb_dbg_cbs
);
1935 install_node(&nb_debug_node
);
1936 install_element(ENABLE_NODE
, &debug_nb_cmd
);
1937 install_element(CONFIG_NODE
, &debug_nb_cmd
);
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
);
1949 install_element(CONFIG_NODE
, &config_load_cmd
);
1950 install_element(CONFIG_NODE
,
1951 &config_database_max_transactions_cmd
);
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
);
1965 void nb_cli_terminate(void)
1967 nb_config_free(vty_shared_candidate_config
);