1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2018 NetDEF, Inc.
10 #include "lib/version.h"
13 #include "lib_errors.h"
15 #include "termtable.h"
18 #include "yang_translator.h"
19 #include "northbound.h"
20 #include "northbound_cli.h"
21 #include "northbound_db.h"
22 #include "lib/northbound_cli_clippy.c"
24 struct debug nb_dbg_cbs_config
= {0, "Northbound callbacks: configuration"};
25 struct debug nb_dbg_cbs_state
= {0, "Northbound callbacks: state"};
26 struct debug nb_dbg_cbs_rpc
= {0, "Northbound callbacks: RPCs"};
27 struct debug nb_dbg_notif
= {0, "Northbound notifications"};
28 struct debug nb_dbg_events
= {0, "Northbound events"};
29 struct debug nb_dbg_libyang
= {0, "libyang debugging"};
31 struct nb_config
*vty_shared_candidate_config
;
32 static struct event_loop
*master
;
34 static void vty_show_nb_errors(struct vty
*vty
, int error
, const char *errmsg
)
36 vty_out(vty
, "Error type: %s\n", nb_err_name(error
));
37 if (strlen(errmsg
) > 0)
38 vty_out(vty
, "Error description: %s\n", errmsg
);
41 static int nb_cli_classic_commit(struct vty
*vty
)
43 struct nb_context context
= {};
44 char errmsg
[BUFSIZ
] = {0};
47 context
.client
= NB_CLIENT_CLI
;
49 ret
= nb_candidate_commit(context
, vty
->candidate_config
, true, NULL
,
50 NULL
, errmsg
, sizeof(errmsg
));
53 /* Successful commit. Print warnings (if any). */
54 if (strlen(errmsg
) > 0)
55 vty_out(vty
, "%s\n", errmsg
);
57 case NB_ERR_NO_CHANGES
:
60 vty_out(vty
, "%% Configuration failed.\n\n");
61 vty_show_nb_errors(vty
, ret
, errmsg
);
62 if (vty
->pending_commit
)
64 "The following commands were dynamically grouped into the same transaction and rejected:\n%s",
65 vty
->pending_cmds_buf
);
67 /* Regenerate candidate for consistency. */
68 nb_config_replace(vty
->candidate_config
, running_config
, true);
69 return CMD_WARNING_CONFIG_FAILED
;
75 static void nb_cli_pending_commit_clear(struct vty
*vty
)
77 vty
->pending_commit
= 0;
78 XFREE(MTYPE_TMP
, vty
->pending_cmds_buf
);
79 vty
->pending_cmds_buflen
= 0;
80 vty
->pending_cmds_bufpos
= 0;
83 int nb_cli_pending_commit_check(struct vty
*vty
)
85 int ret
= CMD_SUCCESS
;
87 if (vty
->pending_commit
) {
88 ret
= nb_cli_classic_commit(vty
);
89 nb_cli_pending_commit_clear(vty
);
95 static int nb_cli_schedule_command(struct vty
*vty
)
97 /* Append command to dynamically sized buffer of scheduled commands. */
98 if (!vty
->pending_cmds_buf
) {
99 vty
->pending_cmds_buflen
= 4096;
100 vty
->pending_cmds_buf
=
101 XCALLOC(MTYPE_TMP
, vty
->pending_cmds_buflen
);
103 if ((strlen(vty
->buf
) + 3)
104 > (vty
->pending_cmds_buflen
- vty
->pending_cmds_bufpos
)) {
105 vty
->pending_cmds_buflen
*= 2;
106 vty
->pending_cmds_buf
=
107 XREALLOC(MTYPE_TMP
, vty
->pending_cmds_buf
,
108 vty
->pending_cmds_buflen
);
110 strlcat(vty
->pending_cmds_buf
, "- ", vty
->pending_cmds_buflen
);
111 vty
->pending_cmds_bufpos
= strlcat(vty
->pending_cmds_buf
, vty
->buf
,
112 vty
->pending_cmds_buflen
);
114 /* Schedule the commit operation. */
115 vty
->pending_commit
= 1;
120 void nb_cli_enqueue_change(struct vty
*vty
, const char *xpath
,
121 enum nb_operation operation
, const char *value
)
123 struct nb_cfg_change
*change
;
125 if (vty
->num_cfg_changes
== VTY_MAXCFGCHANGES
) {
126 /* Not expected to happen. */
128 "%% Exceeded the maximum number of changes (%u) for a single command\n\n",
133 change
= &vty
->cfg_changes
[vty
->num_cfg_changes
++];
134 strlcpy(change
->xpath
, xpath
, sizeof(change
->xpath
));
135 change
->operation
= operation
;
136 change
->value
= value
;
139 static int nb_cli_apply_changes_internal(struct vty
*vty
,
140 const char *xpath_base
,
148 nb_candidate_edit_config_changes(
149 vty
->candidate_config
, vty
->cfg_changes
, vty
->num_cfg_changes
,
150 xpath_base
, VTY_CURR_XPATH
, vty
->xpath_index
, buf
, sizeof(buf
),
154 * Failure to edit the candidate configuration should never
155 * happen in practice, unless there's a bug in the code. When
156 * that happens, log the error but otherwise ignore it.
158 vty_out(vty
, "%s", buf
);
162 * Maybe do an implicit commit when using the classic CLI mode.
164 * NOTE: the implicit commit might be scheduled to run later when
165 * too many commands are being sent at the same time. This is a
166 * protection mechanism where multiple commands are grouped into the
167 * same configuration transaction, allowing them to be processed much
170 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
) {
172 if (vty
->pending_commit
)
173 return nb_cli_pending_commit_check(vty
);
174 } else if (vty
->pending_allowed
)
175 return nb_cli_schedule_command(vty
);
176 assert(!vty
->pending_commit
);
177 return nb_cli_classic_commit(vty
);
183 int nb_cli_apply_changes(struct vty
*vty
, const char *xpath_base_fmt
, ...)
185 char xpath_base
[XPATH_MAXLEN
] = {};
186 bool implicit_commit
;
189 /* Parse the base XPath format string. */
190 if (xpath_base_fmt
) {
193 va_start(ap
, xpath_base_fmt
);
194 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
198 if (vty_mgmt_should_process_cli_apply_changes(vty
)) {
201 if (vty
->type
== VTY_FILE
)
204 implicit_commit
= vty_needs_implicit_commit(vty
);
205 ret
= vty_mgmt_send_config_data(vty
);
206 if (ret
>= 0 && !implicit_commit
)
207 vty
->mgmt_num_pending_setcfg
++;
211 return nb_cli_apply_changes_internal(vty
, xpath_base
, false);
214 int nb_cli_apply_changes_clear_pending(struct vty
*vty
,
215 const char *xpath_base_fmt
, ...)
217 char xpath_base
[XPATH_MAXLEN
] = {};
218 bool implicit_commit
;
221 /* Parse the base XPath format string. */
222 if (xpath_base_fmt
) {
225 va_start(ap
, xpath_base_fmt
);
226 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
230 if (vty_mgmt_should_process_cli_apply_changes(vty
)) {
233 implicit_commit
= vty_needs_implicit_commit(vty
);
234 ret
= vty_mgmt_send_config_data(vty
);
235 if (ret
>= 0 && !implicit_commit
)
236 vty
->mgmt_num_pending_setcfg
++;
240 return nb_cli_apply_changes_internal(vty
, xpath_base
, true);
243 int nb_cli_rpc(struct vty
*vty
, const char *xpath
, struct list
*input
,
246 struct nb_node
*nb_node
;
248 char errmsg
[BUFSIZ
] = {0};
250 nb_node
= nb_node_find(xpath
);
252 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
253 "%s: unknown data path: %s", __func__
, xpath
);
257 ret
= nb_callback_rpc(nb_node
, xpath
, input
, output
, errmsg
,
264 vty_show_nb_errors(vty
, ret
, errmsg
);
269 void nb_cli_confirmed_commit_clean(struct vty
*vty
)
271 event_cancel(&vty
->t_confirmed_commit_timeout
);
272 nb_config_free(vty
->confirmed_commit_rollback
);
273 vty
->confirmed_commit_rollback
= NULL
;
276 int nb_cli_confirmed_commit_rollback(struct vty
*vty
)
278 struct nb_context context
= {};
279 uint32_t transaction_id
;
280 char errmsg
[BUFSIZ
] = {0};
283 /* Perform the rollback. */
284 context
.client
= NB_CLIENT_CLI
;
286 ret
= nb_candidate_commit(
287 context
, vty
->confirmed_commit_rollback
, true,
288 "Rollback to previous configuration - confirmed commit has timed out",
289 &transaction_id
, errmsg
, sizeof(errmsg
));
292 "Rollback performed successfully (Transaction ID #%u).\n",
294 /* Print warnings (if any). */
295 if (strlen(errmsg
) > 0)
296 vty_out(vty
, "%s\n", errmsg
);
299 "Failed to rollback to previous configuration.\n\n");
300 vty_show_nb_errors(vty
, ret
, errmsg
);
306 static void nb_cli_confirmed_commit_timeout(struct event
*thread
)
308 struct vty
*vty
= EVENT_ARG(thread
);
310 /* XXX: broadcast this message to all logged-in users? */
312 "\nConfirmed commit has timed out, rolling back to previous configuration.\n\n");
314 nb_cli_confirmed_commit_rollback(vty
);
315 nb_cli_confirmed_commit_clean(vty
);
318 static int nb_cli_commit(struct vty
*vty
, bool force
,
319 unsigned int confirmed_timeout
, char *comment
)
321 struct nb_context context
= {};
322 uint32_t transaction_id
= 0;
323 char errmsg
[BUFSIZ
] = {0};
326 /* Check if there's a pending confirmed commit. */
327 if (vty
->t_confirmed_commit_timeout
) {
328 if (confirmed_timeout
) {
329 /* Reset timeout if "commit confirmed" is used again. */
331 "%% Resetting confirmed-commit timeout to %u minute(s)\n\n",
334 event_cancel(&vty
->t_confirmed_commit_timeout
);
335 event_add_timer(master
, nb_cli_confirmed_commit_timeout
,
336 vty
, confirmed_timeout
* 60,
337 &vty
->t_confirmed_commit_timeout
);
339 /* Accept commit confirmation. */
340 vty_out(vty
, "%% Commit complete.\n\n");
341 nb_cli_confirmed_commit_clean(vty
);
346 /* "force" parameter. */
347 if (!force
&& nb_candidate_needs_update(vty
->candidate_config
)) {
349 "%% Candidate configuration needs to be updated before commit.\n\n");
351 "Use the \"update\" command or \"commit force\".\n");
355 /* "confirm" parameter. */
356 if (confirmed_timeout
) {
357 vty
->confirmed_commit_rollback
= nb_config_dup(running_config
);
359 vty
->t_confirmed_commit_timeout
= NULL
;
360 event_add_timer(master
, nb_cli_confirmed_commit_timeout
, vty
,
361 confirmed_timeout
* 60,
362 &vty
->t_confirmed_commit_timeout
);
365 context
.client
= NB_CLIENT_CLI
;
367 ret
= nb_candidate_commit(context
, vty
->candidate_config
, true, comment
,
368 &transaction_id
, errmsg
, sizeof(errmsg
));
370 /* Map northbound return code to CLI return code. */
373 nb_config_replace(vty
->candidate_config_base
, running_config
,
376 "%% Configuration committed successfully (Transaction ID #%u).\n\n",
378 /* Print warnings (if any). */
379 if (strlen(errmsg
) > 0)
380 vty_out(vty
, "%s\n", errmsg
);
382 case NB_ERR_NO_CHANGES
:
383 vty_out(vty
, "%% No configuration changes to commit.\n\n");
387 "%% Failed to commit candidate configuration.\n\n");
388 vty_show_nb_errors(vty
, ret
, errmsg
);
393 static int nb_cli_candidate_load_file(struct vty
*vty
,
394 enum nb_cfg_format format
,
395 struct yang_translator
*translator
,
396 const char *path
, bool replace
)
398 struct nb_config
*loaded_config
= NULL
;
399 struct lyd_node
*dnode
;
400 struct ly_ctx
*ly_ctx
;
406 case NB_CFG_FMT_CMDS
:
407 loaded_config
= nb_config_new(NULL
);
408 if (!vty_read_config(loaded_config
, path
, config_default
)) {
409 vty_out(vty
, "%% Failed to load configuration.\n\n");
411 "Please check the logs for more details.\n");
412 nb_config_free(loaded_config
);
416 case NB_CFG_FMT_JSON
:
418 ly_format
= (format
== NB_CFG_FMT_JSON
) ? LYD_JSON
: LYD_XML
;
420 ly_ctx
= translator
? translator
->ly_ctx
: ly_native_ctx
;
421 err
= lyd_parse_data_path(ly_ctx
, path
, ly_format
,
422 LYD_PARSE_ONLY
| LYD_PARSE_NO_STATE
,
425 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_path() failed",
427 vty_out(vty
, "%% Failed to load configuration:\n\n");
429 yang_print_errors(ly_native_ctx
, buf
,
434 && yang_translate_dnode(translator
,
435 YANG_TRANSLATE_TO_NATIVE
, &dnode
)
436 != YANG_TRANSLATE_SUCCESS
) {
437 vty_out(vty
, "%% Failed to translate configuration\n");
438 yang_dnode_free(dnode
);
441 loaded_config
= nb_config_new(dnode
);
446 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
447 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
450 "%% Failed to merge the loaded configuration:\n\n");
452 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
459 static int nb_cli_candidate_load_transaction(struct vty
*vty
,
460 uint32_t transaction_id
,
463 struct nb_config
*loaded_config
;
466 loaded_config
= nb_db_transaction_load(transaction_id
);
467 if (!loaded_config
) {
468 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
474 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
475 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
478 "%% Failed to merge the loaded configuration:\n\n");
480 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
487 /* Prepare the configuration for display. */
488 void nb_cli_show_config_prepare(struct nb_config
*config
, bool with_defaults
)
490 /* Nothing to do for daemons that don't implement any YANG module. */
491 if (config
->dnode
== NULL
)
495 * Call lyd_validate() only to create default child nodes, ignoring
496 * any possible validation error. This doesn't need to be done when
497 * displaying the running configuration since it's always fully
500 if (config
!= running_config
)
501 (void)lyd_validate_all(&config
->dnode
, ly_native_ctx
,
502 LYD_VALIDATE_NO_STATE
, NULL
);
505 static int lyd_node_cmp(const struct lyd_node
**dnode1
,
506 const struct lyd_node
**dnode2
)
508 struct nb_node
*nb_node
= (*dnode1
)->schema
->priv
;
510 return nb_node
->cbs
.cli_cmp(*dnode1
, *dnode2
);
513 static void show_dnode_children_cmds(struct vty
*vty
,
514 const struct lyd_node
*root
,
517 struct nb_node
*nb_node
, *sort_node
= NULL
;
518 struct listnode
*listnode
;
519 struct lyd_node
*child
;
520 struct list
*sort_list
;
523 LY_LIST_FOR (lyd_child(root
), child
) {
524 nb_node
= child
->schema
->priv
;
527 * We finished processing current list,
528 * it's time to print the config.
530 if (sort_node
&& nb_node
!= sort_node
) {
532 (int (*)(const void **,
533 const void **))lyd_node_cmp
);
535 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
536 nb_cli_show_dnode_cmds(vty
, data
,
539 list_delete(&sort_list
);
544 * If the config needs to be sorted,
545 * then add the dnode to the sorting
546 * list for later processing.
548 if (nb_node
&& nb_node
->cbs
.cli_cmp
) {
551 sort_list
= list_new();
554 listnode_add(sort_list
, child
);
558 nb_cli_show_dnode_cmds(vty
, child
, with_defaults
);
563 (int (*)(const void **, const void **))lyd_node_cmp
);
565 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
566 nb_cli_show_dnode_cmds(vty
, data
, with_defaults
);
568 list_delete(&sort_list
);
573 void nb_cli_show_dnode_cmds(struct vty
*vty
, const struct lyd_node
*root
,
576 struct nb_node
*nb_node
;
578 if (!with_defaults
&& yang_dnode_is_default_recursive(root
))
581 nb_node
= root
->schema
->priv
;
583 if (nb_node
&& nb_node
->cbs
.cli_show
)
584 (*nb_node
->cbs
.cli_show
)(vty
, root
, with_defaults
);
586 if (!(root
->schema
->nodetype
& (LYS_LEAF
| LYS_LEAFLIST
| LYS_ANYDATA
)))
587 show_dnode_children_cmds(vty
, root
, with_defaults
);
589 if (nb_node
&& nb_node
->cbs
.cli_show_end
)
590 (*nb_node
->cbs
.cli_show_end
)(vty
, root
);
593 static void nb_cli_show_config_cmds(struct vty
*vty
, struct nb_config
*config
,
596 struct lyd_node
*root
;
598 vty_out(vty
, "Configuration:\n");
600 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
601 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
603 LY_LIST_FOR (config
->dnode
, root
) {
604 nb_cli_show_dnode_cmds(vty
, root
, with_defaults
);
608 vty_out(vty
, "end\n");
611 static int nb_cli_show_config_libyang(struct vty
*vty
, LYD_FORMAT format
,
612 struct nb_config
*config
,
613 struct yang_translator
*translator
,
616 struct lyd_node
*dnode
;
620 dnode
= yang_dnode_dup(config
->dnode
);
622 && yang_translate_dnode(translator
, YANG_TRANSLATE_FROM_NATIVE
,
624 != YANG_TRANSLATE_SUCCESS
) {
625 vty_out(vty
, "%% Failed to translate configuration\n");
626 yang_dnode_free(dnode
);
630 SET_FLAG(options
, LYD_PRINT_WITHSIBLINGS
);
632 SET_FLAG(options
, LYD_PRINT_WD_ALL
);
634 SET_FLAG(options
, LYD_PRINT_WD_TRIM
);
636 if (lyd_print_mem(&strp
, dnode
, format
, options
) == 0 && strp
) {
637 vty_out(vty
, "%s", strp
);
641 yang_dnode_free(dnode
);
646 static int nb_cli_show_config(struct vty
*vty
, struct nb_config
*config
,
647 enum nb_cfg_format format
,
648 struct yang_translator
*translator
,
651 nb_cli_show_config_prepare(config
, with_defaults
);
654 case NB_CFG_FMT_CMDS
:
655 nb_cli_show_config_cmds(vty
, config
, with_defaults
);
657 case NB_CFG_FMT_JSON
:
658 return nb_cli_show_config_libyang(vty
, LYD_JSON
, config
,
659 translator
, with_defaults
);
661 return nb_cli_show_config_libyang(vty
, LYD_XML
, config
,
662 translator
, with_defaults
);
668 static int nb_write_config(struct nb_config
*config
, enum nb_cfg_format format
,
669 struct yang_translator
*translator
, char *path
,
673 struct vty
*file_vty
;
676 snprintf(path
, pathlen
, "/tmp/frr.tmp.XXXXXXXX");
679 flog_warn(EC_LIB_SYSTEM_CALL
, "%s: mkstemp() failed: %s",
680 __func__
, safe_strerror(errno
));
683 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
684 flog_warn(EC_LIB_SYSTEM_CALL
,
685 "%s: fchmod() failed: %s(%d):", __func__
,
686 safe_strerror(errno
), errno
);
690 /* Make vty for configuration file. */
691 file_vty
= vty_new();
693 file_vty
->type
= VTY_FILE
;
695 ret
= nb_cli_show_config(file_vty
, config
, format
, translator
,
702 static int nb_cli_show_config_compare(struct vty
*vty
,
703 struct nb_config
*config1
,
704 struct nb_config
*config2
,
705 enum nb_cfg_format format
,
706 struct yang_translator
*translator
)
708 char config1_path
[256];
709 char config2_path
[256];
710 char command
[BUFSIZ
];
715 if (nb_write_config(config1
, format
, translator
, config1_path
,
716 sizeof(config1_path
))
718 vty_out(vty
, "%% Failed to process configurations.\n\n");
721 if (nb_write_config(config2
, format
, translator
, config2_path
,
722 sizeof(config2_path
))
724 vty_out(vty
, "%% Failed to process configurations.\n\n");
725 unlink(config1_path
);
729 snprintf(command
, sizeof(command
), "diff -u %s %s", config1_path
,
731 fp
= popen(command
, "r");
733 vty_out(vty
, "%% Failed to generate configuration diff.\n\n");
734 unlink(config1_path
);
735 unlink(config2_path
);
738 /* Print diff line by line. */
739 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
742 vty_out(vty
, "%s", line
);
746 unlink(config1_path
);
747 unlink(config2_path
);
752 /* Configure exclusively from this terminal. */
753 DEFUN (config_exclusive
,
754 config_exclusive_cmd
,
755 "configure exclusive",
756 "Configuration from vty interface\n"
757 "Configure exclusively from this terminal\n")
759 return vty_config_enter(vty
, true, true);
762 /* Configure using a private candidate configuration. */
763 DEFUN (config_private
,
766 "Configuration from vty interface\n"
767 "Configure using a private candidate configuration\n")
769 return vty_config_enter(vty
, true, false);
772 DEFPY (config_commit
,
774 "commit [{force$force|confirmed (1-60)}]",
775 "Commit changes into the running configuration\n"
776 "Force commit even if the candidate is outdated\n"
777 "Rollback this commit unless there is a confirming commit\n"
778 "Timeout in minutes for the commit to be confirmed\n")
780 return nb_cli_commit(vty
, !!force
, confirmed
, NULL
);
783 DEFPY (config_commit_comment
,
784 config_commit_comment_cmd
,
785 "commit [{force$force|confirmed (1-60)}] comment LINE...",
786 "Commit changes into the running configuration\n"
787 "Force commit even if the candidate is outdated\n"
788 "Rollback this commit unless there is a confirming commit\n"
789 "Timeout in minutes for the commit to be confirmed\n"
790 "Assign a comment to this commit\n"
791 "Comment for this commit (Max 80 characters)\n")
797 argv_find(argv
, argc
, "LINE", &idx
);
798 comment
= argv_concat(argv
, argc
, idx
);
799 ret
= nb_cli_commit(vty
, !!force
, confirmed
, comment
);
800 XFREE(MTYPE_TMP
, comment
);
805 DEFPY (config_commit_check
,
806 config_commit_check_cmd
,
808 "Commit changes into the running configuration\n"
809 "Check if the configuration changes are valid\n")
811 struct nb_context context
= {};
812 char errmsg
[BUFSIZ
] = {0};
815 context
.client
= NB_CLIENT_CLI
;
817 ret
= nb_candidate_validate(&context
, vty
->candidate_config
, errmsg
,
821 "%% Failed to validate candidate configuration.\n\n");
822 vty_show_nb_errors(vty
, ret
, errmsg
);
826 vty_out(vty
, "%% Candidate configuration validated successfully.\n\n");
831 DEFPY (config_update
,
834 "Update candidate configuration\n")
836 if (!nb_candidate_needs_update(vty
->candidate_config
)) {
837 vty_out(vty
, "%% Update is not necessary.\n\n");
841 if (nb_candidate_update(vty
->candidate_config
) != NB_OK
) {
843 "%% Failed to update the candidate configuration.\n\n");
844 vty_out(vty
, "Please check the logs for more details.\n");
848 nb_config_replace(vty
->candidate_config_base
, running_config
, true);
850 vty_out(vty
, "%% Candidate configuration updated successfully.\n\n");
855 DEFPY (config_discard
,
858 "Discard changes in the candidate configuration\n")
860 nb_config_replace(vty
->candidate_config
, vty
->candidate_config_base
,
870 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
871 |transaction (1-4294967295)$tid\
874 "Configuration related settings\n"
875 "Load configuration into candidate\n"
876 "Load configuration file into candidate\n"
877 "Load configuration file in JSON format\n"
878 "Load configuration file in XML format\n"
879 "Translate configuration file\n"
880 "YANG module translator\n"
881 "Configuration file name (full path)\n"
882 "Load configuration from transaction into candidate\n"
884 "Replace instead of merge\n")
887 enum nb_cfg_format format
;
888 struct yang_translator
*translator
= NULL
;
891 format
= NB_CFG_FMT_JSON
;
893 format
= NB_CFG_FMT_XML
;
895 format
= NB_CFG_FMT_CMDS
;
897 if (translator_family
) {
898 translator
= yang_translator_find(translator_family
);
901 "%% Module translator \"%s\" not found\n",
907 return nb_cli_candidate_load_file(vty
, format
, translator
,
908 filename
, !!replace
);
911 return nb_cli_candidate_load_transaction(vty
, tid
, !!replace
);
914 DEFPY (show_config_running
,
915 show_config_running_cmd
,
916 "show configuration running\
917 [<json$json|xml$xml> [translate WORD$translator_family]]\
918 [with-defaults$with_defaults]",
920 "Configuration information\n"
921 "Running configuration\n"
922 "Change output format to JSON\n"
923 "Change output format to XML\n"
925 "YANG module translator\n"
926 "Show default values\n")
929 enum nb_cfg_format format
;
930 struct yang_translator
*translator
= NULL
;
933 format
= NB_CFG_FMT_JSON
;
935 format
= NB_CFG_FMT_XML
;
937 format
= NB_CFG_FMT_CMDS
;
939 if (translator_family
) {
940 translator
= yang_translator_find(translator_family
);
942 vty_out(vty
, "%% Module translator \"%s\" not found\n",
948 nb_cli_show_config(vty
, running_config
, format
, translator
,
954 DEFPY (show_config_candidate
,
955 show_config_candidate_cmd
,
956 "show configuration candidate\
957 [<json$json|xml$xml> [translate WORD$translator_family]]\
959 with-defaults$with_defaults\
963 "Configuration information\n"
964 "Candidate configuration\n"
965 "Change output format to JSON\n"
966 "Change output format to XML\n"
968 "YANG module translator\n"
969 "Show default values\n"
970 "Show changes applied in the candidate configuration\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",
993 return nb_cli_show_config_compare(
994 vty
, vty
->candidate_config_base
, vty
->candidate_config
,
997 nb_cli_show_config(vty
, vty
->candidate_config
, format
, translator
,
1003 DEFPY (show_config_candidate_section
,
1004 show_config_candidate_section_cmd
,
1008 struct lyd_node
*dnode
;
1010 /* Top-level configuration node, display everything. */
1011 if (vty
->xpath_index
== 0)
1012 return nb_cli_show_config(vty
, vty
->candidate_config
,
1013 NB_CFG_FMT_CMDS
, NULL
, false);
1015 /* Display only the current section of the candidate configuration. */
1016 dnode
= yang_dnode_get(vty
->candidate_config
->dnode
, VTY_CURR_XPATH
);
1018 /* Shouldn't happen. */
1021 nb_cli_show_dnode_cmds(vty
, dnode
, 0);
1022 vty_out(vty
, "!\n");
1027 DEFPY (show_config_compare
,
1028 show_config_compare_cmd
,
1029 "show configuration compare\
1031 candidate$c1_candidate\
1032 |running$c1_running\
1033 |transaction (1-4294967295)$c1_tid\
1036 candidate$c2_candidate\
1037 |running$c2_running\
1038 |transaction (1-4294967295)$c2_tid\
1040 [<json$json|xml$xml> [translate WORD$translator_family]]",
1042 "Configuration information\n"
1043 "Compare two different configurations\n"
1044 "Candidate configuration\n"
1045 "Running configuration\n"
1046 "Configuration transaction\n"
1048 "Candidate configuration\n"
1049 "Running configuration\n"
1050 "Configuration transaction\n"
1052 "Change output format to JSON\n"
1053 "Change output format to XML\n"
1054 "Translate output\n"
1055 "YANG module translator\n")
1057 enum nb_cfg_format format
;
1058 struct yang_translator
*translator
= NULL
;
1059 struct nb_config
*config1
, *config_transaction1
= NULL
;
1060 struct nb_config
*config2
, *config_transaction2
= NULL
;
1061 int ret
= CMD_WARNING
;
1064 config1
= vty
->candidate_config
;
1065 else if (c1_running
)
1066 config1
= running_config
;
1068 config_transaction1
= nb_db_transaction_load(c1_tid
);
1069 if (!config_transaction1
) {
1070 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1071 (unsigned int)c1_tid
);
1074 config1
= config_transaction1
;
1078 config2
= vty
->candidate_config
;
1079 else if (c2_running
)
1080 config2
= running_config
;
1082 config_transaction2
= nb_db_transaction_load(c2_tid
);
1083 if (!config_transaction2
) {
1084 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1085 (unsigned int)c2_tid
);
1088 config2
= config_transaction2
;
1092 format
= NB_CFG_FMT_JSON
;
1094 format
= NB_CFG_FMT_XML
;
1096 format
= NB_CFG_FMT_CMDS
;
1098 if (translator_family
) {
1099 translator
= yang_translator_find(translator_family
);
1101 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1107 ret
= nb_cli_show_config_compare(vty
, config1
, config2
, format
,
1110 if (config_transaction1
)
1111 nb_config_free(config_transaction1
);
1112 if (config_transaction2
)
1113 nb_config_free(config_transaction2
);
1119 * Stripped down version of the "show configuration compare" command.
1120 * The "candidate" option is not present so the command can be installed in
1123 ALIAS (show_config_compare
,
1124 show_config_compare_without_candidate_cmd
,
1125 "show configuration compare\
1128 |transaction (1-4294967295)$c1_tid\
1132 |transaction (1-4294967295)$c2_tid\
1134 [<json$json|xml$xml> [translate WORD$translator_family]]",
1136 "Configuration information\n"
1137 "Compare two different configurations\n"
1138 "Running configuration\n"
1139 "Configuration transaction\n"
1141 "Running configuration\n"
1142 "Configuration transaction\n"
1144 "Change output format to JSON\n"
1145 "Change output format to XML\n"
1146 "Translate output\n"
1147 "YANG module translator\n")
1149 DEFPY (clear_config_transactions
,
1150 clear_config_transactions_cmd
,
1151 "clear configuration transactions oldest (1-100)$n",
1153 "Configuration activity\n"
1154 "Delete transactions from the transactions log\n"
1155 "Delete oldest <n> transactions\n"
1156 "Number of transactions to delete\n")
1158 #ifdef HAVE_CONFIG_ROLLBACKS
1159 if (nb_db_clear_transactions(n
) != NB_OK
) {
1160 vty_out(vty
, "%% Failed to delete transactions.\n\n");
1165 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1166 #endif /* HAVE_CONFIG_ROLLBACKS */
1171 DEFPY (config_database_max_transactions
,
1172 config_database_max_transactions_cmd
,
1173 "configuration database max-transactions (1-100)$max",
1174 "Configuration related settings\n"
1175 "Configuration database\n"
1176 "Set the maximum number of transactions to store\n"
1177 "Number of transactions\n")
1179 #ifdef HAVE_CONFIG_ROLLBACKS
1180 if (nb_db_set_max_transactions(max
) != NB_OK
) {
1182 "%% Failed to update the maximum number of transactions.\n\n");
1186 "%% Maximum number of transactions updated successfully.\n\n");
1189 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1190 #endif /* HAVE_CONFIG_ROLLBACKS */
1195 DEFPY (yang_module_translator_load
,
1196 yang_module_translator_load_cmd
,
1197 "yang module-translator load FILENAME$filename",
1198 "YANG related settings\n"
1199 "YANG module translator\n"
1200 "Load YANG module translator\n"
1201 "File name (full path)\n")
1203 struct yang_translator
*translator
;
1205 translator
= yang_translator_load(filename
);
1207 vty_out(vty
, "%% Failed to load \"%s\"\n\n", filename
);
1208 vty_out(vty
, "Please check the logs for more details.\n");
1212 vty_out(vty
, "%% Module translator \"%s\" loaded successfully.\n\n",
1213 translator
->family
);
1218 DEFPY (yang_module_translator_unload_family
,
1219 yang_module_translator_unload_cmd
,
1220 "yang module-translator unload WORD$translator_family",
1221 "YANG related settings\n"
1222 "YANG module translator\n"
1223 "Unload YANG module translator\n"
1224 "Name of the module translator\n")
1226 struct yang_translator
*translator
;
1228 translator
= yang_translator_find(translator_family
);
1230 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1235 yang_translator_unload(translator
);
1240 #ifdef HAVE_CONFIG_ROLLBACKS
1241 static void nb_cli_show_transactions_cb(void *arg
, int transaction_id
,
1242 const char *client_name
,
1243 const char *date
, const char *comment
)
1245 struct ttable
*tt
= arg
;
1247 ttable_add_row(tt
, "%d|%s|%s|%s", transaction_id
, client_name
, date
,
1251 static int nb_cli_show_transactions(struct vty
*vty
)
1255 /* Prepare table. */
1256 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1257 ttable_add_row(tt
, "Transaction ID|Client|Date|Comment");
1258 tt
->style
.cell
.rpad
= 2;
1259 tt
->style
.corner
= '+';
1261 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1263 /* Fetch transactions from the northbound database. */
1264 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb
, tt
)
1267 "%% Failed to fetch configuration transactions.\n");
1271 /* Dump the generated table. */
1272 if (tt
->nrows
> 1) {
1275 table
= ttable_dump(tt
, "\n");
1276 vty_out(vty
, "%s\n", table
);
1277 XFREE(MTYPE_TMP
, table
);
1279 vty_out(vty
, "No configuration transactions to display.\n\n");
1285 #endif /* HAVE_CONFIG_ROLLBACKS */
1287 DEFPY (show_config_transaction
,
1288 show_config_transaction_cmd
,
1289 "show configuration transaction\
1291 (1-4294967295)$transaction_id\
1292 [<json$json|xml$xml> [translate WORD$translator_family]]\
1294 with-defaults$with_defaults\
1299 "Configuration information\n"
1300 "Configuration transaction\n"
1302 "Change output format to JSON\n"
1303 "Change output format to XML\n"
1304 "Translate output\n"
1305 "YANG module translator\n"
1306 "Show default values\n"
1307 "Show changes compared to the previous transaction\n")
1309 #ifdef HAVE_CONFIG_ROLLBACKS
1310 if (transaction_id
) {
1311 struct nb_config
*config
;
1312 enum nb_cfg_format format
;
1313 struct yang_translator
*translator
= NULL
;
1316 format
= NB_CFG_FMT_JSON
;
1318 format
= NB_CFG_FMT_XML
;
1320 format
= NB_CFG_FMT_CMDS
;
1322 if (translator_family
) {
1323 translator
= yang_translator_find(translator_family
);
1326 "%% Module translator \"%s\" not found\n",
1332 config
= nb_db_transaction_load(transaction_id
);
1334 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1335 (unsigned int)transaction_id
);
1340 struct nb_config
*prev_config
;
1343 /* NOTE: this can be NULL. */
1345 nb_db_transaction_load(transaction_id
- 1);
1347 ret
= nb_cli_show_config_compare(
1348 vty
, prev_config
, config
, format
, translator
);
1350 nb_config_free(prev_config
);
1351 nb_config_free(config
);
1356 nb_cli_show_config(vty
, config
, format
, translator
,
1358 nb_config_free(config
);
1363 return nb_cli_show_transactions(vty
);
1366 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1368 #endif /* HAVE_CONFIG_ROLLBACKS */
1371 static int nb_cli_oper_data_cb(const struct lysc_node
*snode
,
1372 struct yang_translator
*translator
,
1373 struct yang_data
*data
, void *arg
)
1375 struct lyd_node
*dnode
= arg
;
1376 struct ly_ctx
*ly_ctx
;
1381 ret
= yang_translate_xpath(translator
,
1382 YANG_TRANSLATE_FROM_NATIVE
,
1383 data
->xpath
, sizeof(data
->xpath
));
1385 case YANG_TRANSLATE_SUCCESS
:
1387 case YANG_TRANSLATE_NOTFOUND
:
1389 case YANG_TRANSLATE_FAILURE
:
1393 ly_ctx
= translator
->ly_ctx
;
1395 ly_ctx
= ly_native_ctx
;
1398 lyd_new_path(dnode
, ly_ctx
, data
->xpath
, (void *)data
->value
,
1399 LYD_NEW_PATH_UPDATE
, &dnode
);
1401 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_new_path(%s) failed: %s",
1402 __func__
, data
->xpath
, ly_errmsg(ly_native_ctx
));
1407 yang_data_free(data
);
1411 yang_data_free(data
);
1415 DEFPY (show_yang_operational_data
,
1416 show_yang_operational_data_cmd
,
1417 "show yang operational-data XPATH$xpath\
1419 format <json$json|xml$xml>\
1420 |translate WORD$translator_family\
1421 |with-config$with_config\
1424 "YANG information\n"
1425 "Show YANG operational data\n"
1426 "XPath expression specifying the YANG data path\n"
1427 "Set the output format\n"
1428 "JavaScript Object Notation\n"
1429 "Extensible Markup Language\n"
1430 "Translate operational data\n"
1431 "YANG module translator\n"
1432 "Merge configuration data\n")
1435 struct yang_translator
*translator
= NULL
;
1436 struct ly_ctx
*ly_ctx
;
1437 struct lyd_node
*dnode
;
1439 uint32_t print_options
= LYD_PRINT_WITHSIBLINGS
;
1447 if (translator_family
) {
1448 translator
= yang_translator_find(translator_family
);
1450 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1455 ly_ctx
= translator
->ly_ctx
;
1457 ly_ctx
= ly_native_ctx
;
1460 dnode
= yang_dnode_new(ly_ctx
, false);
1461 ret
= nb_oper_data_iterate(xpath
, translator
, 0, nb_cli_oper_data_cb
,
1464 if (format
== LYD_JSON
)
1465 vty_out(vty
, "{}\n");
1467 /* embed ly_last_errmsg() when we get newer libyang */
1468 vty_out(vty
, "<!-- Not found -->\n");
1470 yang_dnode_free(dnode
);
1474 if (with_config
&& yang_dnode_exists(running_config
->dnode
, xpath
)) {
1475 struct lyd_node
*config_dnode
=
1476 yang_dnode_get(running_config
->dnode
, xpath
);
1477 if (config_dnode
!= NULL
) {
1478 lyd_merge_tree(&dnode
, yang_dnode_dup(config_dnode
),
1479 LYD_MERGE_DESTRUCT
);
1480 print_options
|= LYD_PRINT_WD_ALL
;
1484 (void)lyd_validate_all(&dnode
, ly_ctx
, 0, NULL
);
1486 /* Display the data. */
1487 if (lyd_print_mem(&strp
, dnode
, format
, print_options
) != 0 || !strp
) {
1488 vty_out(vty
, "%% Failed to display operational data.\n");
1489 yang_dnode_free(dnode
);
1492 vty_out(vty
, "%s", strp
);
1494 yang_dnode_free(dnode
);
1499 DEFPY (show_yang_module
,
1500 show_yang_module_cmd
,
1501 "show yang module [module-translator WORD$translator_family]",
1503 "YANG information\n"
1504 "Show loaded modules\n"
1505 "YANG module translator\n"
1506 "YANG module translator\n")
1508 struct ly_ctx
*ly_ctx
;
1509 struct yang_translator
*translator
= NULL
;
1510 const struct lys_module
*module
;
1514 if (translator_family
) {
1515 translator
= yang_translator_find(translator_family
);
1517 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1521 ly_ctx
= translator
->ly_ctx
;
1523 ly_ctx
= ly_native_ctx
;
1525 /* Prepare table. */
1526 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1527 ttable_add_row(tt
, "Module|Version|Revision|Flags|Namespace");
1528 tt
->style
.cell
.rpad
= 2;
1529 tt
->style
.corner
= '+';
1531 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1533 while ((module
= ly_ctx_get_module_iter(ly_ctx
, &idx
))) {
1536 snprintf(flags
, sizeof(flags
), "%c%c",
1537 module
->implemented
? 'I' : ' ',
1538 LY_ARRAY_COUNT(module
->deviated_by
) ? 'D' : ' ');
1540 ttable_add_row(tt
, "%s|%s|%s|%s|%s", module
->name
,
1541 (module
->parsed
->version
== 2) ? "1.1" : "1.0",
1542 module
->revision
? module
->revision
: "-", flags
,
1546 /* Dump the generated table. */
1547 if (tt
->nrows
> 1) {
1550 vty_out(vty
, " Flags: I - Implemented, D - Deviated\n\n");
1552 table
= ttable_dump(tt
, "\n");
1553 vty_out(vty
, "%s\n", table
);
1554 XFREE(MTYPE_TMP
, table
);
1556 vty_out(vty
, "No YANG modules to display.\n\n");
1563 DEFPY(show_yang_module_detail
, show_yang_module_detail_cmd
,
1565 [module-translator WORD$translator_family]\
1566 WORD$module_name <compiled$compiled|summary|tree$tree|yang$yang|yin$yin>",
1568 "YANG information\n"
1569 "Show loaded modules\n"
1570 "YANG module translator\n"
1571 "YANG module translator\n"
1573 "Display compiled module in YANG format\n"
1574 "Display summary information about the module\n"
1575 "Display module in the tree (RFC 8340) format\n"
1576 "Display module in the YANG format\n"
1577 "Display module in the YIN format\n")
1579 struct ly_ctx
*ly_ctx
;
1580 struct yang_translator
*translator
= NULL
;
1581 const struct lys_module
*module
;
1582 LYS_OUTFORMAT format
;
1585 if (translator_family
) {
1586 translator
= yang_translator_find(translator_family
);
1588 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1592 ly_ctx
= translator
->ly_ctx
;
1594 ly_ctx
= ly_native_ctx
;
1596 module
= ly_ctx_get_module_latest(ly_ctx
, module_name
);
1598 vty_out(vty
, "%% Module \"%s\" not found\n", module_name
);
1603 format
= LYS_OUT_YANG
;
1605 format
= LYS_OUT_YIN
;
1607 format
= LYS_OUT_YANG_COMPILED
;
1609 format
= LYS_OUT_TREE
;
1612 "%% libyang v2 does not currently support summary\n");
1616 if (lys_print_mem(&strp
, module
, format
, 0) == 0) {
1617 vty_out(vty
, "%s\n", strp
);
1621 vty_out(vty
, "%% Error generating module information\n");
1628 DEFPY (show_yang_module_translator
,
1629 show_yang_module_translator_cmd
,
1630 "show yang module-translator",
1632 "YANG information\n"
1633 "Show loaded YANG module translators\n")
1635 struct yang_translator
*translator
;
1638 /* Prepare table. */
1639 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1640 ttable_add_row(tt
, "Family|Module|Deviations|Coverage (%%)");
1641 tt
->style
.cell
.rpad
= 2;
1642 tt
->style
.corner
= '+';
1644 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1646 RB_FOREACH (translator
, yang_translators
, &yang_translators
) {
1647 struct yang_tmodule
*tmodule
;
1648 struct listnode
*ln
;
1650 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
1651 ttable_add_row(tt
, "%s|%s|%s|%.2f", translator
->family
,
1652 tmodule
->module
->name
,
1653 tmodule
->deviations
->name
,
1658 /* Dump the generated table. */
1659 if (tt
->nrows
> 1) {
1662 table
= ttable_dump(tt
, "\n");
1663 vty_out(vty
, "%s\n", table
);
1664 XFREE(MTYPE_TMP
, table
);
1666 vty_out(vty
, "No YANG module translators to display.\n\n");
1673 #ifdef HAVE_CONFIG_ROLLBACKS
1674 static int nb_cli_rollback_configuration(struct vty
*vty
,
1675 uint32_t transaction_id
)
1677 struct nb_context context
= {};
1678 struct nb_config
*candidate
;
1680 char errmsg
[BUFSIZ
] = {0};
1683 candidate
= nb_db_transaction_load(transaction_id
);
1685 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1690 snprintf(comment
, sizeof(comment
), "Rollback to transaction %u",
1693 context
.client
= NB_CLIENT_CLI
;
1695 ret
= nb_candidate_commit(context
, candidate
, true, comment
, NULL
,
1696 errmsg
, sizeof(errmsg
));
1697 nb_config_free(candidate
);
1701 "%% Configuration was successfully rolled back.\n\n");
1702 /* Print warnings (if any). */
1703 if (strlen(errmsg
) > 0)
1704 vty_out(vty
, "%s\n", errmsg
);
1706 case NB_ERR_NO_CHANGES
:
1708 "%% Aborting - no configuration changes detected.\n\n");
1711 vty_out(vty
, "%% Rollback failed.\n\n");
1712 vty_show_nb_errors(vty
, ret
, errmsg
);
1716 #endif /* HAVE_CONFIG_ROLLBACKS */
1718 DEFPY (rollback_config
,
1719 rollback_config_cmd
,
1720 "rollback configuration (1-4294967295)$transaction_id",
1721 "Rollback to a previous state\n"
1722 "Running configuration\n"
1725 #ifdef HAVE_CONFIG_ROLLBACKS
1726 return nb_cli_rollback_configuration(vty
, transaction_id
);
1729 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1731 #endif /* HAVE_CONFIG_ROLLBACKS */
1734 /* Debug CLI commands. */
1735 static struct debug
*nb_debugs
[] = {
1736 &nb_dbg_cbs_config
, &nb_dbg_cbs_state
, &nb_dbg_cbs_rpc
,
1737 &nb_dbg_notif
, &nb_dbg_events
, &nb_dbg_libyang
,
1740 static const char *const nb_debugs_conflines
[] = {
1741 "debug northbound callbacks configuration",
1742 "debug northbound callbacks state",
1743 "debug northbound callbacks rpc",
1744 "debug northbound notifications",
1745 "debug northbound events",
1746 "debug northbound libyang",
1749 DEFINE_HOOK(nb_client_debug_set_all
, (uint32_t flags
, bool set
), (flags
, set
));
1751 static void nb_debug_set_all(uint32_t flags
, bool set
)
1753 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++) {
1754 DEBUG_FLAGS_SET(nb_debugs
[i
], flags
, set
);
1756 /* If all modes have been turned off, don't preserve options. */
1757 if (!DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_ALL
))
1758 DEBUG_CLEAR(nb_debugs
[i
]);
1761 hook_call(nb_client_debug_set_all
, flags
, set
);
1766 "[no] debug northbound\
1768 callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\
1769 |notifications$notifications\
1775 "Northbound debugging\n"
1782 "libyang debugging\n")
1784 uint32_t mode
= DEBUG_NODE2MODE(vty
->node
);
1787 bool none
= (!cbs_cfg
&& !cbs_state
&& !cbs_rpc
);
1789 if (none
|| cbs_cfg
)
1790 DEBUG_MODE_SET(&nb_dbg_cbs_config
, mode
, !no
);
1791 if (none
|| cbs_state
)
1792 DEBUG_MODE_SET(&nb_dbg_cbs_state
, mode
, !no
);
1793 if (none
|| cbs_rpc
)
1794 DEBUG_MODE_SET(&nb_dbg_cbs_rpc
, mode
, !no
);
1797 DEBUG_MODE_SET(&nb_dbg_notif
, mode
, !no
);
1799 DEBUG_MODE_SET(&nb_dbg_events
, mode
, !no
);
1801 DEBUG_MODE_SET(&nb_dbg_libyang
, mode
, !no
);
1802 yang_debugging_set(!no
);
1805 /* no specific debug --> act on all of them */
1806 if (strmatch(argv
[argc
- 1]->text
, "northbound")) {
1807 nb_debug_set_all(mode
, !no
);
1808 yang_debugging_set(!no
);
1814 DEFINE_HOOK(nb_client_debug_config_write
, (struct vty
*vty
), (vty
));
1816 static int nb_debug_config_write(struct vty
*vty
)
1818 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++)
1819 if (DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_CONF
))
1820 vty_out(vty
, "%s\n", nb_debugs_conflines
[i
]);
1822 hook_call(nb_client_debug_config_write
, vty
);
1827 static struct debug_callbacks nb_dbg_cbs
= {.debug_set_all
= nb_debug_set_all
};
1828 static struct cmd_node nb_debug_node
= {
1829 .name
= "northbound debug",
1830 .node
= NORTHBOUND_DEBUG_NODE
,
1832 .config_write
= nb_debug_config_write
,
1835 void nb_cli_install_default(int node
)
1837 _install_element(node
, &show_config_candidate_section_cmd
);
1839 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL
)
1842 _install_element(node
, &config_commit_cmd
);
1843 _install_element(node
, &config_commit_comment_cmd
);
1844 _install_element(node
, &config_commit_check_cmd
);
1845 _install_element(node
, &config_update_cmd
);
1846 _install_element(node
, &config_discard_cmd
);
1847 _install_element(node
, &show_config_running_cmd
);
1848 _install_element(node
, &show_config_candidate_cmd
);
1849 _install_element(node
, &show_config_compare_cmd
);
1850 _install_element(node
, &show_config_transaction_cmd
);
1853 /* YANG module autocomplete. */
1854 static void yang_module_autocomplete(vector comps
, struct cmd_token
*token
)
1856 const struct lys_module
*module
;
1857 struct yang_translator
*module_tr
;
1861 while ((module
= ly_ctx_get_module_iter(ly_native_ctx
, &idx
)))
1862 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1864 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
) {
1866 while ((module
= ly_ctx_get_module_iter(module_tr
->ly_ctx
,
1869 XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1873 /* YANG module translator autocomplete. */
1874 static void yang_translator_autocomplete(vector comps
, struct cmd_token
*token
)
1876 struct yang_translator
*module_tr
;
1878 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
)
1879 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module_tr
->family
));
1882 static const struct cmd_variable_handler yang_var_handlers
[] = {
1883 {.varname
= "module_name", .completions
= yang_module_autocomplete
},
1884 {.varname
= "translator_family",
1885 .completions
= yang_translator_autocomplete
},
1886 {.completions
= NULL
}};
1888 void nb_cli_init(struct event_loop
*tm
)
1892 /* Initialize the shared candidate configuration. */
1893 vty_shared_candidate_config
= nb_config_new(NULL
);
1895 debug_init(&nb_dbg_cbs
);
1897 install_node(&nb_debug_node
);
1898 install_element(ENABLE_NODE
, &debug_nb_cmd
);
1899 install_element(CONFIG_NODE
, &debug_nb_cmd
);
1901 /* Install commands specific to the transaction-base mode. */
1902 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL
) {
1903 install_element(ENABLE_NODE
, &config_exclusive_cmd
);
1904 install_element(ENABLE_NODE
, &config_private_cmd
);
1905 install_element(ENABLE_NODE
,
1906 &show_config_compare_without_candidate_cmd
);
1907 install_element(ENABLE_NODE
, &show_config_transaction_cmd
);
1908 install_element(ENABLE_NODE
, &rollback_config_cmd
);
1909 install_element(ENABLE_NODE
, &clear_config_transactions_cmd
);
1911 install_element(CONFIG_NODE
, &config_load_cmd
);
1912 install_element(CONFIG_NODE
,
1913 &config_database_max_transactions_cmd
);
1916 /* Other commands. */
1917 install_element(ENABLE_NODE
, &show_config_running_cmd
);
1918 install_element(CONFIG_NODE
, &yang_module_translator_load_cmd
);
1919 install_element(CONFIG_NODE
, &yang_module_translator_unload_cmd
);
1920 install_element(ENABLE_NODE
, &show_yang_operational_data_cmd
);
1921 install_element(ENABLE_NODE
, &show_yang_module_cmd
);
1922 install_element(ENABLE_NODE
, &show_yang_module_detail_cmd
);
1923 install_element(ENABLE_NODE
, &show_yang_module_translator_cmd
);
1924 cmd_variable_handler_register(yang_var_handlers
);
1927 void nb_cli_terminate(void)
1929 nb_config_free(vty_shared_candidate_config
);