2 * Copyright (C) 2018 NetDEF, Inc.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "lib/version.h"
26 #include "lib_errors.h"
28 #include "termtable.h"
31 #include "yang_translator.h"
32 #include "northbound.h"
33 #include "northbound_cli.h"
34 #include "northbound_db.h"
35 #ifndef VTYSH_EXTRACT_PL
36 #include "lib/northbound_cli_clippy.c"
39 struct debug nb_dbg_cbs_config
= {0, "Northbound callbacks: configuration"};
40 struct debug nb_dbg_cbs_state
= {0, "Northbound callbacks: state"};
41 struct debug nb_dbg_cbs_rpc
= {0, "Northbound callbacks: RPCs"};
42 struct debug nb_dbg_notif
= {0, "Northbound notifications"};
43 struct debug nb_dbg_events
= {0, "Northbound events"};
44 struct debug nb_dbg_libyang
= {0, "libyang debugging"};
46 struct nb_config
*vty_shared_candidate_config
;
47 static struct thread_master
*master
;
49 static void vty_show_nb_errors(struct vty
*vty
, int error
, const char *errmsg
)
51 vty_out(vty
, "Error type: %s\n", nb_err_name(error
));
52 if (strlen(errmsg
) > 0)
53 vty_out(vty
, "Error description: %s\n", errmsg
);
56 static int nb_cli_classic_commit(struct vty
*vty
)
58 struct nb_context context
= {};
59 char errmsg
[BUFSIZ
] = {0};
62 context
.client
= NB_CLIENT_CLI
;
64 ret
= nb_candidate_commit(&context
, vty
->candidate_config
, true, NULL
,
65 NULL
, errmsg
, sizeof(errmsg
));
68 /* Successful commit. Print warnings (if any). */
69 if (strlen(errmsg
) > 0)
70 vty_out(vty
, "%s\n", errmsg
);
72 case NB_ERR_NO_CHANGES
:
75 vty_out(vty
, "%% Configuration failed.\n\n");
76 vty_show_nb_errors(vty
, ret
, errmsg
);
77 if (vty
->pending_commit
)
79 "The following commands were dynamically grouped into the same transaction and rejected:\n%s",
80 vty
->pending_cmds_buf
);
82 /* Regenerate candidate for consistency. */
83 nb_config_replace(vty
->candidate_config
, running_config
, true);
84 return CMD_WARNING_CONFIG_FAILED
;
90 static void nb_cli_pending_commit_clear(struct vty
*vty
)
92 vty
->pending_commit
= 0;
93 XFREE(MTYPE_TMP
, vty
->pending_cmds_buf
);
94 vty
->pending_cmds_buflen
= 0;
95 vty
->pending_cmds_bufpos
= 0;
98 int nb_cli_pending_commit_check(struct vty
*vty
)
100 int ret
= CMD_SUCCESS
;
102 if (vty
->pending_commit
) {
103 ret
= nb_cli_classic_commit(vty
);
104 nb_cli_pending_commit_clear(vty
);
110 static int nb_cli_schedule_command(struct vty
*vty
)
112 /* Append command to dynamically sized buffer of scheduled commands. */
113 if (!vty
->pending_cmds_buf
) {
114 vty
->pending_cmds_buflen
= 4096;
115 vty
->pending_cmds_buf
=
116 XCALLOC(MTYPE_TMP
, vty
->pending_cmds_buflen
);
118 if ((strlen(vty
->buf
) + 3)
119 > (vty
->pending_cmds_buflen
- vty
->pending_cmds_bufpos
)) {
120 vty
->pending_cmds_buflen
*= 2;
121 vty
->pending_cmds_buf
=
122 XREALLOC(MTYPE_TMP
, vty
->pending_cmds_buf
,
123 vty
->pending_cmds_buflen
);
125 strlcat(vty
->pending_cmds_buf
, "- ", vty
->pending_cmds_buflen
);
126 vty
->pending_cmds_bufpos
= strlcat(vty
->pending_cmds_buf
, vty
->buf
,
127 vty
->pending_cmds_buflen
);
129 /* Schedule the commit operation. */
130 vty
->pending_commit
= 1;
135 void nb_cli_enqueue_change(struct vty
*vty
, const char *xpath
,
136 enum nb_operation operation
, const char *value
)
138 struct vty_cfg_change
*change
;
140 if (vty
->num_cfg_changes
== VTY_MAXCFGCHANGES
) {
141 /* Not expected to happen. */
143 "%% Exceeded the maximum number of changes (%u) for a single command\n\n",
148 change
= &vty
->cfg_changes
[vty
->num_cfg_changes
++];
149 strlcpy(change
->xpath
, xpath
, sizeof(change
->xpath
));
150 change
->operation
= operation
;
151 change
->value
= value
;
154 static int nb_cli_apply_changes_internal(struct vty
*vty
,
155 const char *xpath_base
,
160 if (xpath_base
== NULL
)
165 /* Edit candidate configuration. */
166 for (size_t i
= 0; i
< vty
->num_cfg_changes
; i
++) {
167 struct vty_cfg_change
*change
= &vty
->cfg_changes
[i
];
168 struct nb_node
*nb_node
;
169 char xpath
[XPATH_MAXLEN
];
170 struct yang_data
*data
;
173 /* Handle relative XPaths. */
174 memset(xpath
, 0, sizeof(xpath
));
175 if (vty
->xpath_index
> 0
176 && (xpath_base
[0] == '.' || change
->xpath
[0] == '.'))
177 strlcpy(xpath
, VTY_CURR_XPATH
, sizeof(xpath
));
179 if (xpath_base
[0] == '.')
180 strlcat(xpath
, xpath_base
+ 1, sizeof(xpath
));
182 strlcat(xpath
, xpath_base
, sizeof(xpath
));
184 if (change
->xpath
[0] == '.')
185 strlcat(xpath
, change
->xpath
+ 1, sizeof(xpath
));
187 strlcpy(xpath
, change
->xpath
, sizeof(xpath
));
189 /* Find the northbound node associated to the data path. */
190 nb_node
= nb_node_find(xpath
);
192 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
193 "%s: unknown data path: %s", __func__
, xpath
);
198 /* If the value is not set, get the default if it exists. */
199 if (change
->value
== NULL
)
200 change
->value
= yang_snode_get_default(nb_node
->snode
);
201 data
= yang_data_new(xpath
, change
->value
);
204 * Ignore "not found" errors when editing the candidate
207 ret
= nb_candidate_edit(vty
->candidate_config
, nb_node
,
208 change
->operation
, xpath
, NULL
, data
);
209 yang_data_free(data
);
210 if (ret
!= NB_OK
&& ret
!= NB_ERR_NOT_FOUND
) {
212 EC_LIB_NB_CANDIDATE_EDIT_ERROR
,
213 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
214 __func__
, nb_operation_name(change
->operation
),
225 * Failure to edit the candidate configuration should never
226 * happen in practice, unless there's a bug in the code. When
227 * that happens, log the error but otherwise ignore it.
229 vty_out(vty
, "%% Failed to edit configuration.\n\n");
231 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
235 * Maybe do an implicit commit when using the classic CLI mode.
237 * NOTE: the implicit commit might be scheduled to run later when
238 * too many commands are being sent at the same time. This is a
239 * protection mechanism where multiple commands are grouped into the
240 * same configuration transaction, allowing them to be processed much
243 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
) {
245 if (vty
->pending_commit
)
246 return nb_cli_pending_commit_check(vty
);
247 } else if (vty
->pending_allowed
)
248 return nb_cli_schedule_command(vty
);
249 assert(!vty
->pending_commit
);
250 return nb_cli_classic_commit(vty
);
256 int nb_cli_apply_changes(struct vty
*vty
, const char *xpath_base_fmt
, ...)
258 char xpath_base
[XPATH_MAXLEN
] = {};
260 /* Parse the base XPath format string. */
261 if (xpath_base_fmt
) {
264 va_start(ap
, xpath_base_fmt
);
265 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
268 return nb_cli_apply_changes_internal(vty
, xpath_base
, false);
271 int nb_cli_apply_changes_clear_pending(struct vty
*vty
,
272 const char *xpath_base_fmt
, ...)
274 char xpath_base
[XPATH_MAXLEN
] = {};
276 /* Parse the base XPath format string. */
277 if (xpath_base_fmt
) {
280 va_start(ap
, xpath_base_fmt
);
281 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
284 return nb_cli_apply_changes_internal(vty
, xpath_base
, true);
287 int nb_cli_rpc(struct vty
*vty
, const char *xpath
, struct list
*input
,
290 struct nb_node
*nb_node
;
292 char errmsg
[BUFSIZ
] = {0};
294 nb_node
= nb_node_find(xpath
);
296 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
297 "%s: unknown data path: %s", __func__
, xpath
);
301 ret
= nb_callback_rpc(nb_node
, xpath
, input
, output
, errmsg
,
308 vty_show_nb_errors(vty
, ret
, errmsg
);
313 void nb_cli_confirmed_commit_clean(struct vty
*vty
)
315 thread_cancel(&vty
->t_confirmed_commit_timeout
);
316 nb_config_free(vty
->confirmed_commit_rollback
);
317 vty
->confirmed_commit_rollback
= NULL
;
320 int nb_cli_confirmed_commit_rollback(struct vty
*vty
)
322 struct nb_context context
= {};
323 uint32_t transaction_id
;
324 char errmsg
[BUFSIZ
] = {0};
327 /* Perform the rollback. */
328 context
.client
= NB_CLIENT_CLI
;
330 ret
= nb_candidate_commit(
331 &context
, vty
->confirmed_commit_rollback
, true,
332 "Rollback to previous configuration - confirmed commit has timed out",
333 &transaction_id
, errmsg
, sizeof(errmsg
));
336 "Rollback performed successfully (Transaction ID #%u).\n",
338 /* Print warnings (if any). */
339 if (strlen(errmsg
) > 0)
340 vty_out(vty
, "%s\n", errmsg
);
343 "Failed to rollback to previous configuration.\n\n");
344 vty_show_nb_errors(vty
, ret
, errmsg
);
350 static void nb_cli_confirmed_commit_timeout(struct thread
*thread
)
352 struct vty
*vty
= THREAD_ARG(thread
);
354 /* XXX: broadcast this message to all logged-in users? */
356 "\nConfirmed commit has timed out, rolling back to previous configuration.\n\n");
358 nb_cli_confirmed_commit_rollback(vty
);
359 nb_cli_confirmed_commit_clean(vty
);
362 static int nb_cli_commit(struct vty
*vty
, bool force
,
363 unsigned int confirmed_timeout
, char *comment
)
365 struct nb_context context
= {};
366 uint32_t transaction_id
= 0;
367 char errmsg
[BUFSIZ
] = {0};
370 /* Check if there's a pending confirmed commit. */
371 if (vty
->t_confirmed_commit_timeout
) {
372 if (confirmed_timeout
) {
373 /* Reset timeout if "commit confirmed" is used again. */
375 "%% Resetting confirmed-commit timeout to %u minute(s)\n\n",
378 thread_cancel(&vty
->t_confirmed_commit_timeout
);
379 thread_add_timer(master
,
380 nb_cli_confirmed_commit_timeout
, vty
,
381 confirmed_timeout
* 60,
382 &vty
->t_confirmed_commit_timeout
);
384 /* Accept commit confirmation. */
385 vty_out(vty
, "%% Commit complete.\n\n");
386 nb_cli_confirmed_commit_clean(vty
);
391 /* "force" parameter. */
392 if (!force
&& nb_candidate_needs_update(vty
->candidate_config
)) {
394 "%% Candidate configuration needs to be updated before commit.\n\n");
396 "Use the \"update\" command or \"commit force\".\n");
400 /* "confirm" parameter. */
401 if (confirmed_timeout
) {
402 vty
->confirmed_commit_rollback
= nb_config_dup(running_config
);
404 vty
->t_confirmed_commit_timeout
= NULL
;
405 thread_add_timer(master
, nb_cli_confirmed_commit_timeout
, vty
,
406 confirmed_timeout
* 60,
407 &vty
->t_confirmed_commit_timeout
);
410 context
.client
= NB_CLIENT_CLI
;
412 ret
= nb_candidate_commit(&context
, vty
->candidate_config
, true,
413 comment
, &transaction_id
, errmsg
,
416 /* Map northbound return code to CLI return code. */
419 nb_config_replace(vty
->candidate_config_base
, running_config
,
422 "%% Configuration committed successfully (Transaction ID #%u).\n\n",
424 /* Print warnings (if any). */
425 if (strlen(errmsg
) > 0)
426 vty_out(vty
, "%s\n", errmsg
);
428 case NB_ERR_NO_CHANGES
:
429 vty_out(vty
, "%% No configuration changes to commit.\n\n");
433 "%% Failed to commit candidate configuration.\n\n");
434 vty_show_nb_errors(vty
, ret
, errmsg
);
439 static int nb_cli_candidate_load_file(struct vty
*vty
,
440 enum nb_cfg_format format
,
441 struct yang_translator
*translator
,
442 const char *path
, bool replace
)
444 struct nb_config
*loaded_config
= NULL
;
445 struct lyd_node
*dnode
;
446 struct ly_ctx
*ly_ctx
;
452 case NB_CFG_FMT_CMDS
:
453 loaded_config
= nb_config_new(NULL
);
454 if (!vty_read_config(loaded_config
, path
, config_default
)) {
455 vty_out(vty
, "%% Failed to load configuration.\n\n");
457 "Please check the logs for more details.\n");
458 nb_config_free(loaded_config
);
462 case NB_CFG_FMT_JSON
:
464 ly_format
= (format
== NB_CFG_FMT_JSON
) ? LYD_JSON
: LYD_XML
;
466 ly_ctx
= translator
? translator
->ly_ctx
: ly_native_ctx
;
467 err
= lyd_parse_data_path(ly_ctx
, path
, ly_format
,
468 LYD_PARSE_ONLY
| LYD_PARSE_NO_STATE
,
471 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_path() failed",
473 vty_out(vty
, "%% Failed to load configuration:\n\n");
475 yang_print_errors(ly_native_ctx
, buf
,
480 && yang_translate_dnode(translator
,
481 YANG_TRANSLATE_TO_NATIVE
, &dnode
)
482 != YANG_TRANSLATE_SUCCESS
) {
483 vty_out(vty
, "%% Failed to translate configuration\n");
484 yang_dnode_free(dnode
);
487 loaded_config
= nb_config_new(dnode
);
492 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
493 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
496 "%% Failed to merge the loaded configuration:\n\n");
498 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
505 static int nb_cli_candidate_load_transaction(struct vty
*vty
,
506 uint32_t transaction_id
,
509 struct nb_config
*loaded_config
;
512 loaded_config
= nb_db_transaction_load(transaction_id
);
513 if (!loaded_config
) {
514 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
520 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
521 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
524 "%% Failed to merge the loaded configuration:\n\n");
526 yang_print_errors(ly_native_ctx
, buf
, sizeof(buf
)));
533 /* Prepare the configuration for display. */
534 void nb_cli_show_config_prepare(struct nb_config
*config
, bool with_defaults
)
536 /* Nothing to do for daemons that don't implement any YANG module. */
537 if (config
->dnode
== NULL
)
541 * Call lyd_validate() only to create default child nodes, ignoring
542 * any possible validation error. This doesn't need to be done when
543 * displaying the running configuration since it's always fully
546 if (config
!= running_config
)
547 (void)lyd_validate_all(&config
->dnode
, ly_native_ctx
,
548 LYD_VALIDATE_NO_STATE
, NULL
);
551 static int lyd_node_cmp(const struct lyd_node
**dnode1
,
552 const struct lyd_node
**dnode2
)
554 struct nb_node
*nb_node
= (*dnode1
)->schema
->priv
;
556 return nb_node
->cbs
.cli_cmp(*dnode1
, *dnode2
);
559 static void show_dnode_children_cmds(struct vty
*vty
,
560 const struct lyd_node
*root
,
563 struct nb_node
*nb_node
, *sort_node
= NULL
;
564 struct listnode
*listnode
;
565 struct lyd_node
*child
;
566 struct list
*sort_list
;
569 LY_LIST_FOR (lyd_child(root
), child
) {
570 nb_node
= child
->schema
->priv
;
573 * We finished processing current list,
574 * it's time to print the config.
576 if (sort_node
&& nb_node
!= sort_node
) {
578 (int (*)(const void **,
579 const void **))lyd_node_cmp
);
581 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
582 nb_cli_show_dnode_cmds(vty
, data
,
585 list_delete(&sort_list
);
590 * If the config needs to be sorted,
591 * then add the dnode to the sorting
592 * list for later processing.
594 if (nb_node
&& nb_node
->cbs
.cli_cmp
) {
597 sort_list
= list_new();
600 listnode_add(sort_list
, child
);
604 nb_cli_show_dnode_cmds(vty
, child
, with_defaults
);
609 (int (*)(const void **, const void **))lyd_node_cmp
);
611 for (ALL_LIST_ELEMENTS_RO(sort_list
, listnode
, data
))
612 nb_cli_show_dnode_cmds(vty
, data
, with_defaults
);
614 list_delete(&sort_list
);
619 void nb_cli_show_dnode_cmds(struct vty
*vty
, const struct lyd_node
*root
,
622 struct nb_node
*nb_node
;
624 if (!with_defaults
&& yang_dnode_is_default_recursive(root
))
627 nb_node
= root
->schema
->priv
;
629 if (nb_node
&& nb_node
->cbs
.cli_show
)
630 (*nb_node
->cbs
.cli_show
)(vty
, root
, with_defaults
);
632 if (!(root
->schema
->nodetype
& (LYS_LEAF
| LYS_LEAFLIST
| LYS_ANYDATA
)))
633 show_dnode_children_cmds(vty
, root
, with_defaults
);
635 if (nb_node
&& nb_node
->cbs
.cli_show_end
)
636 (*nb_node
->cbs
.cli_show_end
)(vty
, root
);
639 static void nb_cli_show_config_cmds(struct vty
*vty
, struct nb_config
*config
,
642 struct lyd_node
*root
;
644 vty_out(vty
, "Configuration:\n");
646 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
647 vty_out(vty
, "frr defaults %s\n", frr_defaults_profile());
649 LY_LIST_FOR (config
->dnode
, root
) {
650 nb_cli_show_dnode_cmds(vty
, root
, with_defaults
);
654 vty_out(vty
, "end\n");
657 static int nb_cli_show_config_libyang(struct vty
*vty
, LYD_FORMAT format
,
658 struct nb_config
*config
,
659 struct yang_translator
*translator
,
662 struct lyd_node
*dnode
;
666 dnode
= yang_dnode_dup(config
->dnode
);
668 && yang_translate_dnode(translator
, YANG_TRANSLATE_FROM_NATIVE
,
670 != YANG_TRANSLATE_SUCCESS
) {
671 vty_out(vty
, "%% Failed to translate configuration\n");
672 yang_dnode_free(dnode
);
676 SET_FLAG(options
, LYD_PRINT_WITHSIBLINGS
);
678 SET_FLAG(options
, LYD_PRINT_WD_ALL
);
680 SET_FLAG(options
, LYD_PRINT_WD_TRIM
);
682 if (lyd_print_mem(&strp
, dnode
, format
, options
) == 0 && strp
) {
683 vty_out(vty
, "%s", strp
);
687 yang_dnode_free(dnode
);
692 static int nb_cli_show_config(struct vty
*vty
, struct nb_config
*config
,
693 enum nb_cfg_format format
,
694 struct yang_translator
*translator
,
697 nb_cli_show_config_prepare(config
, with_defaults
);
700 case NB_CFG_FMT_CMDS
:
701 nb_cli_show_config_cmds(vty
, config
, with_defaults
);
703 case NB_CFG_FMT_JSON
:
704 return nb_cli_show_config_libyang(vty
, LYD_JSON
, config
,
705 translator
, with_defaults
);
707 return nb_cli_show_config_libyang(vty
, LYD_XML
, config
,
708 translator
, with_defaults
);
714 static int nb_write_config(struct nb_config
*config
, enum nb_cfg_format format
,
715 struct yang_translator
*translator
, char *path
,
719 struct vty
*file_vty
;
722 snprintf(path
, pathlen
, "/tmp/frr.tmp.XXXXXXXX");
725 flog_warn(EC_LIB_SYSTEM_CALL
, "%s: mkstemp() failed: %s",
726 __func__
, safe_strerror(errno
));
729 if (fchmod(fd
, CONFIGFILE_MASK
) != 0) {
730 flog_warn(EC_LIB_SYSTEM_CALL
,
731 "%s: fchmod() failed: %s(%d):", __func__
,
732 safe_strerror(errno
), errno
);
736 /* Make vty for configuration file. */
737 file_vty
= vty_new();
739 file_vty
->type
= VTY_FILE
;
741 ret
= nb_cli_show_config(file_vty
, config
, format
, translator
,
748 static int nb_cli_show_config_compare(struct vty
*vty
,
749 struct nb_config
*config1
,
750 struct nb_config
*config2
,
751 enum nb_cfg_format format
,
752 struct yang_translator
*translator
)
754 char config1_path
[256];
755 char config2_path
[256];
756 char command
[BUFSIZ
];
761 if (nb_write_config(config1
, format
, translator
, config1_path
,
762 sizeof(config1_path
))
764 vty_out(vty
, "%% Failed to process configurations.\n\n");
767 if (nb_write_config(config2
, format
, translator
, config2_path
,
768 sizeof(config2_path
))
770 vty_out(vty
, "%% Failed to process configurations.\n\n");
771 unlink(config1_path
);
775 snprintf(command
, sizeof(command
), "diff -u %s %s", config1_path
,
777 fp
= popen(command
, "r");
779 vty_out(vty
, "%% Failed to generate configuration diff.\n\n");
780 unlink(config1_path
);
781 unlink(config2_path
);
784 /* Print diff line by line. */
785 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
788 vty_out(vty
, "%s", line
);
792 unlink(config1_path
);
793 unlink(config2_path
);
798 /* Configure exclusively from this terminal. */
799 DEFUN (config_exclusive
,
800 config_exclusive_cmd
,
801 "configure exclusive",
802 "Configuration from vty interface\n"
803 "Configure exclusively from this terminal\n")
805 return vty_config_enter(vty
, true, true);
808 /* Configure using a private candidate configuration. */
809 DEFUN (config_private
,
812 "Configuration from vty interface\n"
813 "Configure using a private candidate configuration\n")
815 return vty_config_enter(vty
, true, false);
818 DEFPY (config_commit
,
820 "commit [{force$force|confirmed (1-60)}]",
821 "Commit changes into the running configuration\n"
822 "Force commit even if the candidate is outdated\n"
823 "Rollback this commit unless there is a confirming commit\n"
824 "Timeout in minutes for the commit to be confirmed\n")
826 return nb_cli_commit(vty
, !!force
, confirmed
, NULL
);
829 DEFPY (config_commit_comment
,
830 config_commit_comment_cmd
,
831 "commit [{force$force|confirmed (1-60)}] comment LINE...",
832 "Commit changes into the running configuration\n"
833 "Force commit even if the candidate is outdated\n"
834 "Rollback this commit unless there is a confirming commit\n"
835 "Timeout in minutes for the commit to be confirmed\n"
836 "Assign a comment to this commit\n"
837 "Comment for this commit (Max 80 characters)\n")
843 argv_find(argv
, argc
, "LINE", &idx
);
844 comment
= argv_concat(argv
, argc
, idx
);
845 ret
= nb_cli_commit(vty
, !!force
, confirmed
, comment
);
846 XFREE(MTYPE_TMP
, comment
);
851 DEFPY (config_commit_check
,
852 config_commit_check_cmd
,
854 "Commit changes into the running configuration\n"
855 "Check if the configuration changes are valid\n")
857 struct nb_context context
= {};
858 char errmsg
[BUFSIZ
] = {0};
861 context
.client
= NB_CLIENT_CLI
;
863 ret
= nb_candidate_validate(&context
, vty
->candidate_config
, errmsg
,
867 "%% Failed to validate candidate configuration.\n\n");
868 vty_show_nb_errors(vty
, ret
, errmsg
);
872 vty_out(vty
, "%% Candidate configuration validated successfully.\n\n");
877 DEFPY (config_update
,
880 "Update candidate configuration\n")
882 if (!nb_candidate_needs_update(vty
->candidate_config
)) {
883 vty_out(vty
, "%% Update is not necessary.\n\n");
887 if (nb_candidate_update(vty
->candidate_config
) != NB_OK
) {
889 "%% Failed to update the candidate configuration.\n\n");
890 vty_out(vty
, "Please check the logs for more details.\n");
894 nb_config_replace(vty
->candidate_config_base
, running_config
, true);
896 vty_out(vty
, "%% Candidate configuration updated successfully.\n\n");
901 DEFPY (config_discard
,
904 "Discard changes in the candidate configuration\n")
906 nb_config_replace(vty
->candidate_config
, vty
->candidate_config_base
,
916 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
917 |transaction (1-4294967295)$tid\
920 "Configuration related settings\n"
921 "Load configuration into candidate\n"
922 "Load configuration file into candidate\n"
923 "Load configuration file in JSON format\n"
924 "Load configuration file in XML format\n"
925 "Translate configuration file\n"
926 "YANG module translator\n"
927 "Configuration file name (full path)\n"
928 "Load configuration from transaction into candidate\n"
930 "Replace instead of merge\n")
933 enum nb_cfg_format format
;
934 struct yang_translator
*translator
= NULL
;
937 format
= NB_CFG_FMT_JSON
;
939 format
= NB_CFG_FMT_XML
;
941 format
= NB_CFG_FMT_CMDS
;
943 if (translator_family
) {
944 translator
= yang_translator_find(translator_family
);
947 "%% Module translator \"%s\" not found\n",
953 return nb_cli_candidate_load_file(vty
, format
, translator
,
954 filename
, !!replace
);
957 return nb_cli_candidate_load_transaction(vty
, tid
, !!replace
);
960 DEFPY (show_config_running
,
961 show_config_running_cmd
,
962 "show configuration running\
963 [<json$json|xml$xml> [translate WORD$translator_family]]\
964 [with-defaults$with_defaults]",
966 "Configuration information\n"
967 "Running configuration\n"
968 "Change output format to JSON\n"
969 "Change output format to XML\n"
971 "YANG module translator\n"
972 "Show default values\n")
975 enum nb_cfg_format format
;
976 struct yang_translator
*translator
= NULL
;
979 format
= NB_CFG_FMT_JSON
;
981 format
= NB_CFG_FMT_XML
;
983 format
= NB_CFG_FMT_CMDS
;
985 if (translator_family
) {
986 translator
= yang_translator_find(translator_family
);
988 vty_out(vty
, "%% Module translator \"%s\" not found\n",
994 nb_cli_show_config(vty
, running_config
, format
, translator
,
1000 DEFPY (show_config_candidate
,
1001 show_config_candidate_cmd
,
1002 "show configuration candidate\
1003 [<json$json|xml$xml> [translate WORD$translator_family]]\
1005 with-defaults$with_defaults\
1009 "Configuration information\n"
1010 "Candidate configuration\n"
1011 "Change output format to JSON\n"
1012 "Change output format to XML\n"
1013 "Translate output\n"
1014 "YANG module translator\n"
1015 "Show default values\n"
1016 "Show changes applied in the candidate configuration\n")
1019 enum nb_cfg_format format
;
1020 struct yang_translator
*translator
= NULL
;
1023 format
= NB_CFG_FMT_JSON
;
1025 format
= NB_CFG_FMT_XML
;
1027 format
= NB_CFG_FMT_CMDS
;
1029 if (translator_family
) {
1030 translator
= yang_translator_find(translator_family
);
1032 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1039 return nb_cli_show_config_compare(
1040 vty
, vty
->candidate_config_base
, vty
->candidate_config
,
1041 format
, translator
);
1043 nb_cli_show_config(vty
, vty
->candidate_config
, format
, translator
,
1049 DEFPY (show_config_candidate_section
,
1050 show_config_candidate_section_cmd
,
1054 struct lyd_node
*dnode
;
1056 /* Top-level configuration node, display everything. */
1057 if (vty
->xpath_index
== 0)
1058 return nb_cli_show_config(vty
, vty
->candidate_config
,
1059 NB_CFG_FMT_CMDS
, NULL
, false);
1061 /* Display only the current section of the candidate configuration. */
1062 dnode
= yang_dnode_get(vty
->candidate_config
->dnode
, VTY_CURR_XPATH
);
1064 /* Shouldn't happen. */
1067 nb_cli_show_dnode_cmds(vty
, dnode
, 0);
1068 vty_out(vty
, "!\n");
1073 DEFPY (show_config_compare
,
1074 show_config_compare_cmd
,
1075 "show configuration compare\
1077 candidate$c1_candidate\
1078 |running$c1_running\
1079 |transaction (1-4294967295)$c1_tid\
1082 candidate$c2_candidate\
1083 |running$c2_running\
1084 |transaction (1-4294967295)$c2_tid\
1086 [<json$json|xml$xml> [translate WORD$translator_family]]",
1088 "Configuration information\n"
1089 "Compare two different configurations\n"
1090 "Candidate configuration\n"
1091 "Running configuration\n"
1092 "Configuration transaction\n"
1094 "Candidate configuration\n"
1095 "Running configuration\n"
1096 "Configuration transaction\n"
1098 "Change output format to JSON\n"
1099 "Change output format to XML\n"
1100 "Translate output\n"
1101 "YANG module translator\n")
1103 enum nb_cfg_format format
;
1104 struct yang_translator
*translator
= NULL
;
1105 struct nb_config
*config1
, *config_transaction1
= NULL
;
1106 struct nb_config
*config2
, *config_transaction2
= NULL
;
1107 int ret
= CMD_WARNING
;
1110 config1
= vty
->candidate_config
;
1111 else if (c1_running
)
1112 config1
= running_config
;
1114 config_transaction1
= nb_db_transaction_load(c1_tid
);
1115 if (!config_transaction1
) {
1116 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1117 (unsigned int)c1_tid
);
1120 config1
= config_transaction1
;
1124 config2
= vty
->candidate_config
;
1125 else if (c2_running
)
1126 config2
= running_config
;
1128 config_transaction2
= nb_db_transaction_load(c2_tid
);
1129 if (!config_transaction2
) {
1130 vty_out(vty
, "%% Transaction %u does not exist\n\n",
1131 (unsigned int)c2_tid
);
1134 config2
= config_transaction2
;
1138 format
= NB_CFG_FMT_JSON
;
1140 format
= NB_CFG_FMT_XML
;
1142 format
= NB_CFG_FMT_CMDS
;
1144 if (translator_family
) {
1145 translator
= yang_translator_find(translator_family
);
1147 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1153 ret
= nb_cli_show_config_compare(vty
, config1
, config2
, format
,
1156 if (config_transaction1
)
1157 nb_config_free(config_transaction1
);
1158 if (config_transaction2
)
1159 nb_config_free(config_transaction2
);
1165 * Stripped down version of the "show configuration compare" command.
1166 * The "candidate" option is not present so the command can be installed in
1169 ALIAS (show_config_compare
,
1170 show_config_compare_without_candidate_cmd
,
1171 "show configuration compare\
1174 |transaction (1-4294967295)$c1_tid\
1178 |transaction (1-4294967295)$c2_tid\
1180 [<json$json|xml$xml> [translate WORD$translator_family]]",
1182 "Configuration information\n"
1183 "Compare two different configurations\n"
1184 "Running configuration\n"
1185 "Configuration transaction\n"
1187 "Running configuration\n"
1188 "Configuration transaction\n"
1190 "Change output format to JSON\n"
1191 "Change output format to XML\n"
1192 "Translate output\n"
1193 "YANG module translator\n")
1195 DEFPY (clear_config_transactions
,
1196 clear_config_transactions_cmd
,
1197 "clear configuration transactions oldest (1-100)$n",
1199 "Configuration activity\n"
1200 "Delete transactions from the transactions log\n"
1201 "Delete oldest <n> transactions\n"
1202 "Number of transactions to delete\n")
1204 #ifdef HAVE_CONFIG_ROLLBACKS
1205 if (nb_db_clear_transactions(n
) != NB_OK
) {
1206 vty_out(vty
, "%% Failed to delete transactions.\n\n");
1211 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1212 #endif /* HAVE_CONFIG_ROLLBACKS */
1217 DEFPY (config_database_max_transactions
,
1218 config_database_max_transactions_cmd
,
1219 "configuration database max-transactions (1-100)$max",
1220 "Configuration related settings\n"
1221 "Configuration database\n"
1222 "Set the maximum number of transactions to store\n"
1223 "Number of transactions\n")
1225 #ifdef HAVE_CONFIG_ROLLBACKS
1226 if (nb_db_set_max_transactions(max
) != NB_OK
) {
1228 "%% Failed to update the maximum number of transactions.\n\n");
1232 "%% Maximum number of transactions updated successfully.\n\n");
1235 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1236 #endif /* HAVE_CONFIG_ROLLBACKS */
1241 DEFPY (yang_module_translator_load
,
1242 yang_module_translator_load_cmd
,
1243 "yang module-translator load FILENAME$filename",
1244 "YANG related settings\n"
1245 "YANG module translator\n"
1246 "Load YANG module translator\n"
1247 "File name (full path)\n")
1249 struct yang_translator
*translator
;
1251 translator
= yang_translator_load(filename
);
1253 vty_out(vty
, "%% Failed to load \"%s\"\n\n", filename
);
1254 vty_out(vty
, "Please check the logs for more details.\n");
1258 vty_out(vty
, "%% Module translator \"%s\" loaded successfully.\n\n",
1259 translator
->family
);
1264 DEFPY (yang_module_translator_unload_family
,
1265 yang_module_translator_unload_cmd
,
1266 "yang module-translator unload WORD$translator_family",
1267 "YANG related settings\n"
1268 "YANG module translator\n"
1269 "Unload YANG module translator\n"
1270 "Name of the module translator\n")
1272 struct yang_translator
*translator
;
1274 translator
= yang_translator_find(translator_family
);
1276 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1281 yang_translator_unload(translator
);
1286 #ifdef HAVE_CONFIG_ROLLBACKS
1287 static void nb_cli_show_transactions_cb(void *arg
, int transaction_id
,
1288 const char *client_name
,
1289 const char *date
, const char *comment
)
1291 struct ttable
*tt
= arg
;
1293 ttable_add_row(tt
, "%d|%s|%s|%s", transaction_id
, client_name
, date
,
1297 static int nb_cli_show_transactions(struct vty
*vty
)
1301 /* Prepare table. */
1302 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1303 ttable_add_row(tt
, "Transaction ID|Client|Date|Comment");
1304 tt
->style
.cell
.rpad
= 2;
1305 tt
->style
.corner
= '+';
1307 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1309 /* Fetch transactions from the northbound database. */
1310 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb
, tt
)
1313 "%% Failed to fetch configuration transactions.\n");
1317 /* Dump the generated table. */
1318 if (tt
->nrows
> 1) {
1321 table
= ttable_dump(tt
, "\n");
1322 vty_out(vty
, "%s\n", table
);
1323 XFREE(MTYPE_TMP
, table
);
1325 vty_out(vty
, "No configuration transactions to display.\n\n");
1331 #endif /* HAVE_CONFIG_ROLLBACKS */
1333 DEFPY (show_config_transaction
,
1334 show_config_transaction_cmd
,
1335 "show configuration transaction\
1337 (1-4294967295)$transaction_id\
1338 [<json$json|xml$xml> [translate WORD$translator_family]]\
1340 with-defaults$with_defaults\
1345 "Configuration information\n"
1346 "Configuration transaction\n"
1348 "Change output format to JSON\n"
1349 "Change output format to XML\n"
1350 "Translate output\n"
1351 "YANG module translator\n"
1352 "Show default values\n"
1353 "Show changes compared to the previous transaction\n")
1355 #ifdef HAVE_CONFIG_ROLLBACKS
1356 if (transaction_id
) {
1357 struct nb_config
*config
;
1358 enum nb_cfg_format format
;
1359 struct yang_translator
*translator
= NULL
;
1362 format
= NB_CFG_FMT_JSON
;
1364 format
= NB_CFG_FMT_XML
;
1366 format
= NB_CFG_FMT_CMDS
;
1368 if (translator_family
) {
1369 translator
= yang_translator_find(translator_family
);
1372 "%% Module translator \"%s\" not found\n",
1378 config
= nb_db_transaction_load(transaction_id
);
1380 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1381 (unsigned int)transaction_id
);
1386 struct nb_config
*prev_config
;
1389 /* NOTE: this can be NULL. */
1391 nb_db_transaction_load(transaction_id
- 1);
1393 ret
= nb_cli_show_config_compare(
1394 vty
, prev_config
, config
, format
, translator
);
1396 nb_config_free(prev_config
);
1397 nb_config_free(config
);
1402 nb_cli_show_config(vty
, config
, format
, translator
,
1404 nb_config_free(config
);
1409 return nb_cli_show_transactions(vty
);
1412 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1414 #endif /* HAVE_CONFIG_ROLLBACKS */
1417 static int nb_cli_oper_data_cb(const struct lysc_node
*snode
,
1418 struct yang_translator
*translator
,
1419 struct yang_data
*data
, void *arg
)
1421 struct lyd_node
*dnode
= arg
;
1422 struct ly_ctx
*ly_ctx
;
1427 ret
= yang_translate_xpath(translator
,
1428 YANG_TRANSLATE_FROM_NATIVE
,
1429 data
->xpath
, sizeof(data
->xpath
));
1431 case YANG_TRANSLATE_SUCCESS
:
1433 case YANG_TRANSLATE_NOTFOUND
:
1435 case YANG_TRANSLATE_FAILURE
:
1439 ly_ctx
= translator
->ly_ctx
;
1441 ly_ctx
= ly_native_ctx
;
1444 lyd_new_path(dnode
, ly_ctx
, data
->xpath
, (void *)data
->value
,
1445 LYD_NEW_PATH_UPDATE
, &dnode
);
1447 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_new_path(%s) failed: %s",
1448 __func__
, data
->xpath
, ly_errmsg(ly_native_ctx
));
1453 yang_data_free(data
);
1457 yang_data_free(data
);
1461 DEFPY (show_yang_operational_data
,
1462 show_yang_operational_data_cmd
,
1463 "show yang operational-data XPATH$xpath\
1465 format <json$json|xml$xml>\
1466 |translate WORD$translator_family\
1469 "YANG information\n"
1470 "Show YANG operational data\n"
1471 "XPath expression specifying the YANG data path\n"
1472 "Set the output format\n"
1473 "JavaScript Object Notation\n"
1474 "Extensible Markup Language\n"
1475 "Translate operational data\n"
1476 "YANG module translator\n")
1479 struct yang_translator
*translator
= NULL
;
1480 struct ly_ctx
*ly_ctx
;
1481 struct lyd_node
*dnode
;
1489 if (translator_family
) {
1490 translator
= yang_translator_find(translator_family
);
1492 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1497 ly_ctx
= translator
->ly_ctx
;
1499 ly_ctx
= ly_native_ctx
;
1502 dnode
= yang_dnode_new(ly_ctx
, false);
1503 if (nb_oper_data_iterate(xpath
, translator
, 0, nb_cli_oper_data_cb
,
1506 vty_out(vty
, "%% Failed to fetch operational data.\n");
1507 yang_dnode_free(dnode
);
1510 (void)lyd_validate_all(&dnode
, ly_ctx
, 0, NULL
);
1512 /* Display the data. */
1513 if (lyd_print_mem(&strp
, dnode
, format
,
1514 LYD_PRINT_WITHSIBLINGS
| LYD_PRINT_WD_ALL
)
1517 vty_out(vty
, "%% Failed to display operational data.\n");
1518 yang_dnode_free(dnode
);
1521 vty_out(vty
, "%s", strp
);
1523 yang_dnode_free(dnode
);
1528 DEFPY (show_yang_module
,
1529 show_yang_module_cmd
,
1530 "show yang module [module-translator WORD$translator_family]",
1532 "YANG information\n"
1533 "Show loaded modules\n"
1534 "YANG module translator\n"
1535 "YANG module translator\n")
1537 struct ly_ctx
*ly_ctx
;
1538 struct yang_translator
*translator
= NULL
;
1539 const struct lys_module
*module
;
1543 if (translator_family
) {
1544 translator
= yang_translator_find(translator_family
);
1546 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1550 ly_ctx
= translator
->ly_ctx
;
1552 ly_ctx
= ly_native_ctx
;
1554 /* Prepare table. */
1555 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1556 ttable_add_row(tt
, "Module|Version|Revision|Flags|Namespace");
1557 tt
->style
.cell
.rpad
= 2;
1558 tt
->style
.corner
= '+';
1560 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1562 while ((module
= ly_ctx_get_module_iter(ly_ctx
, &idx
))) {
1565 snprintf(flags
, sizeof(flags
), "%c%c",
1566 module
->implemented
? 'I' : ' ',
1567 LY_ARRAY_COUNT(module
->deviated_by
) ? 'D' : ' ');
1569 ttable_add_row(tt
, "%s|%s|%s|%s|%s", module
->name
,
1570 (module
->parsed
->version
== 2) ? "1.1" : "1.0",
1571 module
->revision
? module
->revision
: "-", flags
,
1575 /* Dump the generated table. */
1576 if (tt
->nrows
> 1) {
1579 vty_out(vty
, " Flags: I - Implemented, D - Deviated\n\n");
1581 table
= ttable_dump(tt
, "\n");
1582 vty_out(vty
, "%s\n", table
);
1583 XFREE(MTYPE_TMP
, table
);
1585 vty_out(vty
, "No YANG modules to display.\n\n");
1592 DEFPY(show_yang_module_detail
, show_yang_module_detail_cmd
,
1594 [module-translator WORD$translator_family]\
1595 WORD$module_name <compiled$compiled|summary|tree$tree|yang$yang|yin$yin>",
1597 "YANG information\n"
1598 "Show loaded modules\n"
1599 "YANG module translator\n"
1600 "YANG module translator\n"
1602 "Display compiled module in YANG format\n"
1603 "Display summary information about the module\n"
1604 "Display module in the tree (RFC 8340) format\n"
1605 "Display module in the YANG format\n"
1606 "Display module in the YIN format\n")
1608 struct ly_ctx
*ly_ctx
;
1609 struct yang_translator
*translator
= NULL
;
1610 const struct lys_module
*module
;
1611 LYS_OUTFORMAT format
;
1614 if (translator_family
) {
1615 translator
= yang_translator_find(translator_family
);
1617 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1621 ly_ctx
= translator
->ly_ctx
;
1623 ly_ctx
= ly_native_ctx
;
1625 module
= ly_ctx_get_module_latest(ly_ctx
, module_name
);
1627 vty_out(vty
, "%% Module \"%s\" not found\n", module_name
);
1632 format
= LYS_OUT_YANG
;
1634 format
= LYS_OUT_YIN
;
1636 format
= LYS_OUT_YANG_COMPILED
;
1638 format
= LYS_OUT_TREE
;
1641 "%% libyang v2 does not currently support summary\n");
1645 if (lys_print_mem(&strp
, module
, format
, 0) == 0) {
1646 vty_out(vty
, "%s\n", strp
);
1650 vty_out(vty
, "%% Error generating module information\n");
1657 DEFPY (show_yang_module_translator
,
1658 show_yang_module_translator_cmd
,
1659 "show yang module-translator",
1661 "YANG information\n"
1662 "Show loaded YANG module translators\n")
1664 struct yang_translator
*translator
;
1667 /* Prepare table. */
1668 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1669 ttable_add_row(tt
, "Family|Module|Deviations|Coverage (%%)");
1670 tt
->style
.cell
.rpad
= 2;
1671 tt
->style
.corner
= '+';
1673 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1675 RB_FOREACH (translator
, yang_translators
, &yang_translators
) {
1676 struct yang_tmodule
*tmodule
;
1677 struct listnode
*ln
;
1679 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
1680 ttable_add_row(tt
, "%s|%s|%s|%.2f", translator
->family
,
1681 tmodule
->module
->name
,
1682 tmodule
->deviations
->name
,
1687 /* Dump the generated table. */
1688 if (tt
->nrows
> 1) {
1691 table
= ttable_dump(tt
, "\n");
1692 vty_out(vty
, "%s\n", table
);
1693 XFREE(MTYPE_TMP
, table
);
1695 vty_out(vty
, "No YANG module translators to display.\n\n");
1702 #ifdef HAVE_CONFIG_ROLLBACKS
1703 static int nb_cli_rollback_configuration(struct vty
*vty
,
1704 uint32_t transaction_id
)
1706 struct nb_context context
= {};
1707 struct nb_config
*candidate
;
1709 char errmsg
[BUFSIZ
] = {0};
1712 candidate
= nb_db_transaction_load(transaction_id
);
1714 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1719 snprintf(comment
, sizeof(comment
), "Rollback to transaction %u",
1722 context
.client
= NB_CLIENT_CLI
;
1724 ret
= nb_candidate_commit(&context
, candidate
, true, comment
, NULL
,
1725 errmsg
, sizeof(errmsg
));
1726 nb_config_free(candidate
);
1730 "%% Configuration was successfully rolled back.\n\n");
1731 /* Print warnings (if any). */
1732 if (strlen(errmsg
) > 0)
1733 vty_out(vty
, "%s\n", errmsg
);
1735 case NB_ERR_NO_CHANGES
:
1737 "%% Aborting - no configuration changes detected.\n\n");
1740 vty_out(vty
, "%% Rollback failed.\n\n");
1741 vty_show_nb_errors(vty
, ret
, errmsg
);
1745 #endif /* HAVE_CONFIG_ROLLBACKS */
1747 DEFPY (rollback_config
,
1748 rollback_config_cmd
,
1749 "rollback configuration (1-4294967295)$transaction_id",
1750 "Rollback to a previous state\n"
1751 "Running configuration\n"
1754 #ifdef HAVE_CONFIG_ROLLBACKS
1755 return nb_cli_rollback_configuration(vty
, transaction_id
);
1758 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1760 #endif /* HAVE_CONFIG_ROLLBACKS */
1763 /* Debug CLI commands. */
1764 static struct debug
*nb_debugs
[] = {
1765 &nb_dbg_cbs_config
, &nb_dbg_cbs_state
, &nb_dbg_cbs_rpc
,
1766 &nb_dbg_notif
, &nb_dbg_events
, &nb_dbg_libyang
,
1769 static const char *const nb_debugs_conflines
[] = {
1770 "debug northbound callbacks configuration",
1771 "debug northbound callbacks state",
1772 "debug northbound callbacks rpc",
1773 "debug northbound notifications",
1774 "debug northbound events",
1775 "debug northbound libyang",
1778 DEFINE_HOOK(nb_client_debug_set_all
, (uint32_t flags
, bool set
), (flags
, set
));
1780 static void nb_debug_set_all(uint32_t flags
, bool set
)
1782 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++) {
1783 DEBUG_FLAGS_SET(nb_debugs
[i
], flags
, set
);
1785 /* If all modes have been turned off, don't preserve options. */
1786 if (!DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_ALL
))
1787 DEBUG_CLEAR(nb_debugs
[i
]);
1790 hook_call(nb_client_debug_set_all
, flags
, set
);
1795 "[no] debug northbound\
1797 callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\
1798 |notifications$notifications\
1804 "Northbound debugging\n"
1811 "libyang debugging\n")
1813 uint32_t mode
= DEBUG_NODE2MODE(vty
->node
);
1816 bool none
= (!cbs_cfg
&& !cbs_state
&& !cbs_rpc
);
1818 if (none
|| cbs_cfg
)
1819 DEBUG_MODE_SET(&nb_dbg_cbs_config
, mode
, !no
);
1820 if (none
|| cbs_state
)
1821 DEBUG_MODE_SET(&nb_dbg_cbs_state
, mode
, !no
);
1822 if (none
|| cbs_rpc
)
1823 DEBUG_MODE_SET(&nb_dbg_cbs_rpc
, mode
, !no
);
1826 DEBUG_MODE_SET(&nb_dbg_notif
, mode
, !no
);
1828 DEBUG_MODE_SET(&nb_dbg_events
, mode
, !no
);
1830 DEBUG_MODE_SET(&nb_dbg_libyang
, mode
, !no
);
1831 yang_debugging_set(!no
);
1834 /* no specific debug --> act on all of them */
1835 if (strmatch(argv
[argc
- 1]->text
, "northbound")) {
1836 nb_debug_set_all(mode
, !no
);
1837 yang_debugging_set(!no
);
1843 DEFINE_HOOK(nb_client_debug_config_write
, (struct vty
*vty
), (vty
));
1845 static int nb_debug_config_write(struct vty
*vty
)
1847 for (unsigned int i
= 0; i
< array_size(nb_debugs
); i
++)
1848 if (DEBUG_MODE_CHECK(nb_debugs
[i
], DEBUG_MODE_CONF
))
1849 vty_out(vty
, "%s\n", nb_debugs_conflines
[i
]);
1851 hook_call(nb_client_debug_config_write
, vty
);
1856 static struct debug_callbacks nb_dbg_cbs
= {.debug_set_all
= nb_debug_set_all
};
1857 static struct cmd_node nb_debug_node
= {
1858 .name
= "northbound debug",
1859 .node
= NORTHBOUND_DEBUG_NODE
,
1861 .config_write
= nb_debug_config_write
,
1864 void nb_cli_install_default(int node
)
1866 _install_element(node
, &show_config_candidate_section_cmd
);
1868 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL
)
1871 _install_element(node
, &config_commit_cmd
);
1872 _install_element(node
, &config_commit_comment_cmd
);
1873 _install_element(node
, &config_commit_check_cmd
);
1874 _install_element(node
, &config_update_cmd
);
1875 _install_element(node
, &config_discard_cmd
);
1876 _install_element(node
, &show_config_running_cmd
);
1877 _install_element(node
, &show_config_candidate_cmd
);
1878 _install_element(node
, &show_config_compare_cmd
);
1879 _install_element(node
, &show_config_transaction_cmd
);
1882 /* YANG module autocomplete. */
1883 static void yang_module_autocomplete(vector comps
, struct cmd_token
*token
)
1885 const struct lys_module
*module
;
1886 struct yang_translator
*module_tr
;
1890 while ((module
= ly_ctx_get_module_iter(ly_native_ctx
, &idx
)))
1891 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1893 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
) {
1895 while ((module
= ly_ctx_get_module_iter(module_tr
->ly_ctx
,
1898 XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1902 /* YANG module translator autocomplete. */
1903 static void yang_translator_autocomplete(vector comps
, struct cmd_token
*token
)
1905 struct yang_translator
*module_tr
;
1907 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
)
1908 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module_tr
->family
));
1911 static const struct cmd_variable_handler yang_var_handlers
[] = {
1912 {.varname
= "module_name", .completions
= yang_module_autocomplete
},
1913 {.varname
= "translator_family",
1914 .completions
= yang_translator_autocomplete
},
1915 {.completions
= NULL
}};
1917 void nb_cli_init(struct thread_master
*tm
)
1921 /* Initialize the shared candidate configuration. */
1922 vty_shared_candidate_config
= nb_config_new(NULL
);
1924 debug_init(&nb_dbg_cbs
);
1926 install_node(&nb_debug_node
);
1927 install_element(ENABLE_NODE
, &debug_nb_cmd
);
1928 install_element(CONFIG_NODE
, &debug_nb_cmd
);
1930 /* Install commands specific to the transaction-base mode. */
1931 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL
) {
1932 install_element(ENABLE_NODE
, &config_exclusive_cmd
);
1933 install_element(ENABLE_NODE
, &config_private_cmd
);
1934 install_element(ENABLE_NODE
,
1935 &show_config_compare_without_candidate_cmd
);
1936 install_element(ENABLE_NODE
, &show_config_transaction_cmd
);
1937 install_element(ENABLE_NODE
, &rollback_config_cmd
);
1938 install_element(ENABLE_NODE
, &clear_config_transactions_cmd
);
1940 install_element(CONFIG_NODE
, &config_load_cmd
);
1941 install_element(CONFIG_NODE
,
1942 &config_database_max_transactions_cmd
);
1945 /* Other commands. */
1946 install_element(ENABLE_NODE
, &show_config_running_cmd
);
1947 install_element(CONFIG_NODE
, &yang_module_translator_load_cmd
);
1948 install_element(CONFIG_NODE
, &yang_module_translator_unload_cmd
);
1949 install_element(ENABLE_NODE
, &show_yang_operational_data_cmd
);
1950 install_element(ENABLE_NODE
, &show_yang_module_cmd
);
1951 install_element(ENABLE_NODE
, &show_yang_module_detail_cmd
);
1952 install_element(ENABLE_NODE
, &show_yang_module_translator_cmd
);
1953 cmd_variable_handler_register(yang_var_handlers
);
1956 void nb_cli_terminate(void)
1958 nb_config_free(vty_shared_candidate_config
);