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_fe_enabled()) {
201 implicit_commit
= vty_needs_implicit_commit(vty
);
202 ret
= vty_mgmt_send_config_data(vty
);
203 if (ret
>= 0 && !implicit_commit
)
204 vty
->mgmt_num_pending_setcfg
++;
208 return nb_cli_apply_changes_internal(vty
, xpath_base
, false);
211 int nb_cli_apply_changes_clear_pending(struct vty
*vty
,
212 const char *xpath_base_fmt
, ...)
214 char xpath_base
[XPATH_MAXLEN
] = {};
215 bool implicit_commit
;
218 /* Parse the base XPath format string. */
219 if (xpath_base_fmt
) {
222 va_start(ap
, xpath_base_fmt
);
223 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
227 if (vty_mgmt_fe_enabled()) {
230 implicit_commit
= vty_needs_implicit_commit(vty
);
231 ret
= vty_mgmt_send_config_data(vty
);
232 if (ret
>= 0 && !implicit_commit
)
233 vty
->mgmt_num_pending_setcfg
++;
237 return nb_cli_apply_changes_internal(vty
, xpath_base
, true);
240 int nb_cli_rpc(struct vty
*vty
, const char *xpath
, struct list
*input
,
243 struct nb_node
*nb_node
;
245 char errmsg
[BUFSIZ
] = {0};
247 nb_node
= nb_node_find(xpath
);
249 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
250 "%s: unknown data path: %s", __func__
, xpath
);
254 ret
= nb_callback_rpc(nb_node
, xpath
, input
, output
, errmsg
,
261 vty_show_nb_errors(vty
, ret
, errmsg
);
266 void nb_cli_confirmed_commit_clean(struct vty
*vty
)
268 event_cancel(&vty
->t_confirmed_commit_timeout
);
269 nb_config_free(vty
->confirmed_commit_rollback
);
270 vty
->confirmed_commit_rollback
= NULL
;
273 int nb_cli_confirmed_commit_rollback(struct vty
*vty
)
275 struct nb_context context
= {};
276 uint32_t transaction_id
;
277 char errmsg
[BUFSIZ
] = {0};
280 /* Perform the rollback. */
281 context
.client
= NB_CLIENT_CLI
;
283 ret
= nb_candidate_commit(
284 context
, vty
->confirmed_commit_rollback
, true,
285 "Rollback to previous configuration - confirmed commit has timed out",
286 &transaction_id
, errmsg
, sizeof(errmsg
));
289 "Rollback performed successfully (Transaction ID #%u).\n",
291 /* Print warnings (if any). */
292 if (strlen(errmsg
) > 0)
293 vty_out(vty
, "%s\n", errmsg
);
296 "Failed to rollback to previous configuration.\n\n");
297 vty_show_nb_errors(vty
, ret
, errmsg
);
303 static void nb_cli_confirmed_commit_timeout(struct event
*thread
)
305 struct vty
*vty
= EVENT_ARG(thread
);
307 /* XXX: broadcast this message to all logged-in users? */
309 "\nConfirmed commit has timed out, rolling back to previous configuration.\n\n");
311 nb_cli_confirmed_commit_rollback(vty
);
312 nb_cli_confirmed_commit_clean(vty
);
315 static int nb_cli_commit(struct vty
*vty
, bool force
,
316 unsigned int confirmed_timeout
, char *comment
)
318 struct nb_context context
= {};
319 uint32_t transaction_id
= 0;
320 char errmsg
[BUFSIZ
] = {0};
323 /* Check if there's a pending confirmed commit. */
324 if (vty
->t_confirmed_commit_timeout
) {
325 if (confirmed_timeout
) {
326 /* Reset timeout if "commit confirmed" is used again. */
328 "%% Resetting confirmed-commit timeout to %u minute(s)\n\n",
331 event_cancel(&vty
->t_confirmed_commit_timeout
);
332 event_add_timer(master
, nb_cli_confirmed_commit_timeout
,
333 vty
, confirmed_timeout
* 60,
334 &vty
->t_confirmed_commit_timeout
);
336 /* Accept commit confirmation. */
337 vty_out(vty
, "%% Commit complete.\n\n");
338 nb_cli_confirmed_commit_clean(vty
);
343 /* "force" parameter. */
344 if (!force
&& nb_candidate_needs_update(vty
->candidate_config
)) {
346 "%% Candidate configuration needs to be updated before commit.\n\n");
348 "Use the \"update\" command or \"commit force\".\n");
352 /* "confirm" parameter. */
353 if (confirmed_timeout
) {
354 vty
->confirmed_commit_rollback
= nb_config_dup(running_config
);
356 vty
->t_confirmed_commit_timeout
= NULL
;
357 event_add_timer(master
, nb_cli_confirmed_commit_timeout
, vty
,
358 confirmed_timeout
* 60,
359 &vty
->t_confirmed_commit_timeout
);
362 context
.client
= NB_CLIENT_CLI
;
364 ret
= nb_candidate_commit(context
, vty
->candidate_config
, true, comment
,
365 &transaction_id
, errmsg
, sizeof(errmsg
));
367 /* Map northbound return code to CLI return code. */
370 nb_config_replace(vty
->candidate_config_base
, running_config
,
373 "%% Configuration committed successfully (Transaction ID #%u).\n\n",
375 /* Print warnings (if any). */
376 if (strlen(errmsg
) > 0)
377 vty_out(vty
, "%s\n", errmsg
);
379 case NB_ERR_NO_CHANGES
:
380 vty_out(vty
, "%% No configuration changes to commit.\n\n");
384 "%% Failed to commit candidate configuration.\n\n");
385 vty_show_nb_errors(vty
, ret
, errmsg
);
390 static int nb_cli_candidate_load_file(struct vty
*vty
,
391 enum nb_cfg_format format
,
392 struct yang_translator
*translator
,
393 const char *path
, bool replace
)
395 struct nb_config
*loaded_config
= NULL
;
396 struct lyd_node
*dnode
;
397 struct ly_ctx
*ly_ctx
;
403 case NB_CFG_FMT_CMDS
:
404 loaded_config
= nb_config_new(NULL
);
405 if (!vty_read_config(loaded_config
, path
, config_default
)) {
406 vty_out(vty
, "%% Failed to load configuration.\n\n");
408 "Please check the logs for more details.\n");
409 nb_config_free(loaded_config
);
413 case NB_CFG_FMT_JSON
:
415 ly_format
= (format
== NB_CFG_FMT_JSON
) ? LYD_JSON
: LYD_XML
;
417 ly_ctx
= translator
? translator
->ly_ctx
: ly_native_ctx
;
418 err
= lyd_parse_data_path(ly_ctx
, path
, ly_format
,
419 LYD_PARSE_ONLY
| LYD_PARSE_NO_STATE
,
422 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_path() failed",
424 vty_out(vty
, "%% Failed to load configuration:\n\n");
426 yang_print_errors(ly_native_ctx
, buf
,
431 && yang_translate_dnode(translator
,
432 YANG_TRANSLATE_TO_NATIVE
, &dnode
)
433 != YANG_TRANSLATE_SUCCESS
) {
434 vty_out(vty
, "%% Failed to translate configuration\n");
435 yang_dnode_free(dnode
);
438 loaded_config
= nb_config_new(dnode
);
443 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
444 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
447 "%% Failed to merge the loaded configuration:\n\n");
449 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
456 static int nb_cli_candidate_load_transaction(struct vty
*vty
,
457 uint32_t transaction_id
,
460 struct nb_config
*loaded_config
;
463 loaded_config
= nb_db_transaction_load(transaction_id
);
464 if (!loaded_config
) {
465 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
471 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
472 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
475 "%% Failed to merge the loaded configuration:\n\n");
477 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
484 /* Prepare the configuration for display. */
485 void nb_cli_show_config_prepare(struct nb_config
*config
, bool with_defaults
)
487 /* Nothing to do for daemons that don't implement any YANG module. */
488 if (config
->dnode
== NULL
)
492 * Call lyd_validate() only to create default child nodes, ignoring
493 * any possible validation error. This doesn't need to be done when
494 * displaying the running configuration since it's always fully
497 if (config
!= running_config
)
498 (void)lyd_validate_all(&config
->dnode
, ly_native_ctx
,
499 LYD_VALIDATE_NO_STATE
, NULL
);
502 static int lyd_node_cmp(const struct lyd_node
**dnode1
,
503 const struct lyd_node
**dnode2
)
505 struct nb_node
*nb_node
= (*dnode1
)->schema
->priv
;
507 return nb_node
->cbs
.cli_cmp(*dnode1
, *dnode2
);
510 static void show_dnode_children_cmds(struct vty
*vty
,
511 const struct lyd_node
*root
,
514 struct nb_node
*nb_node
, *sort_node
= NULL
;
515 struct listnode
*listnode
;
516 struct lyd_node
*child
;
517 struct list
*sort_list
;
520 LY_LIST_FOR (lyd_child(root
), child
) {
521 nb_node
= child
->schema
->priv
;
524 * We finished processing current list,
525 * it's time to print the config.
527 if (sort_node
&& nb_node
!= sort_node
) {
529 (int (*)(const void **,
530 const void **))lyd_node_cmp
);
532 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
533 nb_cli_show_dnode_cmds(vty
, data
,
536 list_delete(&sort_list
);
541 * If the config needs to be sorted,
542 * then add the dnode to the sorting
543 * list for later processing.
545 if (nb_node
&& nb_node
->cbs
.cli_cmp
) {
548 sort_list
= list_new();
551 listnode_add(sort_list
, child
);
555 nb_cli_show_dnode_cmds(vty
, child
, with_defaults
);
560 (int (*)(const void **, const void **))lyd_node_cmp
);
562 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
563 nb_cli_show_dnode_cmds(vty
, data
, with_defaults
);
565 list_delete(&sort_list
);
570 void nb_cli_show_dnode_cmds(struct vty
*vty
, const struct lyd_node
*root
,
573 struct nb_node
*nb_node
;
575 if (!with_defaults
&& yang_dnode_is_default_recursive(root
))
578 nb_node
= root
->schema
->priv
;
580 if (nb_node
&& nb_node
->cbs
.cli_show
)
581 (*nb_node
->cbs
.cli_show
)(vty
, root
, with_defaults
);
583 if (!(root
->schema
->nodetype
& (LYS_LEAF
| LYS_LEAFLIST
| LYS_ANYDATA
)))
584 show_dnode_children_cmds(vty
, root
, with_defaults
);
586 if (nb_node
&& nb_node
->cbs
.cli_show_end
)
587 (*nb_node
->cbs
.cli_show_end
)(vty
, root
);
590 static void nb_cli_show_config_cmds(struct vty
*vty
, struct nb_config
*config
,
593 struct lyd_node
*root
;
595 vty_out(vty
, "Configuration:\n");
597 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
598 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
600 LY_LIST_FOR (config
->dnode
, root
) {
601 nb_cli_show_dnode_cmds(vty
, root
, with_defaults
);
605 vty_out(vty
, "end\n");
608 static int nb_cli_show_config_libyang(struct vty
*vty
, LYD_FORMAT format
,
609 struct nb_config
*config
,
610 struct yang_translator
*translator
,
613 struct lyd_node
*dnode
;
617 dnode
= yang_dnode_dup(config
->dnode
);
619 && yang_translate_dnode(translator
, YANG_TRANSLATE_FROM_NATIVE
,
621 != YANG_TRANSLATE_SUCCESS
) {
622 vty_out(vty
, "%% Failed to translate configuration\n");
623 yang_dnode_free(dnode
);
627 SET_FLAG(options
, LYD_PRINT_WITHSIBLINGS
);
629 SET_FLAG(options
, LYD_PRINT_WD_ALL
);
631 SET_FLAG(options
, LYD_PRINT_WD_TRIM
);
633 if (lyd_print_mem(&strp
, dnode
, format
, options
) == 0 && strp
) {
634 vty_out(vty
, "%s", strp
);
638 yang_dnode_free(dnode
);
643 static int nb_cli_show_config(struct vty
*vty
, struct nb_config
*config
,
644 enum nb_cfg_format format
,
645 struct yang_translator
*translator
,
648 nb_cli_show_config_prepare(config
, with_defaults
);
651 case NB_CFG_FMT_CMDS
:
652 nb_cli_show_config_cmds(vty
, config
, with_defaults
);
654 case NB_CFG_FMT_JSON
:
655 return nb_cli_show_config_libyang(vty
, LYD_JSON
, config
,
656 translator
, with_defaults
);
658 return nb_cli_show_config_libyang(vty
, LYD_XML
, config
,
659 translator
, with_defaults
);
665 static int nb_write_config(struct nb_config
*config
, enum nb_cfg_format format
,
666 struct yang_translator
*translator
, char *path
,
670 struct vty
*file_vty
;
673 snprintf(path
, pathlen
, "/tmp/frr.tmp.XXXXXXXX");
676 flog_warn(EC_LIB_SYSTEM_CALL
, "%s: mkstemp() failed: %s",
677 __func__
, safe_strerror(errno
));
680 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
681 flog_warn(EC_LIB_SYSTEM_CALL
,
682 "%s: fchmod() failed: %s(%d):", __func__
,
683 safe_strerror(errno
), errno
);
687 /* Make vty for configuration file. */
688 file_vty
= vty_new();
690 file_vty
->type
= VTY_FILE
;
692 ret
= nb_cli_show_config(file_vty
, config
, format
, translator
,
699 static int nb_cli_show_config_compare(struct vty
*vty
,
700 struct nb_config
*config1
,
701 struct nb_config
*config2
,
702 enum nb_cfg_format format
,
703 struct yang_translator
*translator
)
705 char config1_path
[256];
706 char config2_path
[256];
707 char command
[BUFSIZ
];
712 if (nb_write_config(config1
, format
, translator
, config1_path
,
713 sizeof(config1_path
))
715 vty_out(vty
, "%% Failed to process configurations.\n\n");
718 if (nb_write_config(config2
, format
, translator
, config2_path
,
719 sizeof(config2_path
))
721 vty_out(vty
, "%% Failed to process configurations.\n\n");
722 unlink(config1_path
);
726 snprintf(command
, sizeof(command
), "diff -u %s %s", config1_path
,
728 fp
= popen(command
, "r");
730 vty_out(vty
, "%% Failed to generate configuration diff.\n\n");
731 unlink(config1_path
);
732 unlink(config2_path
);
735 /* Print diff line by line. */
736 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
739 vty_out(vty
, "%s", line
);
743 unlink(config1_path
);
744 unlink(config2_path
);
749 /* Configure exclusively from this terminal. */
750 DEFUN (config_exclusive
,
751 config_exclusive_cmd
,
752 "configure exclusive",
753 "Configuration from vty interface\n"
754 "Configure exclusively from this terminal\n")
756 return vty_config_enter(vty
, true, true);
759 /* Configure using a private candidate configuration. */
760 DEFUN (config_private
,
763 "Configuration from vty interface\n"
764 "Configure using a private candidate configuration\n")
766 return vty_config_enter(vty
, true, false);
769 DEFPY (config_commit
,
771 "commit [{force$force|confirmed (1-60)}]",
772 "Commit changes into the running configuration\n"
773 "Force commit even if the candidate is outdated\n"
774 "Rollback this commit unless there is a confirming commit\n"
775 "Timeout in minutes for the commit to be confirmed\n")
777 return nb_cli_commit(vty
, !!force
, confirmed
, NULL
);
780 DEFPY (config_commit_comment
,
781 config_commit_comment_cmd
,
782 "commit [{force$force|confirmed (1-60)}] comment LINE...",
783 "Commit changes into the running configuration\n"
784 "Force commit even if the candidate is outdated\n"
785 "Rollback this commit unless there is a confirming commit\n"
786 "Timeout in minutes for the commit to be confirmed\n"
787 "Assign a comment to this commit\n"
788 "Comment for this commit (Max 80 characters)\n")
794 argv_find(argv
, argc
, "LINE", &idx
);
795 comment
= argv_concat(argv
, argc
, idx
);
796 ret
= nb_cli_commit(vty
, !!force
, confirmed
, comment
);
797 XFREE(MTYPE_TMP
, comment
);
802 DEFPY (config_commit_check
,
803 config_commit_check_cmd
,
805 "Commit changes into the running configuration\n"
806 "Check if the configuration changes are valid\n")
808 struct nb_context context
= {};
809 char errmsg
[BUFSIZ
] = {0};
812 context
.client
= NB_CLIENT_CLI
;
814 ret
= nb_candidate_validate(&context
, vty
->candidate_config
, errmsg
,
818 "%% Failed to validate candidate configuration.\n\n");
819 vty_show_nb_errors(vty
, ret
, errmsg
);
823 vty_out(vty
, "%% Candidate configuration validated successfully.\n\n");
828 DEFPY (config_update
,
831 "Update candidate configuration\n")
833 if (!nb_candidate_needs_update(vty
->candidate_config
)) {
834 vty_out(vty
, "%% Update is not necessary.\n\n");
838 if (nb_candidate_update(vty
->candidate_config
) != NB_OK
) {
840 "%% Failed to update the candidate configuration.\n\n");
841 vty_out(vty
, "Please check the logs for more details.\n");
845 nb_config_replace(vty
->candidate_config_base
, running_config
, true);
847 vty_out(vty
, "%% Candidate configuration updated successfully.\n\n");
852 DEFPY (config_discard
,
855 "Discard changes in the candidate configuration\n")
857 nb_config_replace(vty
->candidate_config
, vty
->candidate_config_base
,
867 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
868 |transaction (1-4294967295)$tid\
871 "Configuration related settings\n"
872 "Load configuration into candidate\n"
873 "Load configuration file into candidate\n"
874 "Load configuration file in JSON format\n"
875 "Load configuration file in XML format\n"
876 "Translate configuration file\n"
877 "YANG module translator\n"
878 "Configuration file name (full path)\n"
879 "Load configuration from transaction into candidate\n"
881 "Replace instead of merge\n")
884 enum nb_cfg_format format
;
885 struct yang_translator
*translator
= NULL
;
888 format
= NB_CFG_FMT_JSON
;
890 format
= NB_CFG_FMT_XML
;
892 format
= NB_CFG_FMT_CMDS
;
894 if (translator_family
) {
895 translator
= yang_translator_find(translator_family
);
898 "%% Module translator \"%s\" not found\n",
904 return nb_cli_candidate_load_file(vty
, format
, translator
,
905 filename
, !!replace
);
908 return nb_cli_candidate_load_transaction(vty
, tid
, !!replace
);
911 DEFPY (show_config_running
,
912 show_config_running_cmd
,
913 "show configuration running\
914 [<json$json|xml$xml> [translate WORD$translator_family]]\
915 [with-defaults$with_defaults]",
917 "Configuration information\n"
918 "Running configuration\n"
919 "Change output format to JSON\n"
920 "Change output format to XML\n"
922 "YANG module translator\n"
923 "Show default values\n")
926 enum nb_cfg_format format
;
927 struct yang_translator
*translator
= NULL
;
930 format
= NB_CFG_FMT_JSON
;
932 format
= NB_CFG_FMT_XML
;
934 format
= NB_CFG_FMT_CMDS
;
936 if (translator_family
) {
937 translator
= yang_translator_find(translator_family
);
939 vty_out(vty
, "%% Module translator \"%s\" not found\n",
945 nb_cli_show_config(vty
, running_config
, format
, translator
,
951 DEFPY (show_config_candidate
,
952 show_config_candidate_cmd
,
953 "show configuration candidate\
954 [<json$json|xml$xml> [translate WORD$translator_family]]\
956 with-defaults$with_defaults\
960 "Configuration information\n"
961 "Candidate configuration\n"
962 "Change output format to JSON\n"
963 "Change output format to XML\n"
965 "YANG module translator\n"
966 "Show default values\n"
967 "Show changes applied in the candidate configuration\n")
970 enum nb_cfg_format format
;
971 struct yang_translator
*translator
= NULL
;
974 format
= NB_CFG_FMT_JSON
;
976 format
= NB_CFG_FMT_XML
;
978 format
= NB_CFG_FMT_CMDS
;
980 if (translator_family
) {
981 translator
= yang_translator_find(translator_family
);
983 vty_out(vty
, "%% Module translator \"%s\" not found\n",
990 return nb_cli_show_config_compare(
991 vty
, vty
->candidate_config_base
, vty
->candidate_config
,
994 nb_cli_show_config(vty
, vty
->candidate_config
, format
, translator
,
1000 DEFPY (show_config_candidate_section
,
1001 show_config_candidate_section_cmd
,
1005 struct lyd_node
*dnode
;
1007 /* Top-level configuration node, display everything. */
1008 if (vty
->xpath_index
== 0)
1009 return nb_cli_show_config(vty
, vty
->candidate_config
,
1010 NB_CFG_FMT_CMDS
, NULL
, false);
1012 /* Display only the current section of the candidate configuration. */
1013 dnode
= yang_dnode_get(vty
->candidate_config
->dnode
, VTY_CURR_XPATH
);
1015 /* Shouldn't happen. */
1018 nb_cli_show_dnode_cmds(vty
, dnode
, 0);
1019 vty_out(vty
, "!\n");
1024 DEFPY (show_config_compare
,
1025 show_config_compare_cmd
,
1026 "show configuration compare\
1028 candidate$c1_candidate\
1029 |running$c1_running\
1030 |transaction (1-4294967295)$c1_tid\
1033 candidate$c2_candidate\
1034 |running$c2_running\
1035 |transaction (1-4294967295)$c2_tid\
1037 [<json$json|xml$xml> [translate WORD$translator_family]]",
1039 "Configuration information\n"
1040 "Compare two different configurations\n"
1041 "Candidate configuration\n"
1042 "Running configuration\n"
1043 "Configuration transaction\n"
1045 "Candidate configuration\n"
1046 "Running configuration\n"
1047 "Configuration transaction\n"
1049 "Change output format to JSON\n"
1050 "Change output format to XML\n"
1051 "Translate output\n"
1052 "YANG module translator\n")
1054 enum nb_cfg_format format
;
1055 struct yang_translator
*translator
= NULL
;
1056 struct nb_config
*config1
, *config_transaction1
= NULL
;
1057 struct nb_config
*config2
, *config_transaction2
= NULL
;
1058 int ret
= CMD_WARNING
;
1061 config1
= vty
->candidate_config
;
1062 else if (c1_running
)
1063 config1
= running_config
;
1065 config_transaction1
= nb_db_transaction_load(c1_tid
);
1066 if (!config_transaction1
) {
1067 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1068 (unsigned int)c1_tid
);
1071 config1
= config_transaction1
;
1075 config2
= vty
->candidate_config
;
1076 else if (c2_running
)
1077 config2
= running_config
;
1079 config_transaction2
= nb_db_transaction_load(c2_tid
);
1080 if (!config_transaction2
) {
1081 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1082 (unsigned int)c2_tid
);
1085 config2
= config_transaction2
;
1089 format
= NB_CFG_FMT_JSON
;
1091 format
= NB_CFG_FMT_XML
;
1093 format
= NB_CFG_FMT_CMDS
;
1095 if (translator_family
) {
1096 translator
= yang_translator_find(translator_family
);
1098 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1104 ret
= nb_cli_show_config_compare(vty
, config1
, config2
, format
,
1107 if (config_transaction1
)
1108 nb_config_free(config_transaction1
);
1109 if (config_transaction2
)
1110 nb_config_free(config_transaction2
);
1116 * Stripped down version of the "show configuration compare" command.
1117 * The "candidate" option is not present so the command can be installed in
1120 ALIAS (show_config_compare
,
1121 show_config_compare_without_candidate_cmd
,
1122 "show configuration compare\
1125 |transaction (1-4294967295)$c1_tid\
1129 |transaction (1-4294967295)$c2_tid\
1131 [<json$json|xml$xml> [translate WORD$translator_family]]",
1133 "Configuration information\n"
1134 "Compare two different configurations\n"
1135 "Running configuration\n"
1136 "Configuration transaction\n"
1138 "Running configuration\n"
1139 "Configuration transaction\n"
1141 "Change output format to JSON\n"
1142 "Change output format to XML\n"
1143 "Translate output\n"
1144 "YANG module translator\n")
1146 DEFPY (clear_config_transactions
,
1147 clear_config_transactions_cmd
,
1148 "clear configuration transactions oldest (1-100)$n",
1150 "Configuration activity\n"
1151 "Delete transactions from the transactions log\n"
1152 "Delete oldest <n> transactions\n"
1153 "Number of transactions to delete\n")
1155 #ifdef HAVE_CONFIG_ROLLBACKS
1156 if (nb_db_clear_transactions(n
) != NB_OK
) {
1157 vty_out(vty
, "%% Failed to delete transactions.\n\n");
1162 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1163 #endif /* HAVE_CONFIG_ROLLBACKS */
1168 DEFPY (config_database_max_transactions
,
1169 config_database_max_transactions_cmd
,
1170 "configuration database max-transactions (1-100)$max",
1171 "Configuration related settings\n"
1172 "Configuration database\n"
1173 "Set the maximum number of transactions to store\n"
1174 "Number of transactions\n")
1176 #ifdef HAVE_CONFIG_ROLLBACKS
1177 if (nb_db_set_max_transactions(max
) != NB_OK
) {
1179 "%% Failed to update the maximum number of transactions.\n\n");
1183 "%% Maximum number of transactions updated successfully.\n\n");
1186 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1187 #endif /* HAVE_CONFIG_ROLLBACKS */
1192 DEFPY (yang_module_translator_load
,
1193 yang_module_translator_load_cmd
,
1194 "yang module-translator load FILENAME$filename",
1195 "YANG related settings\n"
1196 "YANG module translator\n"
1197 "Load YANG module translator\n"
1198 "File name (full path)\n")
1200 struct yang_translator
*translator
;
1202 translator
= yang_translator_load(filename
);
1204 vty_out(vty
, "%% Failed to load \"%s\"\n\n", filename
);
1205 vty_out(vty
, "Please check the logs for more details.\n");
1209 vty_out(vty
, "%% Module translator \"%s\" loaded successfully.\n\n",
1210 translator
->family
);
1215 DEFPY (yang_module_translator_unload_family
,
1216 yang_module_translator_unload_cmd
,
1217 "yang module-translator unload WORD$translator_family",
1218 "YANG related settings\n"
1219 "YANG module translator\n"
1220 "Unload YANG module translator\n"
1221 "Name of the module translator\n")
1223 struct yang_translator
*translator
;
1225 translator
= yang_translator_find(translator_family
);
1227 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1232 yang_translator_unload(translator
);
1237 #ifdef HAVE_CONFIG_ROLLBACKS
1238 static void nb_cli_show_transactions_cb(void *arg
, int transaction_id
,
1239 const char *client_name
,
1240 const char *date
, const char *comment
)
1242 struct ttable
*tt
= arg
;
1244 ttable_add_row(tt
, "%d|%s|%s|%s", transaction_id
, client_name
, date
,
1248 static int nb_cli_show_transactions(struct vty
*vty
)
1252 /* Prepare table. */
1253 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1254 ttable_add_row(tt
, "Transaction ID|Client|Date|Comment");
1255 tt
->style
.cell
.rpad
= 2;
1256 tt
->style
.corner
= '+';
1258 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1260 /* Fetch transactions from the northbound database. */
1261 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb
, tt
)
1264 "%% Failed to fetch configuration transactions.\n");
1268 /* Dump the generated table. */
1269 if (tt
->nrows
> 1) {
1272 table
= ttable_dump(tt
, "\n");
1273 vty_out(vty
, "%s\n", table
);
1274 XFREE(MTYPE_TMP
, table
);
1276 vty_out(vty
, "No configuration transactions to display.\n\n");
1282 #endif /* HAVE_CONFIG_ROLLBACKS */
1284 DEFPY (show_config_transaction
,
1285 show_config_transaction_cmd
,
1286 "show configuration transaction\
1288 (1-4294967295)$transaction_id\
1289 [<json$json|xml$xml> [translate WORD$translator_family]]\
1291 with-defaults$with_defaults\
1296 "Configuration information\n"
1297 "Configuration transaction\n"
1299 "Change output format to JSON\n"
1300 "Change output format to XML\n"
1301 "Translate output\n"
1302 "YANG module translator\n"
1303 "Show default values\n"
1304 "Show changes compared to the previous transaction\n")
1306 #ifdef HAVE_CONFIG_ROLLBACKS
1307 if (transaction_id
) {
1308 struct nb_config
*config
;
1309 enum nb_cfg_format format
;
1310 struct yang_translator
*translator
= NULL
;
1313 format
= NB_CFG_FMT_JSON
;
1315 format
= NB_CFG_FMT_XML
;
1317 format
= NB_CFG_FMT_CMDS
;
1319 if (translator_family
) {
1320 translator
= yang_translator_find(translator_family
);
1323 "%% Module translator \"%s\" not found\n",
1329 config
= nb_db_transaction_load(transaction_id
);
1331 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1332 (unsigned int)transaction_id
);
1337 struct nb_config
*prev_config
;
1340 /* NOTE: this can be NULL. */
1342 nb_db_transaction_load(transaction_id
- 1);
1344 ret
= nb_cli_show_config_compare(
1345 vty
, prev_config
, config
, format
, translator
);
1347 nb_config_free(prev_config
);
1348 nb_config_free(config
);
1353 nb_cli_show_config(vty
, config
, format
, translator
,
1355 nb_config_free(config
);
1360 return nb_cli_show_transactions(vty
);
1363 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1365 #endif /* HAVE_CONFIG_ROLLBACKS */
1368 static int nb_cli_oper_data_cb(const struct lysc_node
*snode
,
1369 struct yang_translator
*translator
,
1370 struct yang_data
*data
, void *arg
)
1372 struct lyd_node
*dnode
= arg
;
1373 struct ly_ctx
*ly_ctx
;
1378 ret
= yang_translate_xpath(translator
,
1379 YANG_TRANSLATE_FROM_NATIVE
,
1380 data
->xpath
, sizeof(data
->xpath
));
1382 case YANG_TRANSLATE_SUCCESS
:
1384 case YANG_TRANSLATE_NOTFOUND
:
1386 case YANG_TRANSLATE_FAILURE
:
1390 ly_ctx
= translator
->ly_ctx
;
1392 ly_ctx
= ly_native_ctx
;
1395 lyd_new_path(dnode
, ly_ctx
, data
->xpath
, (void *)data
->value
,
1396 LYD_NEW_PATH_UPDATE
, &dnode
);
1398 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_new_path(%s) failed: %s",
1399 __func__
, data
->xpath
, ly_errmsg(ly_native_ctx
));
1404 yang_data_free(data
);
1408 yang_data_free(data
);
1412 DEFPY (show_yang_operational_data
,
1413 show_yang_operational_data_cmd
,
1414 "show yang operational-data XPATH$xpath\
1416 format <json$json|xml$xml>\
1417 |translate WORD$translator_family\
1418 |with-config$with_config\
1421 "YANG information\n"
1422 "Show YANG operational data\n"
1423 "XPath expression specifying the YANG data path\n"
1424 "Set the output format\n"
1425 "JavaScript Object Notation\n"
1426 "Extensible Markup Language\n"
1427 "Translate operational data\n"
1428 "YANG module translator\n"
1429 "Merge configuration data\n")
1432 struct yang_translator
*translator
= NULL
;
1433 struct ly_ctx
*ly_ctx
;
1434 struct lyd_node
*dnode
;
1436 uint32_t print_options
= LYD_PRINT_WITHSIBLINGS
;
1443 if (translator_family
) {
1444 translator
= yang_translator_find(translator_family
);
1446 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1451 ly_ctx
= translator
->ly_ctx
;
1453 ly_ctx
= ly_native_ctx
;
1456 dnode
= yang_dnode_new(ly_ctx
, false);
1457 if (nb_oper_data_iterate(xpath
, translator
, 0, nb_cli_oper_data_cb
,
1460 vty_out(vty
, "%% Failed to fetch operational data.\n");
1461 yang_dnode_free(dnode
);
1465 if (with_config
&& yang_dnode_exists(running_config
->dnode
, xpath
)) {
1466 struct lyd_node
*config_dnode
=
1467 yang_dnode_get(running_config
->dnode
, xpath
);
1468 if (config_dnode
!= NULL
) {
1469 lyd_merge_tree(&dnode
, yang_dnode_dup(config_dnode
),
1470 LYD_MERGE_DESTRUCT
);
1471 print_options
|= LYD_PRINT_WD_ALL
;
1475 (void)lyd_validate_all(&dnode
, ly_ctx
, 0, NULL
);
1477 /* Display the data. */
1478 if (lyd_print_mem(&strp
, dnode
, format
, print_options
) != 0 || !strp
) {
1479 vty_out(vty
, "%% Failed to display operational data.\n");
1480 yang_dnode_free(dnode
);
1483 vty_out(vty
, "%s", strp
);
1485 yang_dnode_free(dnode
);
1490 DEFPY (show_yang_module
,
1491 show_yang_module_cmd
,
1492 "show yang module [module-translator WORD$translator_family]",
1494 "YANG information\n"
1495 "Show loaded modules\n"
1496 "YANG module translator\n"
1497 "YANG module translator\n")
1499 struct ly_ctx
*ly_ctx
;
1500 struct yang_translator
*translator
= NULL
;
1501 const struct lys_module
*module
;
1505 if (translator_family
) {
1506 translator
= yang_translator_find(translator_family
);
1508 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1512 ly_ctx
= translator
->ly_ctx
;
1514 ly_ctx
= ly_native_ctx
;
1516 /* Prepare table. */
1517 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1518 ttable_add_row(tt
, "Module|Version|Revision|Flags|Namespace");
1519 tt
->style
.cell
.rpad
= 2;
1520 tt
->style
.corner
= '+';
1522 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1524 while ((module
= ly_ctx_get_module_iter(ly_ctx
, &idx
))) {
1527 snprintf(flags
, sizeof(flags
), "%c%c",
1528 module
->implemented
? 'I' : ' ',
1529 LY_ARRAY_COUNT(module
->deviated_by
) ? 'D' : ' ');
1531 ttable_add_row(tt
, "%s|%s|%s|%s|%s", module
->name
,
1532 (module
->parsed
->version
== 2) ? "1.1" : "1.0",
1533 module
->revision
? module
->revision
: "-", flags
,
1537 /* Dump the generated table. */
1538 if (tt
->nrows
> 1) {
1541 vty_out(vty
, " Flags: I - Implemented, D - Deviated\n\n");
1543 table
= ttable_dump(tt
, "\n");
1544 vty_out(vty
, "%s\n", table
);
1545 XFREE(MTYPE_TMP
, table
);
1547 vty_out(vty
, "No YANG modules to display.\n\n");
1554 DEFPY(show_yang_module_detail
, show_yang_module_detail_cmd
,
1556 [module-translator WORD$translator_family]\
1557 WORD$module_name <compiled$compiled|summary|tree$tree|yang$yang|yin$yin>",
1559 "YANG information\n"
1560 "Show loaded modules\n"
1561 "YANG module translator\n"
1562 "YANG module translator\n"
1564 "Display compiled module in YANG format\n"
1565 "Display summary information about the module\n"
1566 "Display module in the tree (RFC 8340) format\n"
1567 "Display module in the YANG format\n"
1568 "Display module in the YIN format\n")
1570 struct ly_ctx
*ly_ctx
;
1571 struct yang_translator
*translator
= NULL
;
1572 const struct lys_module
*module
;
1573 LYS_OUTFORMAT format
;
1576 if (translator_family
) {
1577 translator
= yang_translator_find(translator_family
);
1579 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1583 ly_ctx
= translator
->ly_ctx
;
1585 ly_ctx
= ly_native_ctx
;
1587 module
= ly_ctx_get_module_latest(ly_ctx
, module_name
);
1589 vty_out(vty
, "%% Module \"%s\" not found\n", module_name
);
1594 format
= LYS_OUT_YANG
;
1596 format
= LYS_OUT_YIN
;
1598 format
= LYS_OUT_YANG_COMPILED
;
1600 format
= LYS_OUT_TREE
;
1603 "%% libyang v2 does not currently support summary\n");
1607 if (lys_print_mem(&strp
, module
, format
, 0) == 0) {
1608 vty_out(vty
, "%s\n", strp
);
1612 vty_out(vty
, "%% Error generating module information\n");
1619 DEFPY (show_yang_module_translator
,
1620 show_yang_module_translator_cmd
,
1621 "show yang module-translator",
1623 "YANG information\n"
1624 "Show loaded YANG module translators\n")
1626 struct yang_translator
*translator
;
1629 /* Prepare table. */
1630 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1631 ttable_add_row(tt
, "Family|Module|Deviations|Coverage (%%)");
1632 tt
->style
.cell
.rpad
= 2;
1633 tt
->style
.corner
= '+';
1635 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1637 RB_FOREACH (translator
, yang_translators
, &yang_translators
) {
1638 struct yang_tmodule
*tmodule
;
1639 struct listnode
*ln
;
1641 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
1642 ttable_add_row(tt
, "%s|%s|%s|%.2f", translator
->family
,
1643 tmodule
->module
->name
,
1644 tmodule
->deviations
->name
,
1649 /* Dump the generated table. */
1650 if (tt
->nrows
> 1) {
1653 table
= ttable_dump(tt
, "\n");
1654 vty_out(vty
, "%s\n", table
);
1655 XFREE(MTYPE_TMP
, table
);
1657 vty_out(vty
, "No YANG module translators to display.\n\n");
1664 #ifdef HAVE_CONFIG_ROLLBACKS
1665 static int nb_cli_rollback_configuration(struct vty
*vty
,
1666 uint32_t transaction_id
)
1668 struct nb_context context
= {};
1669 struct nb_config
*candidate
;
1671 char errmsg
[BUFSIZ
] = {0};
1674 candidate
= nb_db_transaction_load(transaction_id
);
1676 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1681 snprintf(comment
, sizeof(comment
), "Rollback to transaction %u",
1684 context
.client
= NB_CLIENT_CLI
;
1686 ret
= nb_candidate_commit(context
, candidate
, true, comment
, NULL
,
1687 errmsg
, sizeof(errmsg
));
1688 nb_config_free(candidate
);
1692 "%% Configuration was successfully rolled back.\n\n");
1693 /* Print warnings (if any). */
1694 if (strlen(errmsg
) > 0)
1695 vty_out(vty
, "%s\n", errmsg
);
1697 case NB_ERR_NO_CHANGES
:
1699 "%% Aborting - no configuration changes detected.\n\n");
1702 vty_out(vty
, "%% Rollback failed.\n\n");
1703 vty_show_nb_errors(vty
, ret
, errmsg
);
1707 #endif /* HAVE_CONFIG_ROLLBACKS */
1709 DEFPY (rollback_config
,
1710 rollback_config_cmd
,
1711 "rollback configuration (1-4294967295)$transaction_id",
1712 "Rollback to a previous state\n"
1713 "Running configuration\n"
1716 #ifdef HAVE_CONFIG_ROLLBACKS
1717 return nb_cli_rollback_configuration(vty
, transaction_id
);
1720 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1722 #endif /* HAVE_CONFIG_ROLLBACKS */
1725 /* Debug CLI commands. */
1726 static struct debug
*nb_debugs
[] = {
1727 &nb_dbg_cbs_config
, &nb_dbg_cbs_state
, &nb_dbg_cbs_rpc
,
1728 &nb_dbg_notif
, &nb_dbg_events
, &nb_dbg_libyang
,
1731 static const char *const nb_debugs_conflines
[] = {
1732 "debug northbound callbacks configuration",
1733 "debug northbound callbacks state",
1734 "debug northbound callbacks rpc",
1735 "debug northbound notifications",
1736 "debug northbound events",
1737 "debug northbound libyang",
1740 DEFINE_HOOK(nb_client_debug_set_all
, (uint32_t flags
, bool set
), (flags
, set
));
1742 static void nb_debug_set_all(uint32_t flags
, bool set
)
1744 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++) {
1745 DEBUG_FLAGS_SET(nb_debugs
[i
], flags
, set
);
1747 /* If all modes have been turned off, don't preserve options. */
1748 if (!DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_ALL
))
1749 DEBUG_CLEAR(nb_debugs
[i
]);
1752 hook_call(nb_client_debug_set_all
, flags
, set
);
1757 "[no] debug northbound\
1759 callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\
1760 |notifications$notifications\
1766 "Northbound debugging\n"
1773 "libyang debugging\n")
1775 uint32_t mode
= DEBUG_NODE2MODE(vty
->node
);
1778 bool none
= (!cbs_cfg
&& !cbs_state
&& !cbs_rpc
);
1780 if (none
|| cbs_cfg
)
1781 DEBUG_MODE_SET(&nb_dbg_cbs_config
, mode
, !no
);
1782 if (none
|| cbs_state
)
1783 DEBUG_MODE_SET(&nb_dbg_cbs_state
, mode
, !no
);
1784 if (none
|| cbs_rpc
)
1785 DEBUG_MODE_SET(&nb_dbg_cbs_rpc
, mode
, !no
);
1788 DEBUG_MODE_SET(&nb_dbg_notif
, mode
, !no
);
1790 DEBUG_MODE_SET(&nb_dbg_events
, mode
, !no
);
1792 DEBUG_MODE_SET(&nb_dbg_libyang
, mode
, !no
);
1793 yang_debugging_set(!no
);
1796 /* no specific debug --> act on all of them */
1797 if (strmatch(argv
[argc
- 1]->text
, "northbound")) {
1798 nb_debug_set_all(mode
, !no
);
1799 yang_debugging_set(!no
);
1805 DEFINE_HOOK(nb_client_debug_config_write
, (struct vty
*vty
), (vty
));
1807 static int nb_debug_config_write(struct vty
*vty
)
1809 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++)
1810 if (DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_CONF
))
1811 vty_out(vty
, "%s\n", nb_debugs_conflines
[i
]);
1813 hook_call(nb_client_debug_config_write
, vty
);
1818 static struct debug_callbacks nb_dbg_cbs
= {.debug_set_all
= nb_debug_set_all
};
1819 static struct cmd_node nb_debug_node
= {
1820 .name
= "northbound debug",
1821 .node
= NORTHBOUND_DEBUG_NODE
,
1823 .config_write
= nb_debug_config_write
,
1826 void nb_cli_install_default(int node
)
1828 _install_element(node
, &show_config_candidate_section_cmd
);
1830 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL
)
1833 _install_element(node
, &config_commit_cmd
);
1834 _install_element(node
, &config_commit_comment_cmd
);
1835 _install_element(node
, &config_commit_check_cmd
);
1836 _install_element(node
, &config_update_cmd
);
1837 _install_element(node
, &config_discard_cmd
);
1838 _install_element(node
, &show_config_running_cmd
);
1839 _install_element(node
, &show_config_candidate_cmd
);
1840 _install_element(node
, &show_config_compare_cmd
);
1841 _install_element(node
, &show_config_transaction_cmd
);
1844 /* YANG module autocomplete. */
1845 static void yang_module_autocomplete(vector comps
, struct cmd_token
*token
)
1847 const struct lys_module
*module
;
1848 struct yang_translator
*module_tr
;
1852 while ((module
= ly_ctx_get_module_iter(ly_native_ctx
, &idx
)))
1853 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1855 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
) {
1857 while ((module
= ly_ctx_get_module_iter(module_tr
->ly_ctx
,
1860 XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1864 /* YANG module translator autocomplete. */
1865 static void yang_translator_autocomplete(vector comps
, struct cmd_token
*token
)
1867 struct yang_translator
*module_tr
;
1869 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
)
1870 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module_tr
->family
));
1873 static const struct cmd_variable_handler yang_var_handlers
[] = {
1874 {.varname
= "module_name", .completions
= yang_module_autocomplete
},
1875 {.varname
= "translator_family",
1876 .completions
= yang_translator_autocomplete
},
1877 {.completions
= NULL
}};
1879 void nb_cli_init(struct event_loop
*tm
)
1883 /* Initialize the shared candidate configuration. */
1884 vty_shared_candidate_config
= nb_config_new(NULL
);
1886 debug_init(&nb_dbg_cbs
);
1888 install_node(&nb_debug_node
);
1889 install_element(ENABLE_NODE
, &debug_nb_cmd
);
1890 install_element(CONFIG_NODE
, &debug_nb_cmd
);
1892 /* Install commands specific to the transaction-base mode. */
1893 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL
) {
1894 install_element(ENABLE_NODE
, &config_exclusive_cmd
);
1895 install_element(ENABLE_NODE
, &config_private_cmd
);
1896 install_element(ENABLE_NODE
,
1897 &show_config_compare_without_candidate_cmd
);
1898 install_element(ENABLE_NODE
, &show_config_transaction_cmd
);
1899 install_element(ENABLE_NODE
, &rollback_config_cmd
);
1900 install_element(ENABLE_NODE
, &clear_config_transactions_cmd
);
1902 install_element(CONFIG_NODE
, &config_load_cmd
);
1903 install_element(CONFIG_NODE
,
1904 &config_database_max_transactions_cmd
);
1907 /* Other commands. */
1908 install_element(ENABLE_NODE
, &show_config_running_cmd
);
1909 install_element(CONFIG_NODE
, &yang_module_translator_load_cmd
);
1910 install_element(CONFIG_NODE
, &yang_module_translator_unload_cmd
);
1911 install_element(ENABLE_NODE
, &show_yang_operational_data_cmd
);
1912 install_element(ENABLE_NODE
, &show_yang_module_cmd
);
1913 install_element(ENABLE_NODE
, &show_yang_module_detail_cmd
);
1914 install_element(ENABLE_NODE
, &show_yang_module_translator_cmd
);
1915 cmd_variable_handler_register(yang_var_handlers
);
1918 void nb_cli_terminate(void)
1920 nb_config_free(vty_shared_candidate_config
);