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 vty_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
,
145 if (xpath_base
== NULL
)
150 /* Edit candidate configuration. */
151 for (size_t i
= 0; i
< vty
->num_cfg_changes
; i
++) {
152 struct vty_cfg_change
*change
= &vty
->cfg_changes
[i
];
153 struct nb_node
*nb_node
;
154 char xpath
[XPATH_MAXLEN
];
155 struct yang_data
*data
;
158 /* Handle relative XPaths. */
159 memset(xpath
, 0, sizeof(xpath
));
160 if (vty
->xpath_index
> 0
161 && (xpath_base
[0] == '.' || change
->xpath
[0] == '.'))
162 strlcpy(xpath
, VTY_CURR_XPATH
, sizeof(xpath
));
164 if (xpath_base
[0] == '.')
165 strlcat(xpath
, xpath_base
+ 1, sizeof(xpath
));
167 strlcat(xpath
, xpath_base
, sizeof(xpath
));
169 if (change
->xpath
[0] == '.')
170 strlcat(xpath
, change
->xpath
+ 1, sizeof(xpath
));
172 strlcpy(xpath
, change
->xpath
, sizeof(xpath
));
174 /* Find the northbound node associated to the data path. */
175 nb_node
= nb_node_find(xpath
);
177 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
178 "%s: unknown data path: %s", __func__
, xpath
);
183 /* If the value is not set, get the default if it exists. */
184 if (change
->value
== NULL
)
185 change
->value
= yang_snode_get_default(nb_node
->snode
);
186 data
= yang_data_new(xpath
, change
->value
);
189 * Ignore "not found" errors when editing the candidate
192 ret
= nb_candidate_edit(vty
->candidate_config
, nb_node
,
193 change
->operation
, xpath
, NULL
, data
);
194 yang_data_free(data
);
195 if (ret
!= NB_OK
&& ret
!= NB_ERR_NOT_FOUND
) {
197 EC_LIB_NB_CANDIDATE_EDIT_ERROR
,
198 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
199 __func__
, nb_operation_name(change
->operation
),
210 * Failure to edit the candidate configuration should never
211 * happen in practice, unless there's a bug in the code. When
212 * that happens, log the error but otherwise ignore it.
214 vty_out(vty
, "%% Failed to edit configuration.\n\n");
216 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
220 * Maybe do an implicit commit when using the classic CLI mode.
222 * NOTE: the implicit commit might be scheduled to run later when
223 * too many commands are being sent at the same time. This is a
224 * protection mechanism where multiple commands are grouped into the
225 * same configuration transaction, allowing them to be processed much
228 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
) {
230 if (vty
->pending_commit
)
231 return nb_cli_pending_commit_check(vty
);
232 } else if (vty
->pending_allowed
)
233 return nb_cli_schedule_command(vty
);
234 assert(!vty
->pending_commit
);
235 return nb_cli_classic_commit(vty
);
241 int nb_cli_apply_changes(struct vty
*vty
, const char *xpath_base_fmt
, ...)
243 char xpath_base
[XPATH_MAXLEN
] = {};
245 /* Parse the base XPath format string. */
246 if (xpath_base_fmt
) {
249 va_start(ap
, xpath_base_fmt
);
250 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
253 return nb_cli_apply_changes_internal(vty
, xpath_base
, false);
256 int nb_cli_apply_changes_clear_pending(struct vty
*vty
,
257 const char *xpath_base_fmt
, ...)
259 char xpath_base
[XPATH_MAXLEN
] = {};
261 /* Parse the base XPath format string. */
262 if (xpath_base_fmt
) {
265 va_start(ap
, xpath_base_fmt
);
266 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
269 return nb_cli_apply_changes_internal(vty
, xpath_base
, true);
272 int nb_cli_rpc(struct vty
*vty
, const char *xpath
, struct list
*input
,
275 struct nb_node
*nb_node
;
277 char errmsg
[BUFSIZ
] = {0};
279 nb_node
= nb_node_find(xpath
);
281 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
282 "%s: unknown data path: %s", __func__
, xpath
);
286 ret
= nb_callback_rpc(nb_node
, xpath
, input
, output
, errmsg
,
293 vty_show_nb_errors(vty
, ret
, errmsg
);
298 void nb_cli_confirmed_commit_clean(struct vty
*vty
)
300 thread_cancel(&vty
->t_confirmed_commit_timeout
);
301 nb_config_free(vty
->confirmed_commit_rollback
);
302 vty
->confirmed_commit_rollback
= NULL
;
305 int nb_cli_confirmed_commit_rollback(struct vty
*vty
)
307 struct nb_context context
= {};
308 uint32_t transaction_id
;
309 char errmsg
[BUFSIZ
] = {0};
312 /* Perform the rollback. */
313 context
.client
= NB_CLIENT_CLI
;
315 ret
= nb_candidate_commit(
316 &context
, vty
->confirmed_commit_rollback
, true,
317 "Rollback to previous configuration - confirmed commit has timed out",
318 &transaction_id
, errmsg
, sizeof(errmsg
));
321 "Rollback performed successfully (Transaction ID #%u).\n",
323 /* Print warnings (if any). */
324 if (strlen(errmsg
) > 0)
325 vty_out(vty
, "%s\n", errmsg
);
328 "Failed to rollback to previous configuration.\n\n");
329 vty_show_nb_errors(vty
, ret
, errmsg
);
335 static void nb_cli_confirmed_commit_timeout(struct thread
*thread
)
337 struct vty
*vty
= THREAD_ARG(thread
);
339 /* XXX: broadcast this message to all logged-in users? */
341 "\nConfirmed commit has timed out, rolling back to previous configuration.\n\n");
343 nb_cli_confirmed_commit_rollback(vty
);
344 nb_cli_confirmed_commit_clean(vty
);
347 static int nb_cli_commit(struct vty
*vty
, bool force
,
348 unsigned int confirmed_timeout
, char *comment
)
350 struct nb_context context
= {};
351 uint32_t transaction_id
= 0;
352 char errmsg
[BUFSIZ
] = {0};
355 /* Check if there's a pending confirmed commit. */
356 if (vty
->t_confirmed_commit_timeout
) {
357 if (confirmed_timeout
) {
358 /* Reset timeout if "commit confirmed" is used again. */
360 "%% Resetting confirmed-commit timeout to %u minute(s)\n\n",
363 thread_cancel(&vty
->t_confirmed_commit_timeout
);
364 thread_add_timer(master
,
365 nb_cli_confirmed_commit_timeout
, vty
,
366 confirmed_timeout
* 60,
367 &vty
->t_confirmed_commit_timeout
);
369 /* Accept commit confirmation. */
370 vty_out(vty
, "%% Commit complete.\n\n");
371 nb_cli_confirmed_commit_clean(vty
);
376 /* "force" parameter. */
377 if (!force
&& nb_candidate_needs_update(vty
->candidate_config
)) {
379 "%% Candidate configuration needs to be updated before commit.\n\n");
381 "Use the \"update\" command or \"commit force\".\n");
385 /* "confirm" parameter. */
386 if (confirmed_timeout
) {
387 vty
->confirmed_commit_rollback
= nb_config_dup(running_config
);
389 vty
->t_confirmed_commit_timeout
= NULL
;
390 thread_add_timer(master
, nb_cli_confirmed_commit_timeout
, vty
,
391 confirmed_timeout
* 60,
392 &vty
->t_confirmed_commit_timeout
);
395 context
.client
= NB_CLIENT_CLI
;
397 ret
= nb_candidate_commit(&context
, vty
->candidate_config
, true,
398 comment
, &transaction_id
, errmsg
,
401 /* Map northbound return code to CLI return code. */
404 nb_config_replace(vty
->candidate_config_base
, running_config
,
407 "%% Configuration committed successfully (Transaction ID #%u).\n\n",
409 /* Print warnings (if any). */
410 if (strlen(errmsg
) > 0)
411 vty_out(vty
, "%s\n", errmsg
);
413 case NB_ERR_NO_CHANGES
:
414 vty_out(vty
, "%% No configuration changes to commit.\n\n");
418 "%% Failed to commit candidate configuration.\n\n");
419 vty_show_nb_errors(vty
, ret
, errmsg
);
424 static int nb_cli_candidate_load_file(struct vty
*vty
,
425 enum nb_cfg_format format
,
426 struct yang_translator
*translator
,
427 const char *path
, bool replace
)
429 struct nb_config
*loaded_config
= NULL
;
430 struct lyd_node
*dnode
;
431 struct ly_ctx
*ly_ctx
;
437 case NB_CFG_FMT_CMDS
:
438 loaded_config
= nb_config_new(NULL
);
439 if (!vty_read_config(loaded_config
, path
, config_default
)) {
440 vty_out(vty
, "%% Failed to load configuration.\n\n");
442 "Please check the logs for more details.\n");
443 nb_config_free(loaded_config
);
447 case NB_CFG_FMT_JSON
:
449 ly_format
= (format
== NB_CFG_FMT_JSON
) ? LYD_JSON
: LYD_XML
;
451 ly_ctx
= translator
? translator
->ly_ctx
: ly_native_ctx
;
452 err
= lyd_parse_data_path(ly_ctx
, path
, ly_format
,
453 LYD_PARSE_ONLY
| LYD_PARSE_NO_STATE
,
456 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_path() failed",
458 vty_out(vty
, "%% Failed to load configuration:\n\n");
460 yang_print_errors(ly_native_ctx
, buf
,
465 && yang_translate_dnode(translator
,
466 YANG_TRANSLATE_TO_NATIVE
, &dnode
)
467 != YANG_TRANSLATE_SUCCESS
) {
468 vty_out(vty
, "%% Failed to translate configuration\n");
469 yang_dnode_free(dnode
);
472 loaded_config
= nb_config_new(dnode
);
477 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
478 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
481 "%% Failed to merge the loaded configuration:\n\n");
483 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
490 static int nb_cli_candidate_load_transaction(struct vty
*vty
,
491 uint32_t transaction_id
,
494 struct nb_config
*loaded_config
;
497 loaded_config
= nb_db_transaction_load(transaction_id
);
498 if (!loaded_config
) {
499 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
505 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
506 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
509 "%% Failed to merge the loaded configuration:\n\n");
511 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
518 /* Prepare the configuration for display. */
519 void nb_cli_show_config_prepare(struct nb_config
*config
, bool with_defaults
)
521 /* Nothing to do for daemons that don't implement any YANG module. */
522 if (config
->dnode
== NULL
)
526 * Call lyd_validate() only to create default child nodes, ignoring
527 * any possible validation error. This doesn't need to be done when
528 * displaying the running configuration since it's always fully
531 if (config
!= running_config
)
532 (void)lyd_validate_all(&config
->dnode
, ly_native_ctx
,
533 LYD_VALIDATE_NO_STATE
, NULL
);
536 static int lyd_node_cmp(const struct lyd_node
**dnode1
,
537 const struct lyd_node
**dnode2
)
539 struct nb_node
*nb_node
= (*dnode1
)->schema
->priv
;
541 return nb_node
->cbs
.cli_cmp(*dnode1
, *dnode2
);
544 static void show_dnode_children_cmds(struct vty
*vty
,
545 const struct lyd_node
*root
,
548 struct nb_node
*nb_node
, *sort_node
= NULL
;
549 struct listnode
*listnode
;
550 struct lyd_node
*child
;
551 struct list
*sort_list
;
554 LY_LIST_FOR (lyd_child(root
), child
) {
555 nb_node
= child
->schema
->priv
;
558 * We finished processing current list,
559 * it's time to print the config.
561 if (sort_node
&& nb_node
!= sort_node
) {
563 (int (*)(const void **,
564 const void **))lyd_node_cmp
);
566 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
567 nb_cli_show_dnode_cmds(vty
, data
,
570 list_delete(&sort_list
);
575 * If the config needs to be sorted,
576 * then add the dnode to the sorting
577 * list for later processing.
579 if (nb_node
&& nb_node
->cbs
.cli_cmp
) {
582 sort_list
= list_new();
585 listnode_add(sort_list
, child
);
589 nb_cli_show_dnode_cmds(vty
, child
, with_defaults
);
594 (int (*)(const void **, const void **))lyd_node_cmp
);
596 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
597 nb_cli_show_dnode_cmds(vty
, data
, with_defaults
);
599 list_delete(&sort_list
);
604 void nb_cli_show_dnode_cmds(struct vty
*vty
, const struct lyd_node
*root
,
607 struct nb_node
*nb_node
;
609 if (!with_defaults
&& yang_dnode_is_default_recursive(root
))
612 nb_node
= root
->schema
->priv
;
614 if (nb_node
&& nb_node
->cbs
.cli_show
)
615 (*nb_node
->cbs
.cli_show
)(vty
, root
, with_defaults
);
617 if (!(root
->schema
->nodetype
& (LYS_LEAF
| LYS_LEAFLIST
| LYS_ANYDATA
)))
618 show_dnode_children_cmds(vty
, root
, with_defaults
);
620 if (nb_node
&& nb_node
->cbs
.cli_show_end
)
621 (*nb_node
->cbs
.cli_show_end
)(vty
, root
);
624 static void nb_cli_show_config_cmds(struct vty
*vty
, struct nb_config
*config
,
627 struct lyd_node
*root
;
629 vty_out(vty
, "Configuration:\n");
631 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
632 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
634 LY_LIST_FOR (config
->dnode
, root
) {
635 nb_cli_show_dnode_cmds(vty
, root
, with_defaults
);
639 vty_out(vty
, "end\n");
642 static int nb_cli_show_config_libyang(struct vty
*vty
, LYD_FORMAT format
,
643 struct nb_config
*config
,
644 struct yang_translator
*translator
,
647 struct lyd_node
*dnode
;
651 dnode
= yang_dnode_dup(config
->dnode
);
653 && yang_translate_dnode(translator
, YANG_TRANSLATE_FROM_NATIVE
,
655 != YANG_TRANSLATE_SUCCESS
) {
656 vty_out(vty
, "%% Failed to translate configuration\n");
657 yang_dnode_free(dnode
);
661 SET_FLAG(options
, LYD_PRINT_WITHSIBLINGS
);
663 SET_FLAG(options
, LYD_PRINT_WD_ALL
);
665 SET_FLAG(options
, LYD_PRINT_WD_TRIM
);
667 if (lyd_print_mem(&strp
, dnode
, format
, options
) == 0 && strp
) {
668 vty_out(vty
, "%s", strp
);
672 yang_dnode_free(dnode
);
677 static int nb_cli_show_config(struct vty
*vty
, struct nb_config
*config
,
678 enum nb_cfg_format format
,
679 struct yang_translator
*translator
,
682 nb_cli_show_config_prepare(config
, with_defaults
);
685 case NB_CFG_FMT_CMDS
:
686 nb_cli_show_config_cmds(vty
, config
, with_defaults
);
688 case NB_CFG_FMT_JSON
:
689 return nb_cli_show_config_libyang(vty
, LYD_JSON
, config
,
690 translator
, with_defaults
);
692 return nb_cli_show_config_libyang(vty
, LYD_XML
, config
,
693 translator
, with_defaults
);
699 static int nb_write_config(struct nb_config
*config
, enum nb_cfg_format format
,
700 struct yang_translator
*translator
, char *path
,
704 struct vty
*file_vty
;
707 snprintf(path
, pathlen
, "/tmp/frr.tmp.XXXXXXXX");
710 flog_warn(EC_LIB_SYSTEM_CALL
, "%s: mkstemp() failed: %s",
711 __func__
, safe_strerror(errno
));
714 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
715 flog_warn(EC_LIB_SYSTEM_CALL
,
716 "%s: fchmod() failed: %s(%d):", __func__
,
717 safe_strerror(errno
), errno
);
721 /* Make vty for configuration file. */
722 file_vty
= vty_new();
724 file_vty
->type
= VTY_FILE
;
726 ret
= nb_cli_show_config(file_vty
, config
, format
, translator
,
733 static int nb_cli_show_config_compare(struct vty
*vty
,
734 struct nb_config
*config1
,
735 struct nb_config
*config2
,
736 enum nb_cfg_format format
,
737 struct yang_translator
*translator
)
739 char config1_path
[256];
740 char config2_path
[256];
741 char command
[BUFSIZ
];
746 if (nb_write_config(config1
, format
, translator
, config1_path
,
747 sizeof(config1_path
))
749 vty_out(vty
, "%% Failed to process configurations.\n\n");
752 if (nb_write_config(config2
, format
, translator
, config2_path
,
753 sizeof(config2_path
))
755 vty_out(vty
, "%% Failed to process configurations.\n\n");
756 unlink(config1_path
);
760 snprintf(command
, sizeof(command
), "diff -u %s %s", config1_path
,
762 fp
= popen(command
, "r");
764 vty_out(vty
, "%% Failed to generate configuration diff.\n\n");
765 unlink(config1_path
);
766 unlink(config2_path
);
769 /* Print diff line by line. */
770 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
773 vty_out(vty
, "%s", line
);
777 unlink(config1_path
);
778 unlink(config2_path
);
783 /* Configure exclusively from this terminal. */
784 DEFUN (config_exclusive
,
785 config_exclusive_cmd
,
786 "configure exclusive",
787 "Configuration from vty interface\n"
788 "Configure exclusively from this terminal\n")
790 return vty_config_enter(vty
, true, true);
793 /* Configure using a private candidate configuration. */
794 DEFUN (config_private
,
797 "Configuration from vty interface\n"
798 "Configure using a private candidate configuration\n")
800 return vty_config_enter(vty
, true, false);
803 DEFPY (config_commit
,
805 "commit [{force$force|confirmed (1-60)}]",
806 "Commit changes into the running configuration\n"
807 "Force commit even if the candidate is outdated\n"
808 "Rollback this commit unless there is a confirming commit\n"
809 "Timeout in minutes for the commit to be confirmed\n")
811 return nb_cli_commit(vty
, !!force
, confirmed
, NULL
);
814 DEFPY (config_commit_comment
,
815 config_commit_comment_cmd
,
816 "commit [{force$force|confirmed (1-60)}] comment LINE...",
817 "Commit changes into the running configuration\n"
818 "Force commit even if the candidate is outdated\n"
819 "Rollback this commit unless there is a confirming commit\n"
820 "Timeout in minutes for the commit to be confirmed\n"
821 "Assign a comment to this commit\n"
822 "Comment for this commit (Max 80 characters)\n")
828 argv_find(argv
, argc
, "LINE", &idx
);
829 comment
= argv_concat(argv
, argc
, idx
);
830 ret
= nb_cli_commit(vty
, !!force
, confirmed
, comment
);
831 XFREE(MTYPE_TMP
, comment
);
836 DEFPY (config_commit_check
,
837 config_commit_check_cmd
,
839 "Commit changes into the running configuration\n"
840 "Check if the configuration changes are valid\n")
842 struct nb_context context
= {};
843 char errmsg
[BUFSIZ
] = {0};
846 context
.client
= NB_CLIENT_CLI
;
848 ret
= nb_candidate_validate(&context
, vty
->candidate_config
, errmsg
,
852 "%% Failed to validate candidate configuration.\n\n");
853 vty_show_nb_errors(vty
, ret
, errmsg
);
857 vty_out(vty
, "%% Candidate configuration validated successfully.\n\n");
862 DEFPY (config_update
,
865 "Update candidate configuration\n")
867 if (!nb_candidate_needs_update(vty
->candidate_config
)) {
868 vty_out(vty
, "%% Update is not necessary.\n\n");
872 if (nb_candidate_update(vty
->candidate_config
) != NB_OK
) {
874 "%% Failed to update the candidate configuration.\n\n");
875 vty_out(vty
, "Please check the logs for more details.\n");
879 nb_config_replace(vty
->candidate_config_base
, running_config
, true);
881 vty_out(vty
, "%% Candidate configuration updated successfully.\n\n");
886 DEFPY (config_discard
,
889 "Discard changes in the candidate configuration\n")
891 nb_config_replace(vty
->candidate_config
, vty
->candidate_config_base
,
901 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
902 |transaction (1-4294967295)$tid\
905 "Configuration related settings\n"
906 "Load configuration into candidate\n"
907 "Load configuration file into candidate\n"
908 "Load configuration file in JSON format\n"
909 "Load configuration file in XML format\n"
910 "Translate configuration file\n"
911 "YANG module translator\n"
912 "Configuration file name (full path)\n"
913 "Load configuration from transaction into candidate\n"
915 "Replace instead of merge\n")
918 enum nb_cfg_format format
;
919 struct yang_translator
*translator
= NULL
;
922 format
= NB_CFG_FMT_JSON
;
924 format
= NB_CFG_FMT_XML
;
926 format
= NB_CFG_FMT_CMDS
;
928 if (translator_family
) {
929 translator
= yang_translator_find(translator_family
);
932 "%% Module translator \"%s\" not found\n",
938 return nb_cli_candidate_load_file(vty
, format
, translator
,
939 filename
, !!replace
);
942 return nb_cli_candidate_load_transaction(vty
, tid
, !!replace
);
945 DEFPY (show_config_running
,
946 show_config_running_cmd
,
947 "show configuration running\
948 [<json$json|xml$xml> [translate WORD$translator_family]]\
949 [with-defaults$with_defaults]",
951 "Configuration information\n"
952 "Running configuration\n"
953 "Change output format to JSON\n"
954 "Change output format to XML\n"
956 "YANG module translator\n"
957 "Show default values\n")
960 enum nb_cfg_format format
;
961 struct yang_translator
*translator
= NULL
;
964 format
= NB_CFG_FMT_JSON
;
966 format
= NB_CFG_FMT_XML
;
968 format
= NB_CFG_FMT_CMDS
;
970 if (translator_family
) {
971 translator
= yang_translator_find(translator_family
);
973 vty_out(vty
, "%% Module translator \"%s\" not found\n",
979 nb_cli_show_config(vty
, running_config
, format
, translator
,
985 DEFPY (show_config_candidate
,
986 show_config_candidate_cmd
,
987 "show configuration candidate\
988 [<json$json|xml$xml> [translate WORD$translator_family]]\
990 with-defaults$with_defaults\
994 "Configuration information\n"
995 "Candidate configuration\n"
996 "Change output format to JSON\n"
997 "Change output format to XML\n"
999 "YANG module translator\n"
1000 "Show default values\n"
1001 "Show changes applied in the candidate configuration\n")
1004 enum nb_cfg_format format
;
1005 struct yang_translator
*translator
= NULL
;
1008 format
= NB_CFG_FMT_JSON
;
1010 format
= NB_CFG_FMT_XML
;
1012 format
= NB_CFG_FMT_CMDS
;
1014 if (translator_family
) {
1015 translator
= yang_translator_find(translator_family
);
1017 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1024 return nb_cli_show_config_compare(
1025 vty
, vty
->candidate_config_base
, vty
->candidate_config
,
1026 format
, translator
);
1028 nb_cli_show_config(vty
, vty
->candidate_config
, format
, translator
,
1034 DEFPY (show_config_candidate_section
,
1035 show_config_candidate_section_cmd
,
1039 struct lyd_node
*dnode
;
1041 /* Top-level configuration node, display everything. */
1042 if (vty
->xpath_index
== 0)
1043 return nb_cli_show_config(vty
, vty
->candidate_config
,
1044 NB_CFG_FMT_CMDS
, NULL
, false);
1046 /* Display only the current section of the candidate configuration. */
1047 dnode
= yang_dnode_get(vty
->candidate_config
->dnode
, VTY_CURR_XPATH
);
1049 /* Shouldn't happen. */
1052 nb_cli_show_dnode_cmds(vty
, dnode
, 0);
1053 vty_out(vty
, "!\n");
1058 DEFPY (show_config_compare
,
1059 show_config_compare_cmd
,
1060 "show configuration compare\
1062 candidate$c1_candidate\
1063 |running$c1_running\
1064 |transaction (1-4294967295)$c1_tid\
1067 candidate$c2_candidate\
1068 |running$c2_running\
1069 |transaction (1-4294967295)$c2_tid\
1071 [<json$json|xml$xml> [translate WORD$translator_family]]",
1073 "Configuration information\n"
1074 "Compare two different configurations\n"
1075 "Candidate configuration\n"
1076 "Running configuration\n"
1077 "Configuration transaction\n"
1079 "Candidate configuration\n"
1080 "Running configuration\n"
1081 "Configuration transaction\n"
1083 "Change output format to JSON\n"
1084 "Change output format to XML\n"
1085 "Translate output\n"
1086 "YANG module translator\n")
1088 enum nb_cfg_format format
;
1089 struct yang_translator
*translator
= NULL
;
1090 struct nb_config
*config1
, *config_transaction1
= NULL
;
1091 struct nb_config
*config2
, *config_transaction2
= NULL
;
1092 int ret
= CMD_WARNING
;
1095 config1
= vty
->candidate_config
;
1096 else if (c1_running
)
1097 config1
= running_config
;
1099 config_transaction1
= nb_db_transaction_load(c1_tid
);
1100 if (!config_transaction1
) {
1101 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1102 (unsigned int)c1_tid
);
1105 config1
= config_transaction1
;
1109 config2
= vty
->candidate_config
;
1110 else if (c2_running
)
1111 config2
= running_config
;
1113 config_transaction2
= nb_db_transaction_load(c2_tid
);
1114 if (!config_transaction2
) {
1115 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1116 (unsigned int)c2_tid
);
1119 config2
= config_transaction2
;
1123 format
= NB_CFG_FMT_JSON
;
1125 format
= NB_CFG_FMT_XML
;
1127 format
= NB_CFG_FMT_CMDS
;
1129 if (translator_family
) {
1130 translator
= yang_translator_find(translator_family
);
1132 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1138 ret
= nb_cli_show_config_compare(vty
, config1
, config2
, format
,
1141 if (config_transaction1
)
1142 nb_config_free(config_transaction1
);
1143 if (config_transaction2
)
1144 nb_config_free(config_transaction2
);
1150 * Stripped down version of the "show configuration compare" command.
1151 * The "candidate" option is not present so the command can be installed in
1154 ALIAS (show_config_compare
,
1155 show_config_compare_without_candidate_cmd
,
1156 "show configuration compare\
1159 |transaction (1-4294967295)$c1_tid\
1163 |transaction (1-4294967295)$c2_tid\
1165 [<json$json|xml$xml> [translate WORD$translator_family]]",
1167 "Configuration information\n"
1168 "Compare two different configurations\n"
1169 "Running configuration\n"
1170 "Configuration transaction\n"
1172 "Running configuration\n"
1173 "Configuration transaction\n"
1175 "Change output format to JSON\n"
1176 "Change output format to XML\n"
1177 "Translate output\n"
1178 "YANG module translator\n")
1180 DEFPY (clear_config_transactions
,
1181 clear_config_transactions_cmd
,
1182 "clear configuration transactions oldest (1-100)$n",
1184 "Configuration activity\n"
1185 "Delete transactions from the transactions log\n"
1186 "Delete oldest <n> transactions\n"
1187 "Number of transactions to delete\n")
1189 #ifdef HAVE_CONFIG_ROLLBACKS
1190 if (nb_db_clear_transactions(n
) != NB_OK
) {
1191 vty_out(vty
, "%% Failed to delete transactions.\n\n");
1196 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1197 #endif /* HAVE_CONFIG_ROLLBACKS */
1202 DEFPY (config_database_max_transactions
,
1203 config_database_max_transactions_cmd
,
1204 "configuration database max-transactions (1-100)$max",
1205 "Configuration related settings\n"
1206 "Configuration database\n"
1207 "Set the maximum number of transactions to store\n"
1208 "Number of transactions\n")
1210 #ifdef HAVE_CONFIG_ROLLBACKS
1211 if (nb_db_set_max_transactions(max
) != NB_OK
) {
1213 "%% Failed to update the maximum number of transactions.\n\n");
1217 "%% Maximum number of transactions updated successfully.\n\n");
1220 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1221 #endif /* HAVE_CONFIG_ROLLBACKS */
1226 DEFPY (yang_module_translator_load
,
1227 yang_module_translator_load_cmd
,
1228 "yang module-translator load FILENAME$filename",
1229 "YANG related settings\n"
1230 "YANG module translator\n"
1231 "Load YANG module translator\n"
1232 "File name (full path)\n")
1234 struct yang_translator
*translator
;
1236 translator
= yang_translator_load(filename
);
1238 vty_out(vty
, "%% Failed to load \"%s\"\n\n", filename
);
1239 vty_out(vty
, "Please check the logs for more details.\n");
1243 vty_out(vty
, "%% Module translator \"%s\" loaded successfully.\n\n",
1244 translator
->family
);
1249 DEFPY (yang_module_translator_unload_family
,
1250 yang_module_translator_unload_cmd
,
1251 "yang module-translator unload WORD$translator_family",
1252 "YANG related settings\n"
1253 "YANG module translator\n"
1254 "Unload YANG module translator\n"
1255 "Name of the module translator\n")
1257 struct yang_translator
*translator
;
1259 translator
= yang_translator_find(translator_family
);
1261 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1266 yang_translator_unload(translator
);
1271 #ifdef HAVE_CONFIG_ROLLBACKS
1272 static void nb_cli_show_transactions_cb(void *arg
, int transaction_id
,
1273 const char *client_name
,
1274 const char *date
, const char *comment
)
1276 struct ttable
*tt
= arg
;
1278 ttable_add_row(tt
, "%d|%s|%s|%s", transaction_id
, client_name
, date
,
1282 static int nb_cli_show_transactions(struct vty
*vty
)
1286 /* Prepare table. */
1287 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1288 ttable_add_row(tt
, "Transaction ID|Client|Date|Comment");
1289 tt
->style
.cell
.rpad
= 2;
1290 tt
->style
.corner
= '+';
1292 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1294 /* Fetch transactions from the northbound database. */
1295 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb
, tt
)
1298 "%% Failed to fetch configuration transactions.\n");
1302 /* Dump the generated table. */
1303 if (tt
->nrows
> 1) {
1306 table
= ttable_dump(tt
, "\n");
1307 vty_out(vty
, "%s\n", table
);
1308 XFREE(MTYPE_TMP
, table
);
1310 vty_out(vty
, "No configuration transactions to display.\n\n");
1316 #endif /* HAVE_CONFIG_ROLLBACKS */
1318 DEFPY (show_config_transaction
,
1319 show_config_transaction_cmd
,
1320 "show configuration transaction\
1322 (1-4294967295)$transaction_id\
1323 [<json$json|xml$xml> [translate WORD$translator_family]]\
1325 with-defaults$with_defaults\
1330 "Configuration information\n"
1331 "Configuration transaction\n"
1333 "Change output format to JSON\n"
1334 "Change output format to XML\n"
1335 "Translate output\n"
1336 "YANG module translator\n"
1337 "Show default values\n"
1338 "Show changes compared to the previous transaction\n")
1340 #ifdef HAVE_CONFIG_ROLLBACKS
1341 if (transaction_id
) {
1342 struct nb_config
*config
;
1343 enum nb_cfg_format format
;
1344 struct yang_translator
*translator
= NULL
;
1347 format
= NB_CFG_FMT_JSON
;
1349 format
= NB_CFG_FMT_XML
;
1351 format
= NB_CFG_FMT_CMDS
;
1353 if (translator_family
) {
1354 translator
= yang_translator_find(translator_family
);
1357 "%% Module translator \"%s\" not found\n",
1363 config
= nb_db_transaction_load(transaction_id
);
1365 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1366 (unsigned int)transaction_id
);
1371 struct nb_config
*prev_config
;
1374 /* NOTE: this can be NULL. */
1376 nb_db_transaction_load(transaction_id
- 1);
1378 ret
= nb_cli_show_config_compare(
1379 vty
, prev_config
, config
, format
, translator
);
1381 nb_config_free(prev_config
);
1382 nb_config_free(config
);
1387 nb_cli_show_config(vty
, config
, format
, translator
,
1389 nb_config_free(config
);
1394 return nb_cli_show_transactions(vty
);
1397 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1399 #endif /* HAVE_CONFIG_ROLLBACKS */
1402 static int nb_cli_oper_data_cb(const struct lysc_node
*snode
,
1403 struct yang_translator
*translator
,
1404 struct yang_data
*data
, void *arg
)
1406 struct lyd_node
*dnode
= arg
;
1407 struct ly_ctx
*ly_ctx
;
1412 ret
= yang_translate_xpath(translator
,
1413 YANG_TRANSLATE_FROM_NATIVE
,
1414 data
->xpath
, sizeof(data
->xpath
));
1416 case YANG_TRANSLATE_SUCCESS
:
1418 case YANG_TRANSLATE_NOTFOUND
:
1420 case YANG_TRANSLATE_FAILURE
:
1424 ly_ctx
= translator
->ly_ctx
;
1426 ly_ctx
= ly_native_ctx
;
1429 lyd_new_path(dnode
, ly_ctx
, data
->xpath
, (void *)data
->value
,
1430 LYD_NEW_PATH_UPDATE
, &dnode
);
1432 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_new_path(%s) failed: %s",
1433 __func__
, data
->xpath
, ly_errmsg(ly_native_ctx
));
1438 yang_data_free(data
);
1442 yang_data_free(data
);
1446 DEFPY (show_yang_operational_data
,
1447 show_yang_operational_data_cmd
,
1448 "show yang operational-data XPATH$xpath\
1450 format <json$json|xml$xml>\
1451 |translate WORD$translator_family\
1452 |with-config$with_config\
1455 "YANG information\n"
1456 "Show YANG operational data\n"
1457 "XPath expression specifying the YANG data path\n"
1458 "Set the output format\n"
1459 "JavaScript Object Notation\n"
1460 "Extensible Markup Language\n"
1461 "Translate operational data\n"
1462 "YANG module translator\n"
1463 "Merge configuration data\n")
1466 struct yang_translator
*translator
= NULL
;
1467 struct ly_ctx
*ly_ctx
;
1468 struct lyd_node
*dnode
;
1470 uint32_t print_options
= LYD_PRINT_WITHSIBLINGS
;
1477 if (translator_family
) {
1478 translator
= yang_translator_find(translator_family
);
1480 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1485 ly_ctx
= translator
->ly_ctx
;
1487 ly_ctx
= ly_native_ctx
;
1490 dnode
= yang_dnode_new(ly_ctx
, false);
1491 if (nb_oper_data_iterate(xpath
, translator
, 0, nb_cli_oper_data_cb
,
1494 vty_out(vty
, "%% Failed to fetch operational data.\n");
1495 yang_dnode_free(dnode
);
1499 if (with_config
&& yang_dnode_exists(running_config
->dnode
, xpath
)) {
1500 struct lyd_node
*config_dnode
=
1501 yang_dnode_get(running_config
->dnode
, xpath
);
1502 if (config_dnode
!= NULL
) {
1503 lyd_merge_tree(&dnode
, yang_dnode_dup(config_dnode
),
1504 LYD_MERGE_DESTRUCT
);
1505 print_options
|= LYD_PRINT_WD_ALL
;
1509 (void)lyd_validate_all(&dnode
, ly_ctx
, 0, NULL
);
1511 /* Display the data. */
1512 if (lyd_print_mem(&strp
, dnode
, format
, print_options
) != 0 || !strp
) {
1513 vty_out(vty
, "%% Failed to display operational data.\n");
1514 yang_dnode_free(dnode
);
1517 vty_out(vty
, "%s", strp
);
1519 yang_dnode_free(dnode
);
1524 DEFPY (show_yang_module
,
1525 show_yang_module_cmd
,
1526 "show yang module [module-translator WORD$translator_family]",
1528 "YANG information\n"
1529 "Show loaded modules\n"
1530 "YANG module translator\n"
1531 "YANG module translator\n")
1533 struct ly_ctx
*ly_ctx
;
1534 struct yang_translator
*translator
= NULL
;
1535 const struct lys_module
*module
;
1539 if (translator_family
) {
1540 translator
= yang_translator_find(translator_family
);
1542 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1546 ly_ctx
= translator
->ly_ctx
;
1548 ly_ctx
= ly_native_ctx
;
1550 /* Prepare table. */
1551 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1552 ttable_add_row(tt
, "Module|Version|Revision|Flags|Namespace");
1553 tt
->style
.cell
.rpad
= 2;
1554 tt
->style
.corner
= '+';
1556 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1558 while ((module
= ly_ctx_get_module_iter(ly_ctx
, &idx
))) {
1561 snprintf(flags
, sizeof(flags
), "%c%c",
1562 module
->implemented
? 'I' : ' ',
1563 LY_ARRAY_COUNT(module
->deviated_by
) ? 'D' : ' ');
1565 ttable_add_row(tt
, "%s|%s|%s|%s|%s", module
->name
,
1566 (module
->parsed
->version
== 2) ? "1.1" : "1.0",
1567 module
->revision
? module
->revision
: "-", flags
,
1571 /* Dump the generated table. */
1572 if (tt
->nrows
> 1) {
1575 vty_out(vty
, " Flags: I - Implemented, D - Deviated\n\n");
1577 table
= ttable_dump(tt
, "\n");
1578 vty_out(vty
, "%s\n", table
);
1579 XFREE(MTYPE_TMP
, table
);
1581 vty_out(vty
, "No YANG modules to display.\n\n");
1588 DEFPY(show_yang_module_detail
, show_yang_module_detail_cmd
,
1590 [module-translator WORD$translator_family]\
1591 WORD$module_name <compiled$compiled|summary|tree$tree|yang$yang|yin$yin>",
1593 "YANG information\n"
1594 "Show loaded modules\n"
1595 "YANG module translator\n"
1596 "YANG module translator\n"
1598 "Display compiled module in YANG format\n"
1599 "Display summary information about the module\n"
1600 "Display module in the tree (RFC 8340) format\n"
1601 "Display module in the YANG format\n"
1602 "Display module in the YIN format\n")
1604 struct ly_ctx
*ly_ctx
;
1605 struct yang_translator
*translator
= NULL
;
1606 const struct lys_module
*module
;
1607 LYS_OUTFORMAT format
;
1610 if (translator_family
) {
1611 translator
= yang_translator_find(translator_family
);
1613 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1617 ly_ctx
= translator
->ly_ctx
;
1619 ly_ctx
= ly_native_ctx
;
1621 module
= ly_ctx_get_module_latest(ly_ctx
, module_name
);
1623 vty_out(vty
, "%% Module \"%s\" not found\n", module_name
);
1628 format
= LYS_OUT_YANG
;
1630 format
= LYS_OUT_YIN
;
1632 format
= LYS_OUT_YANG_COMPILED
;
1634 format
= LYS_OUT_TREE
;
1637 "%% libyang v2 does not currently support summary\n");
1641 if (lys_print_mem(&strp
, module
, format
, 0) == 0) {
1642 vty_out(vty
, "%s\n", strp
);
1646 vty_out(vty
, "%% Error generating module information\n");
1653 DEFPY (show_yang_module_translator
,
1654 show_yang_module_translator_cmd
,
1655 "show yang module-translator",
1657 "YANG information\n"
1658 "Show loaded YANG module translators\n")
1660 struct yang_translator
*translator
;
1663 /* Prepare table. */
1664 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1665 ttable_add_row(tt
, "Family|Module|Deviations|Coverage (%%)");
1666 tt
->style
.cell
.rpad
= 2;
1667 tt
->style
.corner
= '+';
1669 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1671 RB_FOREACH (translator
, yang_translators
, &yang_translators
) {
1672 struct yang_tmodule
*tmodule
;
1673 struct listnode
*ln
;
1675 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
1676 ttable_add_row(tt
, "%s|%s|%s|%.2f", translator
->family
,
1677 tmodule
->module
->name
,
1678 tmodule
->deviations
->name
,
1683 /* Dump the generated table. */
1684 if (tt
->nrows
> 1) {
1687 table
= ttable_dump(tt
, "\n");
1688 vty_out(vty
, "%s\n", table
);
1689 XFREE(MTYPE_TMP
, table
);
1691 vty_out(vty
, "No YANG module translators to display.\n\n");
1698 #ifdef HAVE_CONFIG_ROLLBACKS
1699 static int nb_cli_rollback_configuration(struct vty
*vty
,
1700 uint32_t transaction_id
)
1702 struct nb_context context
= {};
1703 struct nb_config
*candidate
;
1705 char errmsg
[BUFSIZ
] = {0};
1708 candidate
= nb_db_transaction_load(transaction_id
);
1710 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1715 snprintf(comment
, sizeof(comment
), "Rollback to transaction %u",
1718 context
.client
= NB_CLIENT_CLI
;
1720 ret
= nb_candidate_commit(&context
, candidate
, true, comment
, NULL
,
1721 errmsg
, sizeof(errmsg
));
1722 nb_config_free(candidate
);
1726 "%% Configuration was successfully rolled back.\n\n");
1727 /* Print warnings (if any). */
1728 if (strlen(errmsg
) > 0)
1729 vty_out(vty
, "%s\n", errmsg
);
1731 case NB_ERR_NO_CHANGES
:
1733 "%% Aborting - no configuration changes detected.\n\n");
1736 vty_out(vty
, "%% Rollback failed.\n\n");
1737 vty_show_nb_errors(vty
, ret
, errmsg
);
1741 #endif /* HAVE_CONFIG_ROLLBACKS */
1743 DEFPY (rollback_config
,
1744 rollback_config_cmd
,
1745 "rollback configuration (1-4294967295)$transaction_id",
1746 "Rollback to a previous state\n"
1747 "Running configuration\n"
1750 #ifdef HAVE_CONFIG_ROLLBACKS
1751 return nb_cli_rollback_configuration(vty
, transaction_id
);
1754 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1756 #endif /* HAVE_CONFIG_ROLLBACKS */
1759 /* Debug CLI commands. */
1760 static struct debug
*nb_debugs
[] = {
1761 &nb_dbg_cbs_config
, &nb_dbg_cbs_state
, &nb_dbg_cbs_rpc
,
1762 &nb_dbg_notif
, &nb_dbg_events
, &nb_dbg_libyang
,
1765 static const char *const nb_debugs_conflines
[] = {
1766 "debug northbound callbacks configuration",
1767 "debug northbound callbacks state",
1768 "debug northbound callbacks rpc",
1769 "debug northbound notifications",
1770 "debug northbound events",
1771 "debug northbound libyang",
1774 DEFINE_HOOK(nb_client_debug_set_all
, (uint32_t flags
, bool set
), (flags
, set
));
1776 static void nb_debug_set_all(uint32_t flags
, bool set
)
1778 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++) {
1779 DEBUG_FLAGS_SET(nb_debugs
[i
], flags
, set
);
1781 /* If all modes have been turned off, don't preserve options. */
1782 if (!DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_ALL
))
1783 DEBUG_CLEAR(nb_debugs
[i
]);
1786 hook_call(nb_client_debug_set_all
, flags
, set
);
1791 "[no] debug northbound\
1793 callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\
1794 |notifications$notifications\
1800 "Northbound debugging\n"
1807 "libyang debugging\n")
1809 uint32_t mode
= DEBUG_NODE2MODE(vty
->node
);
1812 bool none
= (!cbs_cfg
&& !cbs_state
&& !cbs_rpc
);
1814 if (none
|| cbs_cfg
)
1815 DEBUG_MODE_SET(&nb_dbg_cbs_config
, mode
, !no
);
1816 if (none
|| cbs_state
)
1817 DEBUG_MODE_SET(&nb_dbg_cbs_state
, mode
, !no
);
1818 if (none
|| cbs_rpc
)
1819 DEBUG_MODE_SET(&nb_dbg_cbs_rpc
, mode
, !no
);
1822 DEBUG_MODE_SET(&nb_dbg_notif
, mode
, !no
);
1824 DEBUG_MODE_SET(&nb_dbg_events
, mode
, !no
);
1826 DEBUG_MODE_SET(&nb_dbg_libyang
, mode
, !no
);
1827 yang_debugging_set(!no
);
1830 /* no specific debug --> act on all of them */
1831 if (strmatch(argv
[argc
- 1]->text
, "northbound")) {
1832 nb_debug_set_all(mode
, !no
);
1833 yang_debugging_set(!no
);
1839 DEFINE_HOOK(nb_client_debug_config_write
, (struct vty
*vty
), (vty
));
1841 static int nb_debug_config_write(struct vty
*vty
)
1843 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++)
1844 if (DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_CONF
))
1845 vty_out(vty
, "%s\n", nb_debugs_conflines
[i
]);
1847 hook_call(nb_client_debug_config_write
, vty
);
1852 static struct debug_callbacks nb_dbg_cbs
= {.debug_set_all
= nb_debug_set_all
};
1853 static struct cmd_node nb_debug_node
= {
1854 .name
= "northbound debug",
1855 .node
= NORTHBOUND_DEBUG_NODE
,
1857 .config_write
= nb_debug_config_write
,
1860 void nb_cli_install_default(int node
)
1862 _install_element(node
, &show_config_candidate_section_cmd
);
1864 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL
)
1867 _install_element(node
, &config_commit_cmd
);
1868 _install_element(node
, &config_commit_comment_cmd
);
1869 _install_element(node
, &config_commit_check_cmd
);
1870 _install_element(node
, &config_update_cmd
);
1871 _install_element(node
, &config_discard_cmd
);
1872 _install_element(node
, &show_config_running_cmd
);
1873 _install_element(node
, &show_config_candidate_cmd
);
1874 _install_element(node
, &show_config_compare_cmd
);
1875 _install_element(node
, &show_config_transaction_cmd
);
1878 /* YANG module autocomplete. */
1879 static void yang_module_autocomplete(vector comps
, struct cmd_token
*token
)
1881 const struct lys_module
*module
;
1882 struct yang_translator
*module_tr
;
1886 while ((module
= ly_ctx_get_module_iter(ly_native_ctx
, &idx
)))
1887 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1889 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
) {
1891 while ((module
= ly_ctx_get_module_iter(module_tr
->ly_ctx
,
1894 XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1898 /* YANG module translator autocomplete. */
1899 static void yang_translator_autocomplete(vector comps
, struct cmd_token
*token
)
1901 struct yang_translator
*module_tr
;
1903 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
)
1904 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module_tr
->family
));
1907 static const struct cmd_variable_handler yang_var_handlers
[] = {
1908 {.varname
= "module_name", .completions
= yang_module_autocomplete
},
1909 {.varname
= "translator_family",
1910 .completions
= yang_translator_autocomplete
},
1911 {.completions
= NULL
}};
1913 void nb_cli_init(struct thread_master
*tm
)
1917 /* Initialize the shared candidate configuration. */
1918 vty_shared_candidate_config
= nb_config_new(NULL
);
1920 debug_init(&nb_dbg_cbs
);
1922 install_node(&nb_debug_node
);
1923 install_element(ENABLE_NODE
, &debug_nb_cmd
);
1924 install_element(CONFIG_NODE
, &debug_nb_cmd
);
1926 /* Install commands specific to the transaction-base mode. */
1927 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL
) {
1928 install_element(ENABLE_NODE
, &config_exclusive_cmd
);
1929 install_element(ENABLE_NODE
, &config_private_cmd
);
1930 install_element(ENABLE_NODE
,
1931 &show_config_compare_without_candidate_cmd
);
1932 install_element(ENABLE_NODE
, &show_config_transaction_cmd
);
1933 install_element(ENABLE_NODE
, &rollback_config_cmd
);
1934 install_element(ENABLE_NODE
, &clear_config_transactions_cmd
);
1936 install_element(CONFIG_NODE
, &config_load_cmd
);
1937 install_element(CONFIG_NODE
,
1938 &config_database_max_transactions_cmd
);
1941 /* Other commands. */
1942 install_element(ENABLE_NODE
, &show_config_running_cmd
);
1943 install_element(CONFIG_NODE
, &yang_module_translator_load_cmd
);
1944 install_element(CONFIG_NODE
, &yang_module_translator_unload_cmd
);
1945 install_element(ENABLE_NODE
, &show_yang_operational_data_cmd
);
1946 install_element(ENABLE_NODE
, &show_yang_module_cmd
);
1947 install_element(ENABLE_NODE
, &show_yang_module_detail_cmd
);
1948 install_element(ENABLE_NODE
, &show_yang_module_translator_cmd
);
1949 cmd_variable_handler_register(yang_var_handlers
);
1952 void nb_cli_terminate(void)
1954 nb_config_free(vty_shared_candidate_config
);