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 thread_master
*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
] = {};
187 /* Parse the base XPath format string. */
188 if (xpath_base_fmt
) {
191 va_start(ap
, xpath_base_fmt
);
192 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
196 if (vty_mgmt_fe_enabled()) {
198 return vty_mgmt_send_config_data(vty
);
201 return nb_cli_apply_changes_internal(vty
, xpath_base
, false);
204 int nb_cli_apply_changes_clear_pending(struct vty
*vty
,
205 const char *xpath_base_fmt
, ...)
207 char xpath_base
[XPATH_MAXLEN
] = {};
209 /* Parse the base XPath format string. */
210 if (xpath_base_fmt
) {
213 va_start(ap
, xpath_base_fmt
);
214 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
218 if (vty_mgmt_fe_enabled()) {
220 return vty_mgmt_send_config_data(vty
);
223 return nb_cli_apply_changes_internal(vty
, xpath_base
, true);
226 int nb_cli_rpc(struct vty
*vty
, const char *xpath
, struct list
*input
,
229 struct nb_node
*nb_node
;
231 char errmsg
[BUFSIZ
] = {0};
233 nb_node
= nb_node_find(xpath
);
235 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
236 "%s: unknown data path: %s", __func__
, xpath
);
240 ret
= nb_callback_rpc(nb_node
, xpath
, input
, output
, errmsg
,
247 vty_show_nb_errors(vty
, ret
, errmsg
);
252 void nb_cli_confirmed_commit_clean(struct vty
*vty
)
254 thread_cancel(&vty
->t_confirmed_commit_timeout
);
255 nb_config_free(vty
->confirmed_commit_rollback
);
256 vty
->confirmed_commit_rollback
= NULL
;
259 int nb_cli_confirmed_commit_rollback(struct vty
*vty
)
261 struct nb_context context
= {};
262 uint32_t transaction_id
;
263 char errmsg
[BUFSIZ
] = {0};
266 /* Perform the rollback. */
267 context
.client
= NB_CLIENT_CLI
;
269 ret
= nb_candidate_commit(
270 context
, vty
->confirmed_commit_rollback
, true,
271 "Rollback to previous configuration - confirmed commit has timed out",
272 &transaction_id
, errmsg
, sizeof(errmsg
));
275 "Rollback performed successfully (Transaction ID #%u).\n",
277 /* Print warnings (if any). */
278 if (strlen(errmsg
) > 0)
279 vty_out(vty
, "%s\n", errmsg
);
282 "Failed to rollback to previous configuration.\n\n");
283 vty_show_nb_errors(vty
, ret
, errmsg
);
289 static void nb_cli_confirmed_commit_timeout(struct thread
*thread
)
291 struct vty
*vty
= THREAD_ARG(thread
);
293 /* XXX: broadcast this message to all logged-in users? */
295 "\nConfirmed commit has timed out, rolling back to previous configuration.\n\n");
297 nb_cli_confirmed_commit_rollback(vty
);
298 nb_cli_confirmed_commit_clean(vty
);
301 static int nb_cli_commit(struct vty
*vty
, bool force
,
302 unsigned int confirmed_timeout
, char *comment
)
304 struct nb_context context
= {};
305 uint32_t transaction_id
= 0;
306 char errmsg
[BUFSIZ
] = {0};
309 /* Check if there's a pending confirmed commit. */
310 if (vty
->t_confirmed_commit_timeout
) {
311 if (confirmed_timeout
) {
312 /* Reset timeout if "commit confirmed" is used again. */
314 "%% Resetting confirmed-commit timeout to %u minute(s)\n\n",
317 thread_cancel(&vty
->t_confirmed_commit_timeout
);
318 thread_add_timer(master
,
319 nb_cli_confirmed_commit_timeout
, vty
,
320 confirmed_timeout
* 60,
321 &vty
->t_confirmed_commit_timeout
);
323 /* Accept commit confirmation. */
324 vty_out(vty
, "%% Commit complete.\n\n");
325 nb_cli_confirmed_commit_clean(vty
);
330 /* "force" parameter. */
331 if (!force
&& nb_candidate_needs_update(vty
->candidate_config
)) {
333 "%% Candidate configuration needs to be updated before commit.\n\n");
335 "Use the \"update\" command or \"commit force\".\n");
339 /* "confirm" parameter. */
340 if (confirmed_timeout
) {
341 vty
->confirmed_commit_rollback
= nb_config_dup(running_config
);
343 vty
->t_confirmed_commit_timeout
= NULL
;
344 thread_add_timer(master
, nb_cli_confirmed_commit_timeout
, vty
,
345 confirmed_timeout
* 60,
346 &vty
->t_confirmed_commit_timeout
);
349 context
.client
= NB_CLIENT_CLI
;
351 ret
= nb_candidate_commit(context
, vty
->candidate_config
, true, comment
,
352 &transaction_id
, errmsg
, sizeof(errmsg
));
354 /* Map northbound return code to CLI return code. */
357 nb_config_replace(vty
->candidate_config_base
, running_config
,
360 "%% Configuration committed successfully (Transaction ID #%u).\n\n",
362 /* Print warnings (if any). */
363 if (strlen(errmsg
) > 0)
364 vty_out(vty
, "%s\n", errmsg
);
366 case NB_ERR_NO_CHANGES
:
367 vty_out(vty
, "%% No configuration changes to commit.\n\n");
371 "%% Failed to commit candidate configuration.\n\n");
372 vty_show_nb_errors(vty
, ret
, errmsg
);
377 static int nb_cli_candidate_load_file(struct vty
*vty
,
378 enum nb_cfg_format format
,
379 struct yang_translator
*translator
,
380 const char *path
, bool replace
)
382 struct nb_config
*loaded_config
= NULL
;
383 struct lyd_node
*dnode
;
384 struct ly_ctx
*ly_ctx
;
390 case NB_CFG_FMT_CMDS
:
391 loaded_config
= nb_config_new(NULL
);
392 if (!vty_read_config(loaded_config
, path
, config_default
)) {
393 vty_out(vty
, "%% Failed to load configuration.\n\n");
395 "Please check the logs for more details.\n");
396 nb_config_free(loaded_config
);
400 case NB_CFG_FMT_JSON
:
402 ly_format
= (format
== NB_CFG_FMT_JSON
) ? LYD_JSON
: LYD_XML
;
404 ly_ctx
= translator
? translator
->ly_ctx
: ly_native_ctx
;
405 err
= lyd_parse_data_path(ly_ctx
, path
, ly_format
,
406 LYD_PARSE_ONLY
| LYD_PARSE_NO_STATE
,
409 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_path() failed",
411 vty_out(vty
, "%% Failed to load configuration:\n\n");
413 yang_print_errors(ly_native_ctx
, buf
,
418 && yang_translate_dnode(translator
,
419 YANG_TRANSLATE_TO_NATIVE
, &dnode
)
420 != YANG_TRANSLATE_SUCCESS
) {
421 vty_out(vty
, "%% Failed to translate configuration\n");
422 yang_dnode_free(dnode
);
425 loaded_config
= nb_config_new(dnode
);
430 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
431 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
434 "%% Failed to merge the loaded configuration:\n\n");
436 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
443 static int nb_cli_candidate_load_transaction(struct vty
*vty
,
444 uint32_t transaction_id
,
447 struct nb_config
*loaded_config
;
450 loaded_config
= nb_db_transaction_load(transaction_id
);
451 if (!loaded_config
) {
452 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
458 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
459 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
462 "%% Failed to merge the loaded configuration:\n\n");
464 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
471 /* Prepare the configuration for display. */
472 void nb_cli_show_config_prepare(struct nb_config
*config
, bool with_defaults
)
474 /* Nothing to do for daemons that don't implement any YANG module. */
475 if (config
->dnode
== NULL
)
479 * Call lyd_validate() only to create default child nodes, ignoring
480 * any possible validation error. This doesn't need to be done when
481 * displaying the running configuration since it's always fully
484 if (config
!= running_config
)
485 (void)lyd_validate_all(&config
->dnode
, ly_native_ctx
,
486 LYD_VALIDATE_NO_STATE
, NULL
);
489 static int lyd_node_cmp(const struct lyd_node
**dnode1
,
490 const struct lyd_node
**dnode2
)
492 struct nb_node
*nb_node
= (*dnode1
)->schema
->priv
;
494 return nb_node
->cbs
.cli_cmp(*dnode1
, *dnode2
);
497 static void show_dnode_children_cmds(struct vty
*vty
,
498 const struct lyd_node
*root
,
501 struct nb_node
*nb_node
, *sort_node
= NULL
;
502 struct listnode
*listnode
;
503 struct lyd_node
*child
;
504 struct list
*sort_list
;
507 LY_LIST_FOR (lyd_child(root
), child
) {
508 nb_node
= child
->schema
->priv
;
511 * We finished processing current list,
512 * it's time to print the config.
514 if (sort_node
&& nb_node
!= sort_node
) {
516 (int (*)(const void **,
517 const void **))lyd_node_cmp
);
519 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
520 nb_cli_show_dnode_cmds(vty
, data
,
523 list_delete(&sort_list
);
528 * If the config needs to be sorted,
529 * then add the dnode to the sorting
530 * list for later processing.
532 if (nb_node
&& nb_node
->cbs
.cli_cmp
) {
535 sort_list
= list_new();
538 listnode_add(sort_list
, child
);
542 nb_cli_show_dnode_cmds(vty
, child
, with_defaults
);
547 (int (*)(const void **, const void **))lyd_node_cmp
);
549 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
550 nb_cli_show_dnode_cmds(vty
, data
, with_defaults
);
552 list_delete(&sort_list
);
557 void nb_cli_show_dnode_cmds(struct vty
*vty
, const struct lyd_node
*root
,
560 struct nb_node
*nb_node
;
562 if (!with_defaults
&& yang_dnode_is_default_recursive(root
))
565 nb_node
= root
->schema
->priv
;
567 if (nb_node
&& nb_node
->cbs
.cli_show
)
568 (*nb_node
->cbs
.cli_show
)(vty
, root
, with_defaults
);
570 if (!(root
->schema
->nodetype
& (LYS_LEAF
| LYS_LEAFLIST
| LYS_ANYDATA
)))
571 show_dnode_children_cmds(vty
, root
, with_defaults
);
573 if (nb_node
&& nb_node
->cbs
.cli_show_end
)
574 (*nb_node
->cbs
.cli_show_end
)(vty
, root
);
577 static void nb_cli_show_config_cmds(struct vty
*vty
, struct nb_config
*config
,
580 struct lyd_node
*root
;
582 vty_out(vty
, "Configuration:\n");
584 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
585 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
587 LY_LIST_FOR (config
->dnode
, root
) {
588 nb_cli_show_dnode_cmds(vty
, root
, with_defaults
);
592 vty_out(vty
, "end\n");
595 static int nb_cli_show_config_libyang(struct vty
*vty
, LYD_FORMAT format
,
596 struct nb_config
*config
,
597 struct yang_translator
*translator
,
600 struct lyd_node
*dnode
;
604 dnode
= yang_dnode_dup(config
->dnode
);
606 && yang_translate_dnode(translator
, YANG_TRANSLATE_FROM_NATIVE
,
608 != YANG_TRANSLATE_SUCCESS
) {
609 vty_out(vty
, "%% Failed to translate configuration\n");
610 yang_dnode_free(dnode
);
614 SET_FLAG(options
, LYD_PRINT_WITHSIBLINGS
);
616 SET_FLAG(options
, LYD_PRINT_WD_ALL
);
618 SET_FLAG(options
, LYD_PRINT_WD_TRIM
);
620 if (lyd_print_mem(&strp
, dnode
, format
, options
) == 0 && strp
) {
621 vty_out(vty
, "%s", strp
);
625 yang_dnode_free(dnode
);
630 static int nb_cli_show_config(struct vty
*vty
, struct nb_config
*config
,
631 enum nb_cfg_format format
,
632 struct yang_translator
*translator
,
635 nb_cli_show_config_prepare(config
, with_defaults
);
638 case NB_CFG_FMT_CMDS
:
639 nb_cli_show_config_cmds(vty
, config
, with_defaults
);
641 case NB_CFG_FMT_JSON
:
642 return nb_cli_show_config_libyang(vty
, LYD_JSON
, config
,
643 translator
, with_defaults
);
645 return nb_cli_show_config_libyang(vty
, LYD_XML
, config
,
646 translator
, with_defaults
);
652 static int nb_write_config(struct nb_config
*config
, enum nb_cfg_format format
,
653 struct yang_translator
*translator
, char *path
,
657 struct vty
*file_vty
;
660 snprintf(path
, pathlen
, "/tmp/frr.tmp.XXXXXXXX");
663 flog_warn(EC_LIB_SYSTEM_CALL
, "%s: mkstemp() failed: %s",
664 __func__
, safe_strerror(errno
));
667 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
668 flog_warn(EC_LIB_SYSTEM_CALL
,
669 "%s: fchmod() failed: %s(%d):", __func__
,
670 safe_strerror(errno
), errno
);
674 /* Make vty for configuration file. */
675 file_vty
= vty_new();
677 file_vty
->type
= VTY_FILE
;
679 ret
= nb_cli_show_config(file_vty
, config
, format
, translator
,
686 static int nb_cli_show_config_compare(struct vty
*vty
,
687 struct nb_config
*config1
,
688 struct nb_config
*config2
,
689 enum nb_cfg_format format
,
690 struct yang_translator
*translator
)
692 char config1_path
[256];
693 char config2_path
[256];
694 char command
[BUFSIZ
];
699 if (nb_write_config(config1
, format
, translator
, config1_path
,
700 sizeof(config1_path
))
702 vty_out(vty
, "%% Failed to process configurations.\n\n");
705 if (nb_write_config(config2
, format
, translator
, config2_path
,
706 sizeof(config2_path
))
708 vty_out(vty
, "%% Failed to process configurations.\n\n");
709 unlink(config1_path
);
713 snprintf(command
, sizeof(command
), "diff -u %s %s", config1_path
,
715 fp
= popen(command
, "r");
717 vty_out(vty
, "%% Failed to generate configuration diff.\n\n");
718 unlink(config1_path
);
719 unlink(config2_path
);
722 /* Print diff line by line. */
723 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
726 vty_out(vty
, "%s", line
);
730 unlink(config1_path
);
731 unlink(config2_path
);
736 /* Configure exclusively from this terminal. */
737 DEFUN (config_exclusive
,
738 config_exclusive_cmd
,
739 "configure exclusive",
740 "Configuration from vty interface\n"
741 "Configure exclusively from this terminal\n")
743 return vty_config_enter(vty
, true, true);
746 /* Configure using a private candidate configuration. */
747 DEFUN (config_private
,
750 "Configuration from vty interface\n"
751 "Configure using a private candidate configuration\n")
753 return vty_config_enter(vty
, true, false);
756 DEFPY (config_commit
,
758 "commit [{force$force|confirmed (1-60)}]",
759 "Commit changes into the running configuration\n"
760 "Force commit even if the candidate is outdated\n"
761 "Rollback this commit unless there is a confirming commit\n"
762 "Timeout in minutes for the commit to be confirmed\n")
764 return nb_cli_commit(vty
, !!force
, confirmed
, NULL
);
767 DEFPY (config_commit_comment
,
768 config_commit_comment_cmd
,
769 "commit [{force$force|confirmed (1-60)}] comment LINE...",
770 "Commit changes into the running configuration\n"
771 "Force commit even if the candidate is outdated\n"
772 "Rollback this commit unless there is a confirming commit\n"
773 "Timeout in minutes for the commit to be confirmed\n"
774 "Assign a comment to this commit\n"
775 "Comment for this commit (Max 80 characters)\n")
781 argv_find(argv
, argc
, "LINE", &idx
);
782 comment
= argv_concat(argv
, argc
, idx
);
783 ret
= nb_cli_commit(vty
, !!force
, confirmed
, comment
);
784 XFREE(MTYPE_TMP
, comment
);
789 DEFPY (config_commit_check
,
790 config_commit_check_cmd
,
792 "Commit changes into the running configuration\n"
793 "Check if the configuration changes are valid\n")
795 struct nb_context context
= {};
796 char errmsg
[BUFSIZ
] = {0};
799 context
.client
= NB_CLIENT_CLI
;
801 ret
= nb_candidate_validate(&context
, vty
->candidate_config
, errmsg
,
805 "%% Failed to validate candidate configuration.\n\n");
806 vty_show_nb_errors(vty
, ret
, errmsg
);
810 vty_out(vty
, "%% Candidate configuration validated successfully.\n\n");
815 DEFPY (config_update
,
818 "Update candidate configuration\n")
820 if (!nb_candidate_needs_update(vty
->candidate_config
)) {
821 vty_out(vty
, "%% Update is not necessary.\n\n");
825 if (nb_candidate_update(vty
->candidate_config
) != NB_OK
) {
827 "%% Failed to update the candidate configuration.\n\n");
828 vty_out(vty
, "Please check the logs for more details.\n");
832 nb_config_replace(vty
->candidate_config_base
, running_config
, true);
834 vty_out(vty
, "%% Candidate configuration updated successfully.\n\n");
839 DEFPY (config_discard
,
842 "Discard changes in the candidate configuration\n")
844 nb_config_replace(vty
->candidate_config
, vty
->candidate_config_base
,
854 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
855 |transaction (1-4294967295)$tid\
858 "Configuration related settings\n"
859 "Load configuration into candidate\n"
860 "Load configuration file into candidate\n"
861 "Load configuration file in JSON format\n"
862 "Load configuration file in XML format\n"
863 "Translate configuration file\n"
864 "YANG module translator\n"
865 "Configuration file name (full path)\n"
866 "Load configuration from transaction into candidate\n"
868 "Replace instead of merge\n")
871 enum nb_cfg_format format
;
872 struct yang_translator
*translator
= NULL
;
875 format
= NB_CFG_FMT_JSON
;
877 format
= NB_CFG_FMT_XML
;
879 format
= NB_CFG_FMT_CMDS
;
881 if (translator_family
) {
882 translator
= yang_translator_find(translator_family
);
885 "%% Module translator \"%s\" not found\n",
891 return nb_cli_candidate_load_file(vty
, format
, translator
,
892 filename
, !!replace
);
895 return nb_cli_candidate_load_transaction(vty
, tid
, !!replace
);
898 DEFPY (show_config_running
,
899 show_config_running_cmd
,
900 "show configuration running\
901 [<json$json|xml$xml> [translate WORD$translator_family]]\
902 [with-defaults$with_defaults]",
904 "Configuration information\n"
905 "Running configuration\n"
906 "Change output format to JSON\n"
907 "Change output format to XML\n"
909 "YANG module translator\n"
910 "Show default values\n")
913 enum nb_cfg_format format
;
914 struct yang_translator
*translator
= NULL
;
917 format
= NB_CFG_FMT_JSON
;
919 format
= NB_CFG_FMT_XML
;
921 format
= NB_CFG_FMT_CMDS
;
923 if (translator_family
) {
924 translator
= yang_translator_find(translator_family
);
926 vty_out(vty
, "%% Module translator \"%s\" not found\n",
932 nb_cli_show_config(vty
, running_config
, format
, translator
,
938 DEFPY (show_config_candidate
,
939 show_config_candidate_cmd
,
940 "show configuration candidate\
941 [<json$json|xml$xml> [translate WORD$translator_family]]\
943 with-defaults$with_defaults\
947 "Configuration information\n"
948 "Candidate configuration\n"
949 "Change output format to JSON\n"
950 "Change output format to XML\n"
952 "YANG module translator\n"
953 "Show default values\n"
954 "Show changes applied in the candidate configuration\n")
957 enum nb_cfg_format format
;
958 struct yang_translator
*translator
= NULL
;
961 format
= NB_CFG_FMT_JSON
;
963 format
= NB_CFG_FMT_XML
;
965 format
= NB_CFG_FMT_CMDS
;
967 if (translator_family
) {
968 translator
= yang_translator_find(translator_family
);
970 vty_out(vty
, "%% Module translator \"%s\" not found\n",
977 return nb_cli_show_config_compare(
978 vty
, vty
->candidate_config_base
, vty
->candidate_config
,
981 nb_cli_show_config(vty
, vty
->candidate_config
, format
, translator
,
987 DEFPY (show_config_candidate_section
,
988 show_config_candidate_section_cmd
,
992 struct lyd_node
*dnode
;
994 /* Top-level configuration node, display everything. */
995 if (vty
->xpath_index
== 0)
996 return nb_cli_show_config(vty
, vty
->candidate_config
,
997 NB_CFG_FMT_CMDS
, NULL
, false);
999 /* Display only the current section of the candidate configuration. */
1000 dnode
= yang_dnode_get(vty
->candidate_config
->dnode
, VTY_CURR_XPATH
);
1002 /* Shouldn't happen. */
1005 nb_cli_show_dnode_cmds(vty
, dnode
, 0);
1006 vty_out(vty
, "!\n");
1011 DEFPY (show_config_compare
,
1012 show_config_compare_cmd
,
1013 "show configuration compare\
1015 candidate$c1_candidate\
1016 |running$c1_running\
1017 |transaction (1-4294967295)$c1_tid\
1020 candidate$c2_candidate\
1021 |running$c2_running\
1022 |transaction (1-4294967295)$c2_tid\
1024 [<json$json|xml$xml> [translate WORD$translator_family]]",
1026 "Configuration information\n"
1027 "Compare two different configurations\n"
1028 "Candidate configuration\n"
1029 "Running configuration\n"
1030 "Configuration transaction\n"
1032 "Candidate configuration\n"
1033 "Running configuration\n"
1034 "Configuration transaction\n"
1036 "Change output format to JSON\n"
1037 "Change output format to XML\n"
1038 "Translate output\n"
1039 "YANG module translator\n")
1041 enum nb_cfg_format format
;
1042 struct yang_translator
*translator
= NULL
;
1043 struct nb_config
*config1
, *config_transaction1
= NULL
;
1044 struct nb_config
*config2
, *config_transaction2
= NULL
;
1045 int ret
= CMD_WARNING
;
1048 config1
= vty
->candidate_config
;
1049 else if (c1_running
)
1050 config1
= running_config
;
1052 config_transaction1
= nb_db_transaction_load(c1_tid
);
1053 if (!config_transaction1
) {
1054 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1055 (unsigned int)c1_tid
);
1058 config1
= config_transaction1
;
1062 config2
= vty
->candidate_config
;
1063 else if (c2_running
)
1064 config2
= running_config
;
1066 config_transaction2
= nb_db_transaction_load(c2_tid
);
1067 if (!config_transaction2
) {
1068 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1069 (unsigned int)c2_tid
);
1072 config2
= config_transaction2
;
1076 format
= NB_CFG_FMT_JSON
;
1078 format
= NB_CFG_FMT_XML
;
1080 format
= NB_CFG_FMT_CMDS
;
1082 if (translator_family
) {
1083 translator
= yang_translator_find(translator_family
);
1085 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1091 ret
= nb_cli_show_config_compare(vty
, config1
, config2
, format
,
1094 if (config_transaction1
)
1095 nb_config_free(config_transaction1
);
1096 if (config_transaction2
)
1097 nb_config_free(config_transaction2
);
1103 * Stripped down version of the "show configuration compare" command.
1104 * The "candidate" option is not present so the command can be installed in
1107 ALIAS (show_config_compare
,
1108 show_config_compare_without_candidate_cmd
,
1109 "show configuration compare\
1112 |transaction (1-4294967295)$c1_tid\
1116 |transaction (1-4294967295)$c2_tid\
1118 [<json$json|xml$xml> [translate WORD$translator_family]]",
1120 "Configuration information\n"
1121 "Compare two different configurations\n"
1122 "Running configuration\n"
1123 "Configuration transaction\n"
1125 "Running configuration\n"
1126 "Configuration transaction\n"
1128 "Change output format to JSON\n"
1129 "Change output format to XML\n"
1130 "Translate output\n"
1131 "YANG module translator\n")
1133 DEFPY (clear_config_transactions
,
1134 clear_config_transactions_cmd
,
1135 "clear configuration transactions oldest (1-100)$n",
1137 "Configuration activity\n"
1138 "Delete transactions from the transactions log\n"
1139 "Delete oldest <n> transactions\n"
1140 "Number of transactions to delete\n")
1142 #ifdef HAVE_CONFIG_ROLLBACKS
1143 if (nb_db_clear_transactions(n
) != NB_OK
) {
1144 vty_out(vty
, "%% Failed to delete transactions.\n\n");
1149 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1150 #endif /* HAVE_CONFIG_ROLLBACKS */
1155 DEFPY (config_database_max_transactions
,
1156 config_database_max_transactions_cmd
,
1157 "configuration database max-transactions (1-100)$max",
1158 "Configuration related settings\n"
1159 "Configuration database\n"
1160 "Set the maximum number of transactions to store\n"
1161 "Number of transactions\n")
1163 #ifdef HAVE_CONFIG_ROLLBACKS
1164 if (nb_db_set_max_transactions(max
) != NB_OK
) {
1166 "%% Failed to update the maximum number of transactions.\n\n");
1170 "%% Maximum number of transactions updated successfully.\n\n");
1173 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1174 #endif /* HAVE_CONFIG_ROLLBACKS */
1179 DEFPY (yang_module_translator_load
,
1180 yang_module_translator_load_cmd
,
1181 "yang module-translator load FILENAME$filename",
1182 "YANG related settings\n"
1183 "YANG module translator\n"
1184 "Load YANG module translator\n"
1185 "File name (full path)\n")
1187 struct yang_translator
*translator
;
1189 translator
= yang_translator_load(filename
);
1191 vty_out(vty
, "%% Failed to load \"%s\"\n\n", filename
);
1192 vty_out(vty
, "Please check the logs for more details.\n");
1196 vty_out(vty
, "%% Module translator \"%s\" loaded successfully.\n\n",
1197 translator
->family
);
1202 DEFPY (yang_module_translator_unload_family
,
1203 yang_module_translator_unload_cmd
,
1204 "yang module-translator unload WORD$translator_family",
1205 "YANG related settings\n"
1206 "YANG module translator\n"
1207 "Unload YANG module translator\n"
1208 "Name of the module translator\n")
1210 struct yang_translator
*translator
;
1212 translator
= yang_translator_find(translator_family
);
1214 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1219 yang_translator_unload(translator
);
1224 #ifdef HAVE_CONFIG_ROLLBACKS
1225 static void nb_cli_show_transactions_cb(void *arg
, int transaction_id
,
1226 const char *client_name
,
1227 const char *date
, const char *comment
)
1229 struct ttable
*tt
= arg
;
1231 ttable_add_row(tt
, "%d|%s|%s|%s", transaction_id
, client_name
, date
,
1235 static int nb_cli_show_transactions(struct vty
*vty
)
1239 /* Prepare table. */
1240 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1241 ttable_add_row(tt
, "Transaction ID|Client|Date|Comment");
1242 tt
->style
.cell
.rpad
= 2;
1243 tt
->style
.corner
= '+';
1245 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1247 /* Fetch transactions from the northbound database. */
1248 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb
, tt
)
1251 "%% Failed to fetch configuration transactions.\n");
1255 /* Dump the generated table. */
1256 if (tt
->nrows
> 1) {
1259 table
= ttable_dump(tt
, "\n");
1260 vty_out(vty
, "%s\n", table
);
1261 XFREE(MTYPE_TMP
, table
);
1263 vty_out(vty
, "No configuration transactions to display.\n\n");
1269 #endif /* HAVE_CONFIG_ROLLBACKS */
1271 DEFPY (show_config_transaction
,
1272 show_config_transaction_cmd
,
1273 "show configuration transaction\
1275 (1-4294967295)$transaction_id\
1276 [<json$json|xml$xml> [translate WORD$translator_family]]\
1278 with-defaults$with_defaults\
1283 "Configuration information\n"
1284 "Configuration transaction\n"
1286 "Change output format to JSON\n"
1287 "Change output format to XML\n"
1288 "Translate output\n"
1289 "YANG module translator\n"
1290 "Show default values\n"
1291 "Show changes compared to the previous transaction\n")
1293 #ifdef HAVE_CONFIG_ROLLBACKS
1294 if (transaction_id
) {
1295 struct nb_config
*config
;
1296 enum nb_cfg_format format
;
1297 struct yang_translator
*translator
= NULL
;
1300 format
= NB_CFG_FMT_JSON
;
1302 format
= NB_CFG_FMT_XML
;
1304 format
= NB_CFG_FMT_CMDS
;
1306 if (translator_family
) {
1307 translator
= yang_translator_find(translator_family
);
1310 "%% Module translator \"%s\" not found\n",
1316 config
= nb_db_transaction_load(transaction_id
);
1318 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1319 (unsigned int)transaction_id
);
1324 struct nb_config
*prev_config
;
1327 /* NOTE: this can be NULL. */
1329 nb_db_transaction_load(transaction_id
- 1);
1331 ret
= nb_cli_show_config_compare(
1332 vty
, prev_config
, config
, format
, translator
);
1334 nb_config_free(prev_config
);
1335 nb_config_free(config
);
1340 nb_cli_show_config(vty
, config
, format
, translator
,
1342 nb_config_free(config
);
1347 return nb_cli_show_transactions(vty
);
1350 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1352 #endif /* HAVE_CONFIG_ROLLBACKS */
1355 static int nb_cli_oper_data_cb(const struct lysc_node
*snode
,
1356 struct yang_translator
*translator
,
1357 struct yang_data
*data
, void *arg
)
1359 struct lyd_node
*dnode
= arg
;
1360 struct ly_ctx
*ly_ctx
;
1365 ret
= yang_translate_xpath(translator
,
1366 YANG_TRANSLATE_FROM_NATIVE
,
1367 data
->xpath
, sizeof(data
->xpath
));
1369 case YANG_TRANSLATE_SUCCESS
:
1371 case YANG_TRANSLATE_NOTFOUND
:
1373 case YANG_TRANSLATE_FAILURE
:
1377 ly_ctx
= translator
->ly_ctx
;
1379 ly_ctx
= ly_native_ctx
;
1382 lyd_new_path(dnode
, ly_ctx
, data
->xpath
, (void *)data
->value
,
1383 LYD_NEW_PATH_UPDATE
, &dnode
);
1385 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_new_path(%s) failed: %s",
1386 __func__
, data
->xpath
, ly_errmsg(ly_native_ctx
));
1391 yang_data_free(data
);
1395 yang_data_free(data
);
1399 DEFPY (show_yang_operational_data
,
1400 show_yang_operational_data_cmd
,
1401 "show yang operational-data XPATH$xpath\
1403 format <json$json|xml$xml>\
1404 |translate WORD$translator_family\
1405 |with-config$with_config\
1408 "YANG information\n"
1409 "Show YANG operational data\n"
1410 "XPath expression specifying the YANG data path\n"
1411 "Set the output format\n"
1412 "JavaScript Object Notation\n"
1413 "Extensible Markup Language\n"
1414 "Translate operational data\n"
1415 "YANG module translator\n"
1416 "Merge configuration data\n")
1419 struct yang_translator
*translator
= NULL
;
1420 struct ly_ctx
*ly_ctx
;
1421 struct lyd_node
*dnode
;
1423 uint32_t print_options
= LYD_PRINT_WITHSIBLINGS
;
1430 if (translator_family
) {
1431 translator
= yang_translator_find(translator_family
);
1433 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1438 ly_ctx
= translator
->ly_ctx
;
1440 ly_ctx
= ly_native_ctx
;
1443 dnode
= yang_dnode_new(ly_ctx
, false);
1444 if (nb_oper_data_iterate(xpath
, translator
, 0, nb_cli_oper_data_cb
,
1447 vty_out(vty
, "%% Failed to fetch operational data.\n");
1448 yang_dnode_free(dnode
);
1452 if (with_config
&& yang_dnode_exists(running_config
->dnode
, xpath
)) {
1453 struct lyd_node
*config_dnode
=
1454 yang_dnode_get(running_config
->dnode
, xpath
);
1455 if (config_dnode
!= NULL
) {
1456 lyd_merge_tree(&dnode
, yang_dnode_dup(config_dnode
),
1457 LYD_MERGE_DESTRUCT
);
1458 print_options
|= LYD_PRINT_WD_ALL
;
1462 (void)lyd_validate_all(&dnode
, ly_ctx
, 0, NULL
);
1464 /* Display the data. */
1465 if (lyd_print_mem(&strp
, dnode
, format
, print_options
) != 0 || !strp
) {
1466 vty_out(vty
, "%% Failed to display operational data.\n");
1467 yang_dnode_free(dnode
);
1470 vty_out(vty
, "%s", strp
);
1472 yang_dnode_free(dnode
);
1477 DEFPY (show_yang_module
,
1478 show_yang_module_cmd
,
1479 "show yang module [module-translator WORD$translator_family]",
1481 "YANG information\n"
1482 "Show loaded modules\n"
1483 "YANG module translator\n"
1484 "YANG module translator\n")
1486 struct ly_ctx
*ly_ctx
;
1487 struct yang_translator
*translator
= NULL
;
1488 const struct lys_module
*module
;
1492 if (translator_family
) {
1493 translator
= yang_translator_find(translator_family
);
1495 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1499 ly_ctx
= translator
->ly_ctx
;
1501 ly_ctx
= ly_native_ctx
;
1503 /* Prepare table. */
1504 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1505 ttable_add_row(tt
, "Module|Version|Revision|Flags|Namespace");
1506 tt
->style
.cell
.rpad
= 2;
1507 tt
->style
.corner
= '+';
1509 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1511 while ((module
= ly_ctx_get_module_iter(ly_ctx
, &idx
))) {
1514 snprintf(flags
, sizeof(flags
), "%c%c",
1515 module
->implemented
? 'I' : ' ',
1516 LY_ARRAY_COUNT(module
->deviated_by
) ? 'D' : ' ');
1518 ttable_add_row(tt
, "%s|%s|%s|%s|%s", module
->name
,
1519 (module
->parsed
->version
== 2) ? "1.1" : "1.0",
1520 module
->revision
? module
->revision
: "-", flags
,
1524 /* Dump the generated table. */
1525 if (tt
->nrows
> 1) {
1528 vty_out(vty
, " Flags: I - Implemented, D - Deviated\n\n");
1530 table
= ttable_dump(tt
, "\n");
1531 vty_out(vty
, "%s\n", table
);
1532 XFREE(MTYPE_TMP
, table
);
1534 vty_out(vty
, "No YANG modules to display.\n\n");
1541 DEFPY(show_yang_module_detail
, show_yang_module_detail_cmd
,
1543 [module-translator WORD$translator_family]\
1544 WORD$module_name <compiled$compiled|summary|tree$tree|yang$yang|yin$yin>",
1546 "YANG information\n"
1547 "Show loaded modules\n"
1548 "YANG module translator\n"
1549 "YANG module translator\n"
1551 "Display compiled module in YANG format\n"
1552 "Display summary information about the module\n"
1553 "Display module in the tree (RFC 8340) format\n"
1554 "Display module in the YANG format\n"
1555 "Display module in the YIN format\n")
1557 struct ly_ctx
*ly_ctx
;
1558 struct yang_translator
*translator
= NULL
;
1559 const struct lys_module
*module
;
1560 LYS_OUTFORMAT format
;
1563 if (translator_family
) {
1564 translator
= yang_translator_find(translator_family
);
1566 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1570 ly_ctx
= translator
->ly_ctx
;
1572 ly_ctx
= ly_native_ctx
;
1574 module
= ly_ctx_get_module_latest(ly_ctx
, module_name
);
1576 vty_out(vty
, "%% Module \"%s\" not found\n", module_name
);
1581 format
= LYS_OUT_YANG
;
1583 format
= LYS_OUT_YIN
;
1585 format
= LYS_OUT_YANG_COMPILED
;
1587 format
= LYS_OUT_TREE
;
1590 "%% libyang v2 does not currently support summary\n");
1594 if (lys_print_mem(&strp
, module
, format
, 0) == 0) {
1595 vty_out(vty
, "%s\n", strp
);
1599 vty_out(vty
, "%% Error generating module information\n");
1606 DEFPY (show_yang_module_translator
,
1607 show_yang_module_translator_cmd
,
1608 "show yang module-translator",
1610 "YANG information\n"
1611 "Show loaded YANG module translators\n")
1613 struct yang_translator
*translator
;
1616 /* Prepare table. */
1617 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1618 ttable_add_row(tt
, "Family|Module|Deviations|Coverage (%%)");
1619 tt
->style
.cell
.rpad
= 2;
1620 tt
->style
.corner
= '+';
1622 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1624 RB_FOREACH (translator
, yang_translators
, &yang_translators
) {
1625 struct yang_tmodule
*tmodule
;
1626 struct listnode
*ln
;
1628 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
1629 ttable_add_row(tt
, "%s|%s|%s|%.2f", translator
->family
,
1630 tmodule
->module
->name
,
1631 tmodule
->deviations
->name
,
1636 /* Dump the generated table. */
1637 if (tt
->nrows
> 1) {
1640 table
= ttable_dump(tt
, "\n");
1641 vty_out(vty
, "%s\n", table
);
1642 XFREE(MTYPE_TMP
, table
);
1644 vty_out(vty
, "No YANG module translators to display.\n\n");
1651 #ifdef HAVE_CONFIG_ROLLBACKS
1652 static int nb_cli_rollback_configuration(struct vty
*vty
,
1653 uint32_t transaction_id
)
1655 struct nb_context context
= {};
1656 struct nb_config
*candidate
;
1658 char errmsg
[BUFSIZ
] = {0};
1661 candidate
= nb_db_transaction_load(transaction_id
);
1663 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1668 snprintf(comment
, sizeof(comment
), "Rollback to transaction %u",
1671 context
.client
= NB_CLIENT_CLI
;
1673 ret
= nb_candidate_commit(context
, candidate
, true, comment
, NULL
,
1674 errmsg
, sizeof(errmsg
));
1675 nb_config_free(candidate
);
1679 "%% Configuration was successfully rolled back.\n\n");
1680 /* Print warnings (if any). */
1681 if (strlen(errmsg
) > 0)
1682 vty_out(vty
, "%s\n", errmsg
);
1684 case NB_ERR_NO_CHANGES
:
1686 "%% Aborting - no configuration changes detected.\n\n");
1689 vty_out(vty
, "%% Rollback failed.\n\n");
1690 vty_show_nb_errors(vty
, ret
, errmsg
);
1694 #endif /* HAVE_CONFIG_ROLLBACKS */
1696 DEFPY (rollback_config
,
1697 rollback_config_cmd
,
1698 "rollback configuration (1-4294967295)$transaction_id",
1699 "Rollback to a previous state\n"
1700 "Running configuration\n"
1703 #ifdef HAVE_CONFIG_ROLLBACKS
1704 return nb_cli_rollback_configuration(vty
, transaction_id
);
1707 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1709 #endif /* HAVE_CONFIG_ROLLBACKS */
1712 /* Debug CLI commands. */
1713 static struct debug
*nb_debugs
[] = {
1714 &nb_dbg_cbs_config
, &nb_dbg_cbs_state
, &nb_dbg_cbs_rpc
,
1715 &nb_dbg_notif
, &nb_dbg_events
, &nb_dbg_libyang
,
1718 static const char *const nb_debugs_conflines
[] = {
1719 "debug northbound callbacks configuration",
1720 "debug northbound callbacks state",
1721 "debug northbound callbacks rpc",
1722 "debug northbound notifications",
1723 "debug northbound events",
1724 "debug northbound libyang",
1727 DEFINE_HOOK(nb_client_debug_set_all
, (uint32_t flags
, bool set
), (flags
, set
));
1729 static void nb_debug_set_all(uint32_t flags
, bool set
)
1731 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++) {
1732 DEBUG_FLAGS_SET(nb_debugs
[i
], flags
, set
);
1734 /* If all modes have been turned off, don't preserve options. */
1735 if (!DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_ALL
))
1736 DEBUG_CLEAR(nb_debugs
[i
]);
1739 hook_call(nb_client_debug_set_all
, flags
, set
);
1744 "[no] debug northbound\
1746 callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\
1747 |notifications$notifications\
1753 "Northbound debugging\n"
1760 "libyang debugging\n")
1762 uint32_t mode
= DEBUG_NODE2MODE(vty
->node
);
1765 bool none
= (!cbs_cfg
&& !cbs_state
&& !cbs_rpc
);
1767 if (none
|| cbs_cfg
)
1768 DEBUG_MODE_SET(&nb_dbg_cbs_config
, mode
, !no
);
1769 if (none
|| cbs_state
)
1770 DEBUG_MODE_SET(&nb_dbg_cbs_state
, mode
, !no
);
1771 if (none
|| cbs_rpc
)
1772 DEBUG_MODE_SET(&nb_dbg_cbs_rpc
, mode
, !no
);
1775 DEBUG_MODE_SET(&nb_dbg_notif
, mode
, !no
);
1777 DEBUG_MODE_SET(&nb_dbg_events
, mode
, !no
);
1779 DEBUG_MODE_SET(&nb_dbg_libyang
, mode
, !no
);
1780 yang_debugging_set(!no
);
1783 /* no specific debug --> act on all of them */
1784 if (strmatch(argv
[argc
- 1]->text
, "northbound")) {
1785 nb_debug_set_all(mode
, !no
);
1786 yang_debugging_set(!no
);
1792 DEFINE_HOOK(nb_client_debug_config_write
, (struct vty
*vty
), (vty
));
1794 static int nb_debug_config_write(struct vty
*vty
)
1796 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++)
1797 if (DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_CONF
))
1798 vty_out(vty
, "%s\n", nb_debugs_conflines
[i
]);
1800 hook_call(nb_client_debug_config_write
, vty
);
1805 static struct debug_callbacks nb_dbg_cbs
= {.debug_set_all
= nb_debug_set_all
};
1806 static struct cmd_node nb_debug_node
= {
1807 .name
= "northbound debug",
1808 .node
= NORTHBOUND_DEBUG_NODE
,
1810 .config_write
= nb_debug_config_write
,
1813 void nb_cli_install_default(int node
)
1815 _install_element(node
, &show_config_candidate_section_cmd
);
1817 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL
)
1820 _install_element(node
, &config_commit_cmd
);
1821 _install_element(node
, &config_commit_comment_cmd
);
1822 _install_element(node
, &config_commit_check_cmd
);
1823 _install_element(node
, &config_update_cmd
);
1824 _install_element(node
, &config_discard_cmd
);
1825 _install_element(node
, &show_config_running_cmd
);
1826 _install_element(node
, &show_config_candidate_cmd
);
1827 _install_element(node
, &show_config_compare_cmd
);
1828 _install_element(node
, &show_config_transaction_cmd
);
1831 /* YANG module autocomplete. */
1832 static void yang_module_autocomplete(vector comps
, struct cmd_token
*token
)
1834 const struct lys_module
*module
;
1835 struct yang_translator
*module_tr
;
1839 while ((module
= ly_ctx_get_module_iter(ly_native_ctx
, &idx
)))
1840 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1842 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
) {
1844 while ((module
= ly_ctx_get_module_iter(module_tr
->ly_ctx
,
1847 XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1851 /* YANG module translator autocomplete. */
1852 static void yang_translator_autocomplete(vector comps
, struct cmd_token
*token
)
1854 struct yang_translator
*module_tr
;
1856 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
)
1857 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module_tr
->family
));
1860 static const struct cmd_variable_handler yang_var_handlers
[] = {
1861 {.varname
= "module_name", .completions
= yang_module_autocomplete
},
1862 {.varname
= "translator_family",
1863 .completions
= yang_translator_autocomplete
},
1864 {.completions
= NULL
}};
1866 void nb_cli_init(struct thread_master
*tm
)
1870 /* Initialize the shared candidate configuration. */
1871 vty_shared_candidate_config
= nb_config_new(NULL
);
1873 debug_init(&nb_dbg_cbs
);
1875 install_node(&nb_debug_node
);
1876 install_element(ENABLE_NODE
, &debug_nb_cmd
);
1877 install_element(CONFIG_NODE
, &debug_nb_cmd
);
1879 /* Install commands specific to the transaction-base mode. */
1880 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL
) {
1881 install_element(ENABLE_NODE
, &config_exclusive_cmd
);
1882 install_element(ENABLE_NODE
, &config_private_cmd
);
1883 install_element(ENABLE_NODE
,
1884 &show_config_compare_without_candidate_cmd
);
1885 install_element(ENABLE_NODE
, &show_config_transaction_cmd
);
1886 install_element(ENABLE_NODE
, &rollback_config_cmd
);
1887 install_element(ENABLE_NODE
, &clear_config_transactions_cmd
);
1889 install_element(CONFIG_NODE
, &config_load_cmd
);
1890 install_element(CONFIG_NODE
,
1891 &config_database_max_transactions_cmd
);
1894 /* Other commands. */
1895 install_element(ENABLE_NODE
, &show_config_running_cmd
);
1896 install_element(CONFIG_NODE
, &yang_module_translator_load_cmd
);
1897 install_element(CONFIG_NODE
, &yang_module_translator_unload_cmd
);
1898 install_element(ENABLE_NODE
, &show_yang_operational_data_cmd
);
1899 install_element(ENABLE_NODE
, &show_yang_module_cmd
);
1900 install_element(ENABLE_NODE
, &show_yang_module_detail_cmd
);
1901 install_element(ENABLE_NODE
, &show_yang_module_translator_cmd
);
1902 cmd_variable_handler_register(yang_var_handlers
);
1905 void nb_cli_terminate(void)
1907 nb_config_free(vty_shared_candidate_config
);