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, comment
,
398 &transaction_id
, errmsg
, sizeof(errmsg
));
400 /* Map northbound return code to CLI return code. */
403 nb_config_replace(vty
->candidate_config_base
, running_config
,
406 "%% Configuration committed successfully (Transaction ID #%u).\n\n",
408 /* Print warnings (if any). */
409 if (strlen(errmsg
) > 0)
410 vty_out(vty
, "%s\n", errmsg
);
412 case NB_ERR_NO_CHANGES
:
413 vty_out(vty
, "%% No configuration changes to commit.\n\n");
417 "%% Failed to commit candidate configuration.\n\n");
418 vty_show_nb_errors(vty
, ret
, errmsg
);
423 static int nb_cli_candidate_load_file(struct vty
*vty
,
424 enum nb_cfg_format format
,
425 struct yang_translator
*translator
,
426 const char *path
, bool replace
)
428 struct nb_config
*loaded_config
= NULL
;
429 struct lyd_node
*dnode
;
430 struct ly_ctx
*ly_ctx
;
436 case NB_CFG_FMT_CMDS
:
437 loaded_config
= nb_config_new(NULL
);
438 if (!vty_read_config(loaded_config
, path
, config_default
)) {
439 vty_out(vty
, "%% Failed to load configuration.\n\n");
441 "Please check the logs for more details.\n");
442 nb_config_free(loaded_config
);
446 case NB_CFG_FMT_JSON
:
448 ly_format
= (format
== NB_CFG_FMT_JSON
) ? LYD_JSON
: LYD_XML
;
450 ly_ctx
= translator
? translator
->ly_ctx
: ly_native_ctx
;
451 err
= lyd_parse_data_path(ly_ctx
, path
, ly_format
,
452 LYD_PARSE_ONLY
| LYD_PARSE_NO_STATE
,
455 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_path() failed",
457 vty_out(vty
, "%% Failed to load configuration:\n\n");
459 yang_print_errors(ly_native_ctx
, buf
,
464 && yang_translate_dnode(translator
,
465 YANG_TRANSLATE_TO_NATIVE
, &dnode
)
466 != YANG_TRANSLATE_SUCCESS
) {
467 vty_out(vty
, "%% Failed to translate configuration\n");
468 yang_dnode_free(dnode
);
471 loaded_config
= nb_config_new(dnode
);
476 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
477 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
480 "%% Failed to merge the loaded configuration:\n\n");
482 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
489 static int nb_cli_candidate_load_transaction(struct vty
*vty
,
490 uint32_t transaction_id
,
493 struct nb_config
*loaded_config
;
496 loaded_config
= nb_db_transaction_load(transaction_id
);
497 if (!loaded_config
) {
498 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
504 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
505 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
508 "%% Failed to merge the loaded configuration:\n\n");
510 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
517 /* Prepare the configuration for display. */
518 void nb_cli_show_config_prepare(struct nb_config
*config
, bool with_defaults
)
520 /* Nothing to do for daemons that don't implement any YANG module. */
521 if (config
->dnode
== NULL
)
525 * Call lyd_validate() only to create default child nodes, ignoring
526 * any possible validation error. This doesn't need to be done when
527 * displaying the running configuration since it's always fully
530 if (config
!= running_config
)
531 (void)lyd_validate_all(&config
->dnode
, ly_native_ctx
,
532 LYD_VALIDATE_NO_STATE
, NULL
);
535 static int lyd_node_cmp(const struct lyd_node
**dnode1
,
536 const struct lyd_node
**dnode2
)
538 struct nb_node
*nb_node
= (*dnode1
)->schema
->priv
;
540 return nb_node
->cbs
.cli_cmp(*dnode1
, *dnode2
);
543 static void show_dnode_children_cmds(struct vty
*vty
,
544 const struct lyd_node
*root
,
547 struct nb_node
*nb_node
, *sort_node
= NULL
;
548 struct listnode
*listnode
;
549 struct lyd_node
*child
;
550 struct list
*sort_list
;
553 LY_LIST_FOR (lyd_child(root
), child
) {
554 nb_node
= child
->schema
->priv
;
557 * We finished processing current list,
558 * it's time to print the config.
560 if (sort_node
&& nb_node
!= sort_node
) {
562 (int (*)(const void **,
563 const void **))lyd_node_cmp
);
565 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
566 nb_cli_show_dnode_cmds(vty
, data
,
569 list_delete(&sort_list
);
574 * If the config needs to be sorted,
575 * then add the dnode to the sorting
576 * list for later processing.
578 if (nb_node
&& nb_node
->cbs
.cli_cmp
) {
581 sort_list
= list_new();
584 listnode_add(sort_list
, child
);
588 nb_cli_show_dnode_cmds(vty
, child
, with_defaults
);
593 (int (*)(const void **, const void **))lyd_node_cmp
);
595 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
596 nb_cli_show_dnode_cmds(vty
, data
, with_defaults
);
598 list_delete(&sort_list
);
603 void nb_cli_show_dnode_cmds(struct vty
*vty
, const struct lyd_node
*root
,
606 struct nb_node
*nb_node
;
608 if (!with_defaults
&& yang_dnode_is_default_recursive(root
))
611 nb_node
= root
->schema
->priv
;
613 if (nb_node
&& nb_node
->cbs
.cli_show
)
614 (*nb_node
->cbs
.cli_show
)(vty
, root
, with_defaults
);
616 if (!(root
->schema
->nodetype
& (LYS_LEAF
| LYS_LEAFLIST
| LYS_ANYDATA
)))
617 show_dnode_children_cmds(vty
, root
, with_defaults
);
619 if (nb_node
&& nb_node
->cbs
.cli_show_end
)
620 (*nb_node
->cbs
.cli_show_end
)(vty
, root
);
623 static void nb_cli_show_config_cmds(struct vty
*vty
, struct nb_config
*config
,
626 struct lyd_node
*root
;
628 vty_out(vty
, "Configuration:\n");
630 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
631 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
633 LY_LIST_FOR (config
->dnode
, root
) {
634 nb_cli_show_dnode_cmds(vty
, root
, with_defaults
);
638 vty_out(vty
, "end\n");
641 static int nb_cli_show_config_libyang(struct vty
*vty
, LYD_FORMAT format
,
642 struct nb_config
*config
,
643 struct yang_translator
*translator
,
646 struct lyd_node
*dnode
;
650 dnode
= yang_dnode_dup(config
->dnode
);
652 && yang_translate_dnode(translator
, YANG_TRANSLATE_FROM_NATIVE
,
654 != YANG_TRANSLATE_SUCCESS
) {
655 vty_out(vty
, "%% Failed to translate configuration\n");
656 yang_dnode_free(dnode
);
660 SET_FLAG(options
, LYD_PRINT_WITHSIBLINGS
);
662 SET_FLAG(options
, LYD_PRINT_WD_ALL
);
664 SET_FLAG(options
, LYD_PRINT_WD_TRIM
);
666 if (lyd_print_mem(&strp
, dnode
, format
, options
) == 0 && strp
) {
667 vty_out(vty
, "%s", strp
);
671 yang_dnode_free(dnode
);
676 static int nb_cli_show_config(struct vty
*vty
, struct nb_config
*config
,
677 enum nb_cfg_format format
,
678 struct yang_translator
*translator
,
681 nb_cli_show_config_prepare(config
, with_defaults
);
684 case NB_CFG_FMT_CMDS
:
685 nb_cli_show_config_cmds(vty
, config
, with_defaults
);
687 case NB_CFG_FMT_JSON
:
688 return nb_cli_show_config_libyang(vty
, LYD_JSON
, config
,
689 translator
, with_defaults
);
691 return nb_cli_show_config_libyang(vty
, LYD_XML
, config
,
692 translator
, with_defaults
);
698 static int nb_write_config(struct nb_config
*config
, enum nb_cfg_format format
,
699 struct yang_translator
*translator
, char *path
,
703 struct vty
*file_vty
;
706 snprintf(path
, pathlen
, "/tmp/frr.tmp.XXXXXXXX");
709 flog_warn(EC_LIB_SYSTEM_CALL
, "%s: mkstemp() failed: %s",
710 __func__
, safe_strerror(errno
));
713 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
714 flog_warn(EC_LIB_SYSTEM_CALL
,
715 "%s: fchmod() failed: %s(%d):", __func__
,
716 safe_strerror(errno
), errno
);
720 /* Make vty for configuration file. */
721 file_vty
= vty_new();
723 file_vty
->type
= VTY_FILE
;
725 ret
= nb_cli_show_config(file_vty
, config
, format
, translator
,
732 static int nb_cli_show_config_compare(struct vty
*vty
,
733 struct nb_config
*config1
,
734 struct nb_config
*config2
,
735 enum nb_cfg_format format
,
736 struct yang_translator
*translator
)
738 char config1_path
[256];
739 char config2_path
[256];
740 char command
[BUFSIZ
];
745 if (nb_write_config(config1
, format
, translator
, config1_path
,
746 sizeof(config1_path
))
748 vty_out(vty
, "%% Failed to process configurations.\n\n");
751 if (nb_write_config(config2
, format
, translator
, config2_path
,
752 sizeof(config2_path
))
754 vty_out(vty
, "%% Failed to process configurations.\n\n");
755 unlink(config1_path
);
759 snprintf(command
, sizeof(command
), "diff -u %s %s", config1_path
,
761 fp
= popen(command
, "r");
763 vty_out(vty
, "%% Failed to generate configuration diff.\n\n");
764 unlink(config1_path
);
765 unlink(config2_path
);
768 /* Print diff line by line. */
769 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
772 vty_out(vty
, "%s", line
);
776 unlink(config1_path
);
777 unlink(config2_path
);
782 /* Configure exclusively from this terminal. */
783 DEFUN (config_exclusive
,
784 config_exclusive_cmd
,
785 "configure exclusive",
786 "Configuration from vty interface\n"
787 "Configure exclusively from this terminal\n")
789 return vty_config_enter(vty
, true, true);
792 /* Configure using a private candidate configuration. */
793 DEFUN (config_private
,
796 "Configuration from vty interface\n"
797 "Configure using a private candidate configuration\n")
799 return vty_config_enter(vty
, true, false);
802 DEFPY (config_commit
,
804 "commit [{force$force|confirmed (1-60)}]",
805 "Commit changes into the running configuration\n"
806 "Force commit even if the candidate is outdated\n"
807 "Rollback this commit unless there is a confirming commit\n"
808 "Timeout in minutes for the commit to be confirmed\n")
810 return nb_cli_commit(vty
, !!force
, confirmed
, NULL
);
813 DEFPY (config_commit_comment
,
814 config_commit_comment_cmd
,
815 "commit [{force$force|confirmed (1-60)}] comment LINE...",
816 "Commit changes into the running configuration\n"
817 "Force commit even if the candidate is outdated\n"
818 "Rollback this commit unless there is a confirming commit\n"
819 "Timeout in minutes for the commit to be confirmed\n"
820 "Assign a comment to this commit\n"
821 "Comment for this commit (Max 80 characters)\n")
827 argv_find(argv
, argc
, "LINE", &idx
);
828 comment
= argv_concat(argv
, argc
, idx
);
829 ret
= nb_cli_commit(vty
, !!force
, confirmed
, comment
);
830 XFREE(MTYPE_TMP
, comment
);
835 DEFPY (config_commit_check
,
836 config_commit_check_cmd
,
838 "Commit changes into the running configuration\n"
839 "Check if the configuration changes are valid\n")
841 struct nb_context context
= {};
842 char errmsg
[BUFSIZ
] = {0};
845 context
.client
= NB_CLIENT_CLI
;
847 ret
= nb_candidate_validate(&context
, vty
->candidate_config
, errmsg
,
851 "%% Failed to validate candidate configuration.\n\n");
852 vty_show_nb_errors(vty
, ret
, errmsg
);
856 vty_out(vty
, "%% Candidate configuration validated successfully.\n\n");
861 DEFPY (config_update
,
864 "Update candidate configuration\n")
866 if (!nb_candidate_needs_update(vty
->candidate_config
)) {
867 vty_out(vty
, "%% Update is not necessary.\n\n");
871 if (nb_candidate_update(vty
->candidate_config
) != NB_OK
) {
873 "%% Failed to update the candidate configuration.\n\n");
874 vty_out(vty
, "Please check the logs for more details.\n");
878 nb_config_replace(vty
->candidate_config_base
, running_config
, true);
880 vty_out(vty
, "%% Candidate configuration updated successfully.\n\n");
885 DEFPY (config_discard
,
888 "Discard changes in the candidate configuration\n")
890 nb_config_replace(vty
->candidate_config
, vty
->candidate_config_base
,
900 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
901 |transaction (1-4294967295)$tid\
904 "Configuration related settings\n"
905 "Load configuration into candidate\n"
906 "Load configuration file into candidate\n"
907 "Load configuration file in JSON format\n"
908 "Load configuration file in XML format\n"
909 "Translate configuration file\n"
910 "YANG module translator\n"
911 "Configuration file name (full path)\n"
912 "Load configuration from transaction into candidate\n"
914 "Replace instead of merge\n")
917 enum nb_cfg_format format
;
918 struct yang_translator
*translator
= NULL
;
921 format
= NB_CFG_FMT_JSON
;
923 format
= NB_CFG_FMT_XML
;
925 format
= NB_CFG_FMT_CMDS
;
927 if (translator_family
) {
928 translator
= yang_translator_find(translator_family
);
931 "%% Module translator \"%s\" not found\n",
937 return nb_cli_candidate_load_file(vty
, format
, translator
,
938 filename
, !!replace
);
941 return nb_cli_candidate_load_transaction(vty
, tid
, !!replace
);
944 DEFPY (show_config_running
,
945 show_config_running_cmd
,
946 "show configuration running\
947 [<json$json|xml$xml> [translate WORD$translator_family]]\
948 [with-defaults$with_defaults]",
950 "Configuration information\n"
951 "Running configuration\n"
952 "Change output format to JSON\n"
953 "Change output format to XML\n"
955 "YANG module translator\n"
956 "Show default values\n")
959 enum nb_cfg_format format
;
960 struct yang_translator
*translator
= NULL
;
963 format
= NB_CFG_FMT_JSON
;
965 format
= NB_CFG_FMT_XML
;
967 format
= NB_CFG_FMT_CMDS
;
969 if (translator_family
) {
970 translator
= yang_translator_find(translator_family
);
972 vty_out(vty
, "%% Module translator \"%s\" not found\n",
978 nb_cli_show_config(vty
, running_config
, format
, translator
,
984 DEFPY (show_config_candidate
,
985 show_config_candidate_cmd
,
986 "show configuration candidate\
987 [<json$json|xml$xml> [translate WORD$translator_family]]\
989 with-defaults$with_defaults\
993 "Configuration information\n"
994 "Candidate configuration\n"
995 "Change output format to JSON\n"
996 "Change output format to XML\n"
998 "YANG module translator\n"
999 "Show default values\n"
1000 "Show changes applied in the candidate configuration\n")
1003 enum nb_cfg_format format
;
1004 struct yang_translator
*translator
= NULL
;
1007 format
= NB_CFG_FMT_JSON
;
1009 format
= NB_CFG_FMT_XML
;
1011 format
= NB_CFG_FMT_CMDS
;
1013 if (translator_family
) {
1014 translator
= yang_translator_find(translator_family
);
1016 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1023 return nb_cli_show_config_compare(
1024 vty
, vty
->candidate_config_base
, vty
->candidate_config
,
1025 format
, translator
);
1027 nb_cli_show_config(vty
, vty
->candidate_config
, format
, translator
,
1033 DEFPY (show_config_candidate_section
,
1034 show_config_candidate_section_cmd
,
1038 struct lyd_node
*dnode
;
1040 /* Top-level configuration node, display everything. */
1041 if (vty
->xpath_index
== 0)
1042 return nb_cli_show_config(vty
, vty
->candidate_config
,
1043 NB_CFG_FMT_CMDS
, NULL
, false);
1045 /* Display only the current section of the candidate configuration. */
1046 dnode
= yang_dnode_get(vty
->candidate_config
->dnode
, VTY_CURR_XPATH
);
1048 /* Shouldn't happen. */
1051 nb_cli_show_dnode_cmds(vty
, dnode
, 0);
1052 vty_out(vty
, "!\n");
1057 DEFPY (show_config_compare
,
1058 show_config_compare_cmd
,
1059 "show configuration compare\
1061 candidate$c1_candidate\
1062 |running$c1_running\
1063 |transaction (1-4294967295)$c1_tid\
1066 candidate$c2_candidate\
1067 |running$c2_running\
1068 |transaction (1-4294967295)$c2_tid\
1070 [<json$json|xml$xml> [translate WORD$translator_family]]",
1072 "Configuration information\n"
1073 "Compare two different configurations\n"
1074 "Candidate configuration\n"
1075 "Running configuration\n"
1076 "Configuration transaction\n"
1078 "Candidate configuration\n"
1079 "Running configuration\n"
1080 "Configuration transaction\n"
1082 "Change output format to JSON\n"
1083 "Change output format to XML\n"
1084 "Translate output\n"
1085 "YANG module translator\n")
1087 enum nb_cfg_format format
;
1088 struct yang_translator
*translator
= NULL
;
1089 struct nb_config
*config1
, *config_transaction1
= NULL
;
1090 struct nb_config
*config2
, *config_transaction2
= NULL
;
1091 int ret
= CMD_WARNING
;
1094 config1
= vty
->candidate_config
;
1095 else if (c1_running
)
1096 config1
= running_config
;
1098 config_transaction1
= nb_db_transaction_load(c1_tid
);
1099 if (!config_transaction1
) {
1100 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1101 (unsigned int)c1_tid
);
1104 config1
= config_transaction1
;
1108 config2
= vty
->candidate_config
;
1109 else if (c2_running
)
1110 config2
= running_config
;
1112 config_transaction2
= nb_db_transaction_load(c2_tid
);
1113 if (!config_transaction2
) {
1114 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1115 (unsigned int)c2_tid
);
1118 config2
= config_transaction2
;
1122 format
= NB_CFG_FMT_JSON
;
1124 format
= NB_CFG_FMT_XML
;
1126 format
= NB_CFG_FMT_CMDS
;
1128 if (translator_family
) {
1129 translator
= yang_translator_find(translator_family
);
1131 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1137 ret
= nb_cli_show_config_compare(vty
, config1
, config2
, format
,
1140 if (config_transaction1
)
1141 nb_config_free(config_transaction1
);
1142 if (config_transaction2
)
1143 nb_config_free(config_transaction2
);
1149 * Stripped down version of the "show configuration compare" command.
1150 * The "candidate" option is not present so the command can be installed in
1153 ALIAS (show_config_compare
,
1154 show_config_compare_without_candidate_cmd
,
1155 "show configuration compare\
1158 |transaction (1-4294967295)$c1_tid\
1162 |transaction (1-4294967295)$c2_tid\
1164 [<json$json|xml$xml> [translate WORD$translator_family]]",
1166 "Configuration information\n"
1167 "Compare two different configurations\n"
1168 "Running configuration\n"
1169 "Configuration transaction\n"
1171 "Running configuration\n"
1172 "Configuration transaction\n"
1174 "Change output format to JSON\n"
1175 "Change output format to XML\n"
1176 "Translate output\n"
1177 "YANG module translator\n")
1179 DEFPY (clear_config_transactions
,
1180 clear_config_transactions_cmd
,
1181 "clear configuration transactions oldest (1-100)$n",
1183 "Configuration activity\n"
1184 "Delete transactions from the transactions log\n"
1185 "Delete oldest <n> transactions\n"
1186 "Number of transactions to delete\n")
1188 #ifdef HAVE_CONFIG_ROLLBACKS
1189 if (nb_db_clear_transactions(n
) != NB_OK
) {
1190 vty_out(vty
, "%% Failed to delete transactions.\n\n");
1195 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1196 #endif /* HAVE_CONFIG_ROLLBACKS */
1201 DEFPY (config_database_max_transactions
,
1202 config_database_max_transactions_cmd
,
1203 "configuration database max-transactions (1-100)$max",
1204 "Configuration related settings\n"
1205 "Configuration database\n"
1206 "Set the maximum number of transactions to store\n"
1207 "Number of transactions\n")
1209 #ifdef HAVE_CONFIG_ROLLBACKS
1210 if (nb_db_set_max_transactions(max
) != NB_OK
) {
1212 "%% Failed to update the maximum number of transactions.\n\n");
1216 "%% Maximum number of transactions updated successfully.\n\n");
1219 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1220 #endif /* HAVE_CONFIG_ROLLBACKS */
1225 DEFPY (yang_module_translator_load
,
1226 yang_module_translator_load_cmd
,
1227 "yang module-translator load FILENAME$filename",
1228 "YANG related settings\n"
1229 "YANG module translator\n"
1230 "Load YANG module translator\n"
1231 "File name (full path)\n")
1233 struct yang_translator
*translator
;
1235 translator
= yang_translator_load(filename
);
1237 vty_out(vty
, "%% Failed to load \"%s\"\n\n", filename
);
1238 vty_out(vty
, "Please check the logs for more details.\n");
1242 vty_out(vty
, "%% Module translator \"%s\" loaded successfully.\n\n",
1243 translator
->family
);
1248 DEFPY (yang_module_translator_unload_family
,
1249 yang_module_translator_unload_cmd
,
1250 "yang module-translator unload WORD$translator_family",
1251 "YANG related settings\n"
1252 "YANG module translator\n"
1253 "Unload YANG module translator\n"
1254 "Name of the module translator\n")
1256 struct yang_translator
*translator
;
1258 translator
= yang_translator_find(translator_family
);
1260 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1265 yang_translator_unload(translator
);
1270 #ifdef HAVE_CONFIG_ROLLBACKS
1271 static void nb_cli_show_transactions_cb(void *arg
, int transaction_id
,
1272 const char *client_name
,
1273 const char *date
, const char *comment
)
1275 struct ttable
*tt
= arg
;
1277 ttable_add_row(tt
, "%d|%s|%s|%s", transaction_id
, client_name
, date
,
1281 static int nb_cli_show_transactions(struct vty
*vty
)
1285 /* Prepare table. */
1286 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1287 ttable_add_row(tt
, "Transaction ID|Client|Date|Comment");
1288 tt
->style
.cell
.rpad
= 2;
1289 tt
->style
.corner
= '+';
1291 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1293 /* Fetch transactions from the northbound database. */
1294 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb
, tt
)
1297 "%% Failed to fetch configuration transactions.\n");
1301 /* Dump the generated table. */
1302 if (tt
->nrows
> 1) {
1305 table
= ttable_dump(tt
, "\n");
1306 vty_out(vty
, "%s\n", table
);
1307 XFREE(MTYPE_TMP
, table
);
1309 vty_out(vty
, "No configuration transactions to display.\n\n");
1315 #endif /* HAVE_CONFIG_ROLLBACKS */
1317 DEFPY (show_config_transaction
,
1318 show_config_transaction_cmd
,
1319 "show configuration transaction\
1321 (1-4294967295)$transaction_id\
1322 [<json$json|xml$xml> [translate WORD$translator_family]]\
1324 with-defaults$with_defaults\
1329 "Configuration information\n"
1330 "Configuration transaction\n"
1332 "Change output format to JSON\n"
1333 "Change output format to XML\n"
1334 "Translate output\n"
1335 "YANG module translator\n"
1336 "Show default values\n"
1337 "Show changes compared to the previous transaction\n")
1339 #ifdef HAVE_CONFIG_ROLLBACKS
1340 if (transaction_id
) {
1341 struct nb_config
*config
;
1342 enum nb_cfg_format format
;
1343 struct yang_translator
*translator
= NULL
;
1346 format
= NB_CFG_FMT_JSON
;
1348 format
= NB_CFG_FMT_XML
;
1350 format
= NB_CFG_FMT_CMDS
;
1352 if (translator_family
) {
1353 translator
= yang_translator_find(translator_family
);
1356 "%% Module translator \"%s\" not found\n",
1362 config
= nb_db_transaction_load(transaction_id
);
1364 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1365 (unsigned int)transaction_id
);
1370 struct nb_config
*prev_config
;
1373 /* NOTE: this can be NULL. */
1375 nb_db_transaction_load(transaction_id
- 1);
1377 ret
= nb_cli_show_config_compare(
1378 vty
, prev_config
, config
, format
, translator
);
1380 nb_config_free(prev_config
);
1381 nb_config_free(config
);
1386 nb_cli_show_config(vty
, config
, format
, translator
,
1388 nb_config_free(config
);
1393 return nb_cli_show_transactions(vty
);
1396 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1398 #endif /* HAVE_CONFIG_ROLLBACKS */
1401 static int nb_cli_oper_data_cb(const struct lysc_node
*snode
,
1402 struct yang_translator
*translator
,
1403 struct yang_data
*data
, void *arg
)
1405 struct lyd_node
*dnode
= arg
;
1406 struct ly_ctx
*ly_ctx
;
1411 ret
= yang_translate_xpath(translator
,
1412 YANG_TRANSLATE_FROM_NATIVE
,
1413 data
->xpath
, sizeof(data
->xpath
));
1415 case YANG_TRANSLATE_SUCCESS
:
1417 case YANG_TRANSLATE_NOTFOUND
:
1419 case YANG_TRANSLATE_FAILURE
:
1423 ly_ctx
= translator
->ly_ctx
;
1425 ly_ctx
= ly_native_ctx
;
1428 lyd_new_path(dnode
, ly_ctx
, data
->xpath
, (void *)data
->value
,
1429 LYD_NEW_PATH_UPDATE
, &dnode
);
1431 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_new_path(%s) failed: %s",
1432 __func__
, data
->xpath
, ly_errmsg(ly_native_ctx
));
1437 yang_data_free(data
);
1441 yang_data_free(data
);
1445 DEFPY (show_yang_operational_data
,
1446 show_yang_operational_data_cmd
,
1447 "show yang operational-data XPATH$xpath\
1449 format <json$json|xml$xml>\
1450 |translate WORD$translator_family\
1451 |with-config$with_config\
1454 "YANG information\n"
1455 "Show YANG operational data\n"
1456 "XPath expression specifying the YANG data path\n"
1457 "Set the output format\n"
1458 "JavaScript Object Notation\n"
1459 "Extensible Markup Language\n"
1460 "Translate operational data\n"
1461 "YANG module translator\n"
1462 "Merge configuration data\n")
1465 struct yang_translator
*translator
= NULL
;
1466 struct ly_ctx
*ly_ctx
;
1467 struct lyd_node
*dnode
;
1469 uint32_t print_options
= LYD_PRINT_WITHSIBLINGS
;
1476 if (translator_family
) {
1477 translator
= yang_translator_find(translator_family
);
1479 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1484 ly_ctx
= translator
->ly_ctx
;
1486 ly_ctx
= ly_native_ctx
;
1489 dnode
= yang_dnode_new(ly_ctx
, false);
1490 if (nb_oper_data_iterate(xpath
, translator
, 0, nb_cli_oper_data_cb
,
1493 vty_out(vty
, "%% Failed to fetch operational data.\n");
1494 yang_dnode_free(dnode
);
1498 if (with_config
&& yang_dnode_exists(running_config
->dnode
, xpath
)) {
1499 struct lyd_node
*config_dnode
=
1500 yang_dnode_get(running_config
->dnode
, xpath
);
1501 if (config_dnode
!= NULL
) {
1502 lyd_merge_tree(&dnode
, yang_dnode_dup(config_dnode
),
1503 LYD_MERGE_DESTRUCT
);
1504 print_options
|= LYD_PRINT_WD_ALL
;
1508 (void)lyd_validate_all(&dnode
, ly_ctx
, 0, NULL
);
1510 /* Display the data. */
1511 if (lyd_print_mem(&strp
, dnode
, format
, print_options
) != 0 || !strp
) {
1512 vty_out(vty
, "%% Failed to display operational data.\n");
1513 yang_dnode_free(dnode
);
1516 vty_out(vty
, "%s", strp
);
1518 yang_dnode_free(dnode
);
1523 DEFPY (show_yang_module
,
1524 show_yang_module_cmd
,
1525 "show yang module [module-translator WORD$translator_family]",
1527 "YANG information\n"
1528 "Show loaded modules\n"
1529 "YANG module translator\n"
1530 "YANG module translator\n")
1532 struct ly_ctx
*ly_ctx
;
1533 struct yang_translator
*translator
= NULL
;
1534 const struct lys_module
*module
;
1538 if (translator_family
) {
1539 translator
= yang_translator_find(translator_family
);
1541 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1545 ly_ctx
= translator
->ly_ctx
;
1547 ly_ctx
= ly_native_ctx
;
1549 /* Prepare table. */
1550 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1551 ttable_add_row(tt
, "Module|Version|Revision|Flags|Namespace");
1552 tt
->style
.cell
.rpad
= 2;
1553 tt
->style
.corner
= '+';
1555 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1557 while ((module
= ly_ctx_get_module_iter(ly_ctx
, &idx
))) {
1560 snprintf(flags
, sizeof(flags
), "%c%c",
1561 module
->implemented
? 'I' : ' ',
1562 LY_ARRAY_COUNT(module
->deviated_by
) ? 'D' : ' ');
1564 ttable_add_row(tt
, "%s|%s|%s|%s|%s", module
->name
,
1565 (module
->parsed
->version
== 2) ? "1.1" : "1.0",
1566 module
->revision
? module
->revision
: "-", flags
,
1570 /* Dump the generated table. */
1571 if (tt
->nrows
> 1) {
1574 vty_out(vty
, " Flags: I - Implemented, D - Deviated\n\n");
1576 table
= ttable_dump(tt
, "\n");
1577 vty_out(vty
, "%s\n", table
);
1578 XFREE(MTYPE_TMP
, table
);
1580 vty_out(vty
, "No YANG modules to display.\n\n");
1587 DEFPY(show_yang_module_detail
, show_yang_module_detail_cmd
,
1589 [module-translator WORD$translator_family]\
1590 WORD$module_name <compiled$compiled|summary|tree$tree|yang$yang|yin$yin>",
1592 "YANG information\n"
1593 "Show loaded modules\n"
1594 "YANG module translator\n"
1595 "YANG module translator\n"
1597 "Display compiled module in YANG format\n"
1598 "Display summary information about the module\n"
1599 "Display module in the tree (RFC 8340) format\n"
1600 "Display module in the YANG format\n"
1601 "Display module in the YIN format\n")
1603 struct ly_ctx
*ly_ctx
;
1604 struct yang_translator
*translator
= NULL
;
1605 const struct lys_module
*module
;
1606 LYS_OUTFORMAT format
;
1609 if (translator_family
) {
1610 translator
= yang_translator_find(translator_family
);
1612 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1616 ly_ctx
= translator
->ly_ctx
;
1618 ly_ctx
= ly_native_ctx
;
1620 module
= ly_ctx_get_module_latest(ly_ctx
, module_name
);
1622 vty_out(vty
, "%% Module \"%s\" not found\n", module_name
);
1627 format
= LYS_OUT_YANG
;
1629 format
= LYS_OUT_YIN
;
1631 format
= LYS_OUT_YANG_COMPILED
;
1633 format
= LYS_OUT_TREE
;
1636 "%% libyang v2 does not currently support summary\n");
1640 if (lys_print_mem(&strp
, module
, format
, 0) == 0) {
1641 vty_out(vty
, "%s\n", strp
);
1645 vty_out(vty
, "%% Error generating module information\n");
1652 DEFPY (show_yang_module_translator
,
1653 show_yang_module_translator_cmd
,
1654 "show yang module-translator",
1656 "YANG information\n"
1657 "Show loaded YANG module translators\n")
1659 struct yang_translator
*translator
;
1662 /* Prepare table. */
1663 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1664 ttable_add_row(tt
, "Family|Module|Deviations|Coverage (%%)");
1665 tt
->style
.cell
.rpad
= 2;
1666 tt
->style
.corner
= '+';
1668 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1670 RB_FOREACH (translator
, yang_translators
, &yang_translators
) {
1671 struct yang_tmodule
*tmodule
;
1672 struct listnode
*ln
;
1674 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
1675 ttable_add_row(tt
, "%s|%s|%s|%.2f", translator
->family
,
1676 tmodule
->module
->name
,
1677 tmodule
->deviations
->name
,
1682 /* Dump the generated table. */
1683 if (tt
->nrows
> 1) {
1686 table
= ttable_dump(tt
, "\n");
1687 vty_out(vty
, "%s\n", table
);
1688 XFREE(MTYPE_TMP
, table
);
1690 vty_out(vty
, "No YANG module translators to display.\n\n");
1697 #ifdef HAVE_CONFIG_ROLLBACKS
1698 static int nb_cli_rollback_configuration(struct vty
*vty
,
1699 uint32_t transaction_id
)
1701 struct nb_context context
= {};
1702 struct nb_config
*candidate
;
1704 char errmsg
[BUFSIZ
] = {0};
1707 candidate
= nb_db_transaction_load(transaction_id
);
1709 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1714 snprintf(comment
, sizeof(comment
), "Rollback to transaction %u",
1717 context
.client
= NB_CLIENT_CLI
;
1719 ret
= nb_candidate_commit(context
, candidate
, true, comment
, NULL
,
1720 errmsg
, sizeof(errmsg
));
1721 nb_config_free(candidate
);
1725 "%% Configuration was successfully rolled back.\n\n");
1726 /* Print warnings (if any). */
1727 if (strlen(errmsg
) > 0)
1728 vty_out(vty
, "%s\n", errmsg
);
1730 case NB_ERR_NO_CHANGES
:
1732 "%% Aborting - no configuration changes detected.\n\n");
1735 vty_out(vty
, "%% Rollback failed.\n\n");
1736 vty_show_nb_errors(vty
, ret
, errmsg
);
1740 #endif /* HAVE_CONFIG_ROLLBACKS */
1742 DEFPY (rollback_config
,
1743 rollback_config_cmd
,
1744 "rollback configuration (1-4294967295)$transaction_id",
1745 "Rollback to a previous state\n"
1746 "Running configuration\n"
1749 #ifdef HAVE_CONFIG_ROLLBACKS
1750 return nb_cli_rollback_configuration(vty
, transaction_id
);
1753 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1755 #endif /* HAVE_CONFIG_ROLLBACKS */
1758 /* Debug CLI commands. */
1759 static struct debug
*nb_debugs
[] = {
1760 &nb_dbg_cbs_config
, &nb_dbg_cbs_state
, &nb_dbg_cbs_rpc
,
1761 &nb_dbg_notif
, &nb_dbg_events
, &nb_dbg_libyang
,
1764 static const char *const nb_debugs_conflines
[] = {
1765 "debug northbound callbacks configuration",
1766 "debug northbound callbacks state",
1767 "debug northbound callbacks rpc",
1768 "debug northbound notifications",
1769 "debug northbound events",
1770 "debug northbound libyang",
1773 DEFINE_HOOK(nb_client_debug_set_all
, (uint32_t flags
, bool set
), (flags
, set
));
1775 static void nb_debug_set_all(uint32_t flags
, bool set
)
1777 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++) {
1778 DEBUG_FLAGS_SET(nb_debugs
[i
], flags
, set
);
1780 /* If all modes have been turned off, don't preserve options. */
1781 if (!DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_ALL
))
1782 DEBUG_CLEAR(nb_debugs
[i
]);
1785 hook_call(nb_client_debug_set_all
, flags
, set
);
1790 "[no] debug northbound\
1792 callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\
1793 |notifications$notifications\
1799 "Northbound debugging\n"
1806 "libyang debugging\n")
1808 uint32_t mode
= DEBUG_NODE2MODE(vty
->node
);
1811 bool none
= (!cbs_cfg
&& !cbs_state
&& !cbs_rpc
);
1813 if (none
|| cbs_cfg
)
1814 DEBUG_MODE_SET(&nb_dbg_cbs_config
, mode
, !no
);
1815 if (none
|| cbs_state
)
1816 DEBUG_MODE_SET(&nb_dbg_cbs_state
, mode
, !no
);
1817 if (none
|| cbs_rpc
)
1818 DEBUG_MODE_SET(&nb_dbg_cbs_rpc
, mode
, !no
);
1821 DEBUG_MODE_SET(&nb_dbg_notif
, mode
, !no
);
1823 DEBUG_MODE_SET(&nb_dbg_events
, mode
, !no
);
1825 DEBUG_MODE_SET(&nb_dbg_libyang
, mode
, !no
);
1826 yang_debugging_set(!no
);
1829 /* no specific debug --> act on all of them */
1830 if (strmatch(argv
[argc
- 1]->text
, "northbound")) {
1831 nb_debug_set_all(mode
, !no
);
1832 yang_debugging_set(!no
);
1838 DEFINE_HOOK(nb_client_debug_config_write
, (struct vty
*vty
), (vty
));
1840 static int nb_debug_config_write(struct vty
*vty
)
1842 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++)
1843 if (DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_CONF
))
1844 vty_out(vty
, "%s\n", nb_debugs_conflines
[i
]);
1846 hook_call(nb_client_debug_config_write
, vty
);
1851 static struct debug_callbacks nb_dbg_cbs
= {.debug_set_all
= nb_debug_set_all
};
1852 static struct cmd_node nb_debug_node
= {
1853 .name
= "northbound debug",
1854 .node
= NORTHBOUND_DEBUG_NODE
,
1856 .config_write
= nb_debug_config_write
,
1859 void nb_cli_install_default(int node
)
1861 _install_element(node
, &show_config_candidate_section_cmd
);
1863 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL
)
1866 _install_element(node
, &config_commit_cmd
);
1867 _install_element(node
, &config_commit_comment_cmd
);
1868 _install_element(node
, &config_commit_check_cmd
);
1869 _install_element(node
, &config_update_cmd
);
1870 _install_element(node
, &config_discard_cmd
);
1871 _install_element(node
, &show_config_running_cmd
);
1872 _install_element(node
, &show_config_candidate_cmd
);
1873 _install_element(node
, &show_config_compare_cmd
);
1874 _install_element(node
, &show_config_transaction_cmd
);
1877 /* YANG module autocomplete. */
1878 static void yang_module_autocomplete(vector comps
, struct cmd_token
*token
)
1880 const struct lys_module
*module
;
1881 struct yang_translator
*module_tr
;
1885 while ((module
= ly_ctx_get_module_iter(ly_native_ctx
, &idx
)))
1886 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1888 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
) {
1890 while ((module
= ly_ctx_get_module_iter(module_tr
->ly_ctx
,
1893 XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1897 /* YANG module translator autocomplete. */
1898 static void yang_translator_autocomplete(vector comps
, struct cmd_token
*token
)
1900 struct yang_translator
*module_tr
;
1902 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
)
1903 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module_tr
->family
));
1906 static const struct cmd_variable_handler yang_var_handlers
[] = {
1907 {.varname
= "module_name", .completions
= yang_module_autocomplete
},
1908 {.varname
= "translator_family",
1909 .completions
= yang_translator_autocomplete
},
1910 {.completions
= NULL
}};
1912 void nb_cli_init(struct thread_master
*tm
)
1916 /* Initialize the shared candidate configuration. */
1917 vty_shared_candidate_config
= nb_config_new(NULL
);
1919 debug_init(&nb_dbg_cbs
);
1921 install_node(&nb_debug_node
);
1922 install_element(ENABLE_NODE
, &debug_nb_cmd
);
1923 install_element(CONFIG_NODE
, &debug_nb_cmd
);
1925 /* Install commands specific to the transaction-base mode. */
1926 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL
) {
1927 install_element(ENABLE_NODE
, &config_exclusive_cmd
);
1928 install_element(ENABLE_NODE
, &config_private_cmd
);
1929 install_element(ENABLE_NODE
,
1930 &show_config_compare_without_candidate_cmd
);
1931 install_element(ENABLE_NODE
, &show_config_transaction_cmd
);
1932 install_element(ENABLE_NODE
, &rollback_config_cmd
);
1933 install_element(ENABLE_NODE
, &clear_config_transactions_cmd
);
1935 install_element(CONFIG_NODE
, &config_load_cmd
);
1936 install_element(CONFIG_NODE
,
1937 &config_database_max_transactions_cmd
);
1940 /* Other commands. */
1941 install_element(ENABLE_NODE
, &show_config_running_cmd
);
1942 install_element(CONFIG_NODE
, &yang_module_translator_load_cmd
);
1943 install_element(CONFIG_NODE
, &yang_module_translator_unload_cmd
);
1944 install_element(ENABLE_NODE
, &show_yang_operational_data_cmd
);
1945 install_element(ENABLE_NODE
, &show_yang_module_cmd
);
1946 install_element(ENABLE_NODE
, &show_yang_module_detail_cmd
);
1947 install_element(ENABLE_NODE
, &show_yang_module_translator_cmd
);
1948 cmd_variable_handler_register(yang_var_handlers
);
1951 void nb_cli_terminate(void)
1953 nb_config_free(vty_shared_candidate_config
);