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
25 #include "lib_errors.h"
27 #include "termtable.h"
29 #include "yang_translator.h"
30 #include "northbound.h"
31 #include "northbound_cli.h"
32 #include "northbound_db.h"
33 #ifndef VTYSH_EXTRACT_PL
34 #include "lib/northbound_cli_clippy.c"
38 struct nb_config
*vty_shared_candidate_config
;
40 static void vty_show_libyang_errors(struct vty
*vty
, struct ly_ctx
*ly_ctx
)
42 struct ly_err_item
*ei
;
45 ei
= ly_err_first(ly_ctx
);
49 for (; ei
; ei
= ei
->next
)
50 vty_out(vty
, "%s\n", ei
->msg
);
52 path
= ly_errpath(ly_ctx
);
54 vty_out(vty
, "YANG path: %s\n", path
);
56 ly_err_clean(ly_ctx
, NULL
);
59 void nb_cli_enqueue_change(struct vty
*vty
, const char *xpath
,
60 enum nb_operation operation
, const char *value
)
62 struct vty_cfg_change
*change
;
64 if (vty
->num_cfg_changes
== VTY_MAXCFGCHANGES
) {
65 /* Not expected to happen. */
67 "%% Exceeded the maximum number of changes (%u) for a single command\n\n",
72 change
= &vty
->cfg_changes
[vty
->num_cfg_changes
++];
73 change
->xpath
= xpath
;
74 change
->operation
= operation
;
75 change
->value
= value
;
78 int nb_cli_apply_changes(struct vty
*vty
, const char *xpath_base_fmt
, ...)
80 struct nb_config
*candidate_transitory
;
81 char xpath_base
[XPATH_MAXLEN
];
89 * Create a copy of the candidate configuration. For consistency, we
90 * need to ensure that either all changes made by the command are
91 * accepted or none are.
93 candidate_transitory
= nb_config_dup(vty
->candidate_config
);
95 /* Parse the base XPath format string. */
96 va_start(ap
, xpath_base_fmt
);
97 vsnprintf(xpath_base
, sizeof(xpath_base
), xpath_base_fmt
, ap
);
100 /* Edit candidate configuration. */
101 for (size_t i
= 0; i
< vty
->num_cfg_changes
; i
++) {
102 struct vty_cfg_change
*change
= &vty
->cfg_changes
[i
];
103 struct nb_node
*nb_node
;
104 char xpath
[XPATH_MAXLEN
];
105 struct yang_data
*data
;
107 /* Handle relative XPaths. */
108 memset(xpath
, 0, sizeof(xpath
));
109 if (vty
->xpath_index
> 0
110 && ((xpath_base_fmt
&& xpath_base
[0] == '.')
111 || change
->xpath
[0] == '.'))
112 strlcpy(xpath
, VTY_CURR_XPATH
, sizeof(xpath
));
113 if (xpath_base_fmt
) {
114 if (xpath_base
[0] == '.')
115 strlcat(xpath
, xpath_base
+ 1, sizeof(xpath
));
117 strlcat(xpath
, xpath_base
, sizeof(xpath
));
119 if (change
->xpath
[0] == '.')
120 strlcat(xpath
, change
->xpath
+ 1, sizeof(xpath
));
122 strlcpy(xpath
, change
->xpath
, sizeof(xpath
));
124 /* Find the northbound node associated to the data path. */
125 nb_node
= nb_node_find(xpath
);
127 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
128 "%s: unknown data path: %s", __func__
, xpath
);
133 /* If the value is not set, get the default if it exists. */
134 if (change
->value
== NULL
)
135 change
->value
= yang_snode_get_default(nb_node
->snode
);
136 data
= yang_data_new(xpath
, change
->value
);
139 * Ignore "not found" errors when editing the candidate
142 ret
= nb_candidate_edit(candidate_transitory
, nb_node
,
143 change
->operation
, xpath
, NULL
, data
);
144 yang_data_free(data
);
145 if (ret
!= NB_OK
&& ret
!= NB_ERR_NOT_FOUND
) {
147 EC_LIB_NB_CANDIDATE_EDIT_ERROR
,
148 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
149 __func__
, nb_operation_name(change
->operation
),
157 nb_config_free(candidate_transitory
);
159 switch (frr_get_cli_mode()) {
160 case FRR_CLI_CLASSIC
:
161 vty_out(vty
, "%% Configuration failed.\n\n");
163 case FRR_CLI_TRANSACTIONAL
:
165 "%% Failed to edit candidate configuration.\n\n");
168 vty_show_libyang_errors(vty
, ly_native_ctx
);
170 return CMD_WARNING_CONFIG_FAILED
;
173 nb_config_replace(vty
->candidate_config
, candidate_transitory
, false);
175 /* Do an implicit "commit" when using the classic CLI mode. */
176 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
) {
177 ret
= nb_candidate_commit(vty
->candidate_config
, NB_CLIENT_CLI
,
179 if (ret
!= NB_OK
&& ret
!= NB_ERR_NO_CHANGES
) {
180 vty_out(vty
, "%% Configuration failed: %s.\n\n",
183 "Please check the logs for more details.\n");
185 /* Regenerate candidate for consistency. */
186 nb_config_replace(vty
->candidate_config
, running_config
,
188 return CMD_WARNING_CONFIG_FAILED
;
195 int nb_cli_rpc(const char *xpath
, struct list
*input
, struct list
*output
)
197 struct nb_node
*nb_node
;
200 nb_node
= nb_node_find(xpath
);
202 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
203 "%s: unknown data path: %s", __func__
, xpath
);
207 ret
= nb_node
->cbs
.rpc(xpath
, input
, output
);
216 static int nb_cli_commit(struct vty
*vty
, bool force
, char *comment
)
218 uint32_t transaction_id
;
221 if (vty_exclusive_lock
!= NULL
&& vty_exclusive_lock
!= vty
) {
222 vty_out(vty
, "%% Configuration is locked by another VTY.\n\n");
226 if (!force
&& nb_candidate_needs_update(vty
->candidate_config
)) {
228 "%% Candidate configuration needs to be updated before commit.\n\n");
230 "Use the \"update\" command or \"commit force\".\n");
234 ret
= nb_candidate_commit(vty
->candidate_config
, NB_CLIENT_CLI
, true,
235 comment
, &transaction_id
);
237 /* Map northbound return code to CLI return code. */
240 nb_config_replace(vty
->candidate_config_base
, running_config
,
243 "%% Configuration committed successfully (Transaction ID #%u).\n\n",
246 case NB_ERR_NO_CHANGES
:
247 vty_out(vty
, "%% No configuration changes to commit.\n\n");
251 "%% Failed to commit candidate configuration: %s.\n\n",
253 vty_out(vty
, "Please check the logs for more details.\n");
258 static int nb_cli_candidate_load_file(struct vty
*vty
,
259 enum nb_cfg_format format
,
260 struct yang_translator
*translator
,
261 const char *path
, bool replace
)
263 struct nb_config
*loaded_config
= NULL
;
264 struct lyd_node
*dnode
;
265 struct ly_ctx
*ly_ctx
;
269 case NB_CFG_FMT_CMDS
:
270 loaded_config
= nb_config_new(NULL
);
271 if (!vty_read_config(loaded_config
, path
, config_default
)) {
272 vty_out(vty
, "%% Failed to load configuration.\n\n");
274 "Please check the logs for more details.\n");
275 nb_config_free(loaded_config
);
279 case NB_CFG_FMT_JSON
:
281 ly_format
= (format
== NB_CFG_FMT_JSON
) ? LYD_JSON
: LYD_XML
;
283 ly_ctx
= translator
? translator
->ly_ctx
: ly_native_ctx
;
284 dnode
= lyd_parse_path(ly_ctx
, path
, ly_format
, LYD_OPT_EDIT
);
286 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_path() failed",
288 vty_out(vty
, "%% Failed to load configuration:\n\n");
289 vty_show_libyang_errors(vty
, ly_ctx
);
293 && yang_translate_dnode(translator
,
294 YANG_TRANSLATE_TO_NATIVE
, &dnode
)
295 != YANG_TRANSLATE_SUCCESS
) {
296 vty_out(vty
, "%% Failed to translate configuration\n");
297 yang_dnode_free(dnode
);
300 loaded_config
= nb_config_new(dnode
);
305 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
306 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
309 "%% Failed to merge the loaded configuration:\n\n");
310 vty_show_libyang_errors(vty
, ly_native_ctx
);
317 static int nb_cli_candidate_load_transaction(struct vty
*vty
,
318 uint32_t transaction_id
,
321 struct nb_config
*loaded_config
;
323 loaded_config
= nb_db_transaction_load(transaction_id
);
324 if (!loaded_config
) {
325 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
331 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
332 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
335 "%% Failed to merge the loaded configuration:\n\n");
336 vty_show_libyang_errors(vty
, ly_native_ctx
);
343 void nb_cli_show_dnode_cmds(struct vty
*vty
, struct lyd_node
*root
,
346 struct lyd_node
*next
, *child
;
348 LY_TREE_DFS_BEGIN (root
, next
, child
) {
349 struct nb_node
*nb_node
;
351 nb_node
= child
->schema
->priv
;
352 if (!nb_node
->cbs
.cli_show
)
355 /* Skip default values. */
356 if (!with_defaults
&& yang_dnode_is_default_recursive(child
))
359 (*nb_node
->cbs
.cli_show
)(vty
, child
, with_defaults
);
361 LY_TREE_DFS_END(root
, next
, child
);
365 static void nb_cli_show_config_cmds(struct vty
*vty
, struct nb_config
*config
,
368 struct lyd_node
*root
;
370 vty_out(vty
, "Configuration:\n");
372 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
373 vty_out(vty
, "frr defaults %s\n", DFLT_NAME
);
375 LY_TREE_FOR (config
->dnode
, root
)
376 nb_cli_show_dnode_cmds(vty
, root
, with_defaults
);
379 vty_out(vty
, "end\n");
382 static int nb_cli_show_config_libyang(struct vty
*vty
, LYD_FORMAT format
,
383 struct nb_config
*config
,
384 struct yang_translator
*translator
,
387 struct lyd_node
*dnode
;
391 dnode
= yang_dnode_dup(config
->dnode
);
393 && yang_translate_dnode(translator
, YANG_TRANSLATE_FROM_NATIVE
,
395 != YANG_TRANSLATE_SUCCESS
) {
396 vty_out(vty
, "%% Failed to translate configuration\n");
397 yang_dnode_free(dnode
);
401 options
= LYP_FORMAT
| LYP_WITHSIBLINGS
;
403 options
|= LYP_WD_ALL
;
405 options
|= LYP_WD_TRIM
;
407 if (lyd_print_mem(&strp
, dnode
, format
, options
) == 0 && strp
) {
408 vty_out(vty
, "%s", strp
);
412 yang_dnode_free(dnode
);
417 static int nb_cli_show_config(struct vty
*vty
, struct nb_config
*config
,
418 enum nb_cfg_format format
,
419 struct yang_translator
*translator
,
423 case NB_CFG_FMT_CMDS
:
424 nb_cli_show_config_cmds(vty
, config
, with_defaults
);
426 case NB_CFG_FMT_JSON
:
427 return nb_cli_show_config_libyang(vty
, LYD_JSON
, config
,
428 translator
, with_defaults
);
430 return nb_cli_show_config_libyang(vty
, LYD_XML
, config
,
431 translator
, with_defaults
);
437 static int nb_write_config(struct nb_config
*config
, enum nb_cfg_format format
,
438 struct yang_translator
*translator
, char *path
,
442 struct vty
*file_vty
;
445 snprintf(path
, pathlen
, "/tmp/frr.tmp.XXXXXXXX");
448 flog_warn(EC_LIB_SYSTEM_CALL
, "%s: mkstemp() failed: %s",
449 __func__
, safe_strerror(errno
));
453 /* Make vty for configuration file. */
454 file_vty
= vty_new();
456 file_vty
->type
= VTY_FILE
;
458 ret
= nb_cli_show_config(file_vty
, config
, format
, translator
,
465 static int nb_cli_show_config_compare(struct vty
*vty
,
466 struct nb_config
*config1
,
467 struct nb_config
*config2
,
468 enum nb_cfg_format format
,
469 struct yang_translator
*translator
)
471 char config1_path
[256];
472 char config2_path
[256];
473 char command
[BUFSIZ
];
478 if (nb_write_config(config1
, format
, translator
, config1_path
,
479 sizeof(config1_path
))
481 vty_out(vty
, "%% Failed to process configurations.\n\n");
484 if (nb_write_config(config2
, format
, translator
, config2_path
,
485 sizeof(config2_path
))
487 vty_out(vty
, "%% Failed to process configurations.\n\n");
488 unlink(config1_path
);
492 snprintf(command
, sizeof(command
), "diff -u %s %s", config1_path
,
494 fp
= popen(command
, "r");
496 vty_out(vty
, "%% Failed to generate configuration diff.\n\n");
497 unlink(config1_path
);
498 unlink(config2_path
);
501 /* Print diff line by line. */
502 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
505 vty_out(vty
, "%s", line
);
509 unlink(config1_path
);
510 unlink(config2_path
);
515 /* Configure exclusively from this terminal. */
516 DEFUN (config_exclusive
,
517 config_exclusive_cmd
,
518 "configure exclusive",
519 "Configuration from vty interface\n"
520 "Configure exclusively from this terminal\n")
522 if (vty_config_exclusive_lock(vty
))
523 vty
->node
= CONFIG_NODE
;
525 vty_out(vty
, "VTY configuration is locked by other VTY\n");
526 return CMD_WARNING_CONFIG_FAILED
;
529 vty
->private_config
= true;
530 vty
->candidate_config
= nb_config_dup(running_config
);
531 vty
->candidate_config_base
= nb_config_dup(running_config
);
533 "Warning: uncommitted changes will be discarded on exit.\n\n");
538 /* Configure using a private candidate configuration. */
539 DEFUN (config_private
,
542 "Configuration from vty interface\n"
543 "Configure using a private candidate configuration\n")
545 if (vty_config_lock(vty
))
546 vty
->node
= CONFIG_NODE
;
548 vty_out(vty
, "VTY configuration is locked by other VTY\n");
549 return CMD_WARNING_CONFIG_FAILED
;
552 vty
->private_config
= true;
553 vty
->candidate_config
= nb_config_dup(running_config
);
554 vty
->candidate_config_base
= nb_config_dup(running_config
);
556 "Warning: uncommitted changes will be discarded on exit.\n\n");
561 DEFPY (config_commit
,
563 "commit [force$force]",
564 "Commit changes into the running configuration\n"
565 "Force commit even if the candidate is outdated\n")
567 return nb_cli_commit(vty
, !!force
, NULL
);
570 DEFPY (config_commit_comment
,
571 config_commit_comment_cmd
,
572 "commit [force$force] comment LINE...",
573 "Commit changes into the running configuration\n"
574 "Force commit even if the candidate is outdated\n"
575 "Assign a comment to this commit\n"
576 "Comment for this commit (Max 80 characters)\n")
582 argv_find(argv
, argc
, "LINE", &idx
);
583 comment
= argv_concat(argv
, argc
, idx
);
584 ret
= nb_cli_commit(vty
, !!force
, comment
);
585 XFREE(MTYPE_TMP
, comment
);
590 DEFPY (config_commit_check
,
591 config_commit_check_cmd
,
593 "Commit changes into the running configuration\n"
594 "Check if the configuration changes are valid\n")
598 ret
= nb_candidate_validate(vty
->candidate_config
);
601 "%% Failed to validate candidate configuration.\n\n");
602 vty_show_libyang_errors(vty
, ly_native_ctx
);
606 vty_out(vty
, "%% Candidate configuration validated successfully.\n\n");
611 DEFPY (config_update
,
614 "Update candidate configuration\n")
616 if (!nb_candidate_needs_update(vty
->candidate_config
)) {
617 vty_out(vty
, "%% Update is not necessary.\n\n");
621 if (nb_candidate_update(vty
->candidate_config
) != NB_OK
) {
623 "%% Failed to update the candidate configuration.\n\n");
624 vty_out(vty
, "Please check the logs for more details.\n");
628 nb_config_replace(vty
->candidate_config_base
, running_config
, true);
630 vty_out(vty
, "%% Candidate configuration updated successfully.\n\n");
635 DEFPY (config_discard
,
638 "Discard changes in the candidate configuration\n")
640 nb_config_replace(vty
->candidate_config
, vty
->candidate_config_base
,
650 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
651 |transaction (1-4294967296)$tid\
654 "Configuration related settings\n"
655 "Load configuration into candidate\n"
656 "Load configuration file into candidate\n"
657 "Load configuration file in JSON format\n"
658 "Load configuration file in XML format\n"
659 "Translate configuration file\n"
660 "YANG module translator\n"
661 "Configuration file name (full path)\n"
662 "Load configuration from transaction into candidate\n"
664 "Replace instead of merge\n")
667 enum nb_cfg_format format
;
668 struct yang_translator
*translator
= NULL
;
671 format
= NB_CFG_FMT_JSON
;
673 format
= NB_CFG_FMT_XML
;
675 format
= NB_CFG_FMT_CMDS
;
677 if (translator_family
) {
678 translator
= yang_translator_find(translator_family
);
681 "%% Module translator \"%s\" not found\n",
687 return nb_cli_candidate_load_file(vty
, format
, translator
,
688 filename
, !!replace
);
691 return nb_cli_candidate_load_transaction(vty
, tid
, !!replace
);
694 DEFPY (show_config_running
,
695 show_config_running_cmd
,
696 "show configuration running\
697 [<json$json|xml$xml> [translate WORD$translator_family]]\
698 [with-defaults$with_defaults]",
700 "Configuration information\n"
701 "Running configuration\n"
702 "Change output format to JSON\n"
703 "Change output format to XML\n"
705 "YANG module translator\n"
706 "Show default values\n")
709 enum nb_cfg_format format
;
710 struct yang_translator
*translator
= NULL
;
713 format
= NB_CFG_FMT_JSON
;
715 format
= NB_CFG_FMT_XML
;
717 format
= NB_CFG_FMT_CMDS
;
719 if (translator_family
) {
720 translator
= yang_translator_find(translator_family
);
722 vty_out(vty
, "%% Module translator \"%s\" not found\n",
728 nb_cli_show_config(vty
, running_config
, format
, translator
,
734 DEFPY (show_config_candidate
,
735 show_config_candidate_cmd
,
736 "show configuration candidate\
737 [<json$json|xml$xml> [translate WORD$translator_family]]\
739 with-defaults$with_defaults\
743 "Configuration information\n"
744 "Candidate configuration\n"
745 "Change output format to JSON\n"
746 "Change output format to XML\n"
748 "YANG module translator\n"
749 "Show default values\n"
750 "Show changes applied in the candidate configuration\n")
753 enum nb_cfg_format format
;
754 struct yang_translator
*translator
= NULL
;
757 format
= NB_CFG_FMT_JSON
;
759 format
= NB_CFG_FMT_XML
;
761 format
= NB_CFG_FMT_CMDS
;
763 if (translator_family
) {
764 translator
= yang_translator_find(translator_family
);
766 vty_out(vty
, "%% Module translator \"%s\" not found\n",
773 return nb_cli_show_config_compare(
774 vty
, vty
->candidate_config_base
, vty
->candidate_config
,
777 nb_cli_show_config(vty
, vty
->candidate_config
, format
, translator
,
783 DEFPY (show_config_compare
,
784 show_config_compare_cmd
,
785 "show configuration compare\
787 candidate$c1_candidate\
789 |transaction (1-4294967296)$c1_tid\
792 candidate$c2_candidate\
794 |transaction (1-4294967296)$c2_tid\
796 [<json$json|xml$xml> [translate WORD$translator_family]]",
798 "Configuration information\n"
799 "Compare two different configurations\n"
800 "Candidate configuration\n"
801 "Running configuration\n"
802 "Configuration transaction\n"
804 "Candidate configuration\n"
805 "Running configuration\n"
806 "Configuration transaction\n"
808 "Change output format to JSON\n"
809 "Change output format to XML\n"
811 "YANG module translator\n")
813 enum nb_cfg_format format
;
814 struct yang_translator
*translator
= NULL
;
815 struct nb_config
*config1
, *config_transaction1
= NULL
;
816 struct nb_config
*config2
, *config_transaction2
= NULL
;
817 int ret
= CMD_WARNING
;
820 config1
= vty
->candidate_config
;
822 config1
= running_config
;
824 config_transaction1
= nb_db_transaction_load(c1_tid
);
825 if (!config_transaction1
) {
826 vty_out(vty
, "%% Transaction %u does not exist\n\n",
827 (unsigned int)c1_tid
);
830 config1
= config_transaction1
;
834 config2
= vty
->candidate_config
;
836 config2
= running_config
;
838 config_transaction2
= nb_db_transaction_load(c2_tid
);
839 if (!config_transaction2
) {
840 vty_out(vty
, "%% Transaction %u does not exist\n\n",
841 (unsigned int)c2_tid
);
844 config2
= config_transaction2
;
848 format
= NB_CFG_FMT_JSON
;
850 format
= NB_CFG_FMT_XML
;
852 format
= NB_CFG_FMT_CMDS
;
854 if (translator_family
) {
855 translator
= yang_translator_find(translator_family
);
857 vty_out(vty
, "%% Module translator \"%s\" not found\n",
863 ret
= nb_cli_show_config_compare(vty
, config1
, config2
, format
,
866 if (config_transaction1
)
867 nb_config_free(config_transaction1
);
868 if (config_transaction2
)
869 nb_config_free(config_transaction2
);
875 * Stripped down version of the "show configuration compare" command.
876 * The "candidate" option is not present so the command can be installed in
879 ALIAS (show_config_compare
,
880 show_config_compare_without_candidate_cmd
,
881 "show configuration compare\
884 |transaction (1-4294967296)$c1_tid\
888 |transaction (1-4294967296)$c2_tid\
890 [<json$json|xml$xml> [translate WORD$translator_family]]",
892 "Configuration information\n"
893 "Compare two different configurations\n"
894 "Running configuration\n"
895 "Configuration transaction\n"
897 "Running configuration\n"
898 "Configuration transaction\n"
900 "Change output format to JSON\n"
901 "Change output format to XML\n"
903 "YANG module translator\n")
905 DEFPY (clear_config_transactions
,
906 clear_config_transactions_cmd
,
907 "clear configuration transactions oldest (1-100)$n",
909 "Configuration activity\n"
910 "Delete transactions from the transactions log\n"
911 "Delete oldest <n> transactions\n"
912 "Number of transactions to delete\n")
914 #ifdef HAVE_CONFIG_ROLLBACKS
915 if (nb_db_clear_transactions(n
) != NB_OK
) {
916 vty_out(vty
, "%% Failed to delete transactions.\n\n");
921 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
922 #endif /* HAVE_CONFIG_ROLLBACKS */
927 DEFPY (config_database_max_transactions
,
928 config_database_max_transactions_cmd
,
929 "configuration database max-transactions (1-100)$max",
930 "Configuration related settings\n"
931 "Configuration database\n"
932 "Set the maximum number of transactions to store\n"
933 "Number of transactions\n")
935 #ifdef HAVE_CONFIG_ROLLBACKS
936 if (nb_db_set_max_transactions(max
) != NB_OK
) {
938 "%% Failed to update the maximum number of transactions.\n\n");
942 "%% Maximum number of transactions updated successfully.\n\n");
945 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
946 #endif /* HAVE_CONFIG_ROLLBACKS */
951 DEFPY (yang_module_translator_load
,
952 yang_module_translator_load_cmd
,
953 "yang module-translator load FILENAME$filename",
954 "YANG related settings\n"
955 "YANG module translator\n"
956 "Load YANG module translator\n"
957 "File name (full path)\n")
959 struct yang_translator
*translator
;
961 translator
= yang_translator_load(filename
);
963 vty_out(vty
, "%% Failed to load \"%s\"\n\n", filename
);
964 vty_out(vty
, "Please check the logs for more details.\n");
968 vty_out(vty
, "%% Module translator \"%s\" loaded successfully.\n\n",
974 DEFPY (yang_module_translator_unload_family
,
975 yang_module_translator_unload_cmd
,
976 "yang module-translator unload WORD$translator_family",
977 "YANG related settings\n"
978 "YANG module translator\n"
979 "Unload YANG module translator\n"
980 "Name of the module translator\n")
982 struct yang_translator
*translator
;
984 translator
= yang_translator_find(translator_family
);
986 vty_out(vty
, "%% Module translator \"%s\" not found\n",
991 yang_translator_unload(translator
);
996 #ifdef HAVE_CONFIG_ROLLBACKS
997 static void nb_cli_show_transactions_cb(void *arg
, int transaction_id
,
998 const char *client_name
,
999 const char *date
, const char *comment
)
1001 struct ttable
*tt
= arg
;
1003 ttable_add_row(tt
, "%d|%s|%s|%s", transaction_id
, client_name
, date
,
1007 static int nb_cli_show_transactions(struct vty
*vty
)
1011 /* Prepare table. */
1012 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1013 ttable_add_row(tt
, "Transaction ID|Client|Date|Comment");
1014 tt
->style
.cell
.rpad
= 2;
1015 tt
->style
.corner
= '+';
1017 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1019 /* Fetch transactions from the northbound database. */
1020 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb
, tt
)
1023 "%% Failed to fetch configuration transactions.\n");
1027 /* Dump the generated table. */
1028 if (tt
->nrows
> 1) {
1031 table
= ttable_dump(tt
, "\n");
1032 vty_out(vty
, "%s\n", table
);
1033 XFREE(MTYPE_TMP
, table
);
1035 vty_out(vty
, "No configuration transactions to display.\n\n");
1041 #endif /* HAVE_CONFIG_ROLLBACKS */
1043 DEFPY (show_config_transaction
,
1044 show_config_transaction_cmd
,
1045 "show configuration transaction\
1047 (1-4294967296)$transaction_id\
1048 [<json$json|xml$xml> [translate WORD$translator_family]]\
1050 with-defaults$with_defaults\
1055 "Configuration information\n"
1056 "Configuration transaction\n"
1058 "Change output format to JSON\n"
1059 "Change output format to XML\n"
1060 "Translate output\n"
1061 "YANG module translator\n"
1062 "Show default values\n"
1063 "Show changes compared to the previous transaction\n")
1065 #ifdef HAVE_CONFIG_ROLLBACKS
1066 if (transaction_id
) {
1067 struct nb_config
*config
;
1068 enum nb_cfg_format format
;
1069 struct yang_translator
*translator
= NULL
;
1072 format
= NB_CFG_FMT_JSON
;
1074 format
= NB_CFG_FMT_XML
;
1076 format
= NB_CFG_FMT_CMDS
;
1078 if (translator_family
) {
1079 translator
= yang_translator_find(translator_family
);
1082 "%% Module translator \"%s\" not found\n",
1088 config
= nb_db_transaction_load(transaction_id
);
1090 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1091 (unsigned int)transaction_id
);
1096 struct nb_config
*prev_config
;
1099 /* NOTE: this can be NULL. */
1101 nb_db_transaction_load(transaction_id
- 1);
1103 ret
= nb_cli_show_config_compare(
1104 vty
, prev_config
, config
, format
, translator
);
1106 nb_config_free(prev_config
);
1107 nb_config_free(config
);
1112 nb_cli_show_config(vty
, config
, format
, translator
,
1114 nb_config_free(config
);
1119 return nb_cli_show_transactions(vty
);
1122 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1124 #endif /* HAVE_CONFIG_ROLLBACKS */
1127 DEFPY (show_yang_module
,
1128 show_yang_module_cmd
,
1129 "show yang module [module-translator WORD$translator_family]",
1131 "YANG information\n"
1132 "Show loaded modules\n"
1133 "YANG module translator\n"
1134 "YANG module translator\n")
1136 struct ly_ctx
*ly_ctx
;
1137 struct yang_translator
*translator
= NULL
;
1138 const struct lys_module
*module
;
1142 if (translator_family
) {
1143 translator
= yang_translator_find(translator_family
);
1145 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1149 ly_ctx
= translator
->ly_ctx
;
1151 ly_ctx
= ly_native_ctx
;
1153 /* Prepare table. */
1154 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1155 ttable_add_row(tt
, "Module|Version|Revision|Flags|Namespace");
1156 tt
->style
.cell
.rpad
= 2;
1157 tt
->style
.corner
= '+';
1159 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1161 while ((module
= ly_ctx_get_module_iter(ly_ctx
, &idx
))) {
1164 snprintf(flags
, sizeof(flags
), "%c%c",
1165 module
->implemented
? 'I' : ' ',
1166 (module
->deviated
== 1) ? 'D' : ' ');
1168 ttable_add_row(tt
, "%s|%s|%s|%s|%s", module
->name
,
1169 (module
->version
== 2) ? "1.1" : "1.0",
1170 (module
->rev_size
> 0) ? module
->rev
[0].date
1175 /* Dump the generated table. */
1176 if (tt
->nrows
> 1) {
1179 vty_out(vty
, " Flags: I - Implemented, D - Deviated\n\n");
1181 table
= ttable_dump(tt
, "\n");
1182 vty_out(vty
, "%s\n", table
);
1183 XFREE(MTYPE_TMP
, table
);
1185 vty_out(vty
, "No YANG modules to display.\n\n");
1192 DEFPY (show_yang_module_detail
,
1193 show_yang_module_detail_cmd
,
1195 [module-translator WORD$translator_family]\
1196 WORD$module_name <summary|tree$tree|yang$yang|yin$yin>",
1198 "YANG information\n"
1199 "Show loaded modules\n"
1200 "YANG module translator\n"
1201 "YANG module translator\n"
1203 "Display summary information about the module\n"
1204 "Display module in the tree (RFC 8340) format\n"
1205 "Display module in the YANG format\n"
1206 "Display module in the YIN format\n")
1208 struct ly_ctx
*ly_ctx
;
1209 struct yang_translator
*translator
= NULL
;
1210 const struct lys_module
*module
;
1211 LYS_OUTFORMAT format
;
1214 if (translator_family
) {
1215 translator
= yang_translator_find(translator_family
);
1217 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1221 ly_ctx
= translator
->ly_ctx
;
1223 ly_ctx
= ly_native_ctx
;
1225 module
= ly_ctx_get_module(ly_ctx
, module_name
, NULL
, 0);
1227 vty_out(vty
, "%% Module \"%s\" not found\n", module_name
);
1232 format
= LYS_OUT_YANG
;
1234 format
= LYS_OUT_YIN
;
1236 format
= LYS_OUT_TREE
;
1238 format
= LYS_OUT_INFO
;
1240 if (lys_print_mem(&strp
, module
, format
, NULL
, 0, 0) == 0) {
1241 vty_out(vty
, "%s\n", strp
);
1245 vty_out(vty
, "%% Error generating module information\n");
1252 DEFPY (show_yang_module_translator
,
1253 show_yang_module_translator_cmd
,
1254 "show yang module-translator",
1256 "YANG information\n"
1257 "Show loaded YANG module translators\n")
1259 struct yang_translator
*translator
;
1262 /* Prepare table. */
1263 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1264 ttable_add_row(tt
, "Family|Module|Deviations|Coverage (%%)");
1265 tt
->style
.cell
.rpad
= 2;
1266 tt
->style
.corner
= '+';
1268 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1270 RB_FOREACH (translator
, yang_translators
, &yang_translators
) {
1271 struct yang_tmodule
*tmodule
;
1272 struct listnode
*ln
;
1274 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
1275 ttable_add_row(tt
, "%s|%s|%s|%.2f", translator
->family
,
1276 tmodule
->module
->name
,
1277 tmodule
->deviations
->name
,
1282 /* Dump the generated table. */
1283 if (tt
->nrows
> 1) {
1286 table
= ttable_dump(tt
, "\n");
1287 vty_out(vty
, "%s\n", table
);
1288 XFREE(MTYPE_TMP
, table
);
1290 vty_out(vty
, "No YANG module translators to display.\n\n");
1297 #ifdef HAVE_CONFIG_ROLLBACKS
1298 static int nb_cli_rollback_configuration(struct vty
*vty
,
1299 uint32_t transaction_id
)
1301 struct nb_config
*candidate
;
1305 candidate
= nb_db_transaction_load(transaction_id
);
1307 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1312 snprintf(comment
, sizeof(comment
), "Rollback to transaction %u",
1315 ret
= nb_candidate_commit(candidate
, NB_CLIENT_CLI
, true, comment
,
1317 nb_config_free(candidate
);
1321 "%% Configuration was successfully rolled back.\n\n");
1323 case NB_ERR_NO_CHANGES
:
1325 "%% Aborting - no configuration changes detected.\n\n");
1328 vty_out(vty
, "%% Rollback failed.\n\n");
1329 vty_out(vty
, "Please check the logs for more details.\n");
1333 #endif /* HAVE_CONFIG_ROLLBACKS */
1335 DEFPY (rollback_config
,
1336 rollback_config_cmd
,
1337 "rollback configuration (1-4294967296)$transaction_id",
1338 "Rollback to a previous state\n"
1339 "Running configuration\n"
1342 #ifdef HAVE_CONFIG_ROLLBACKS
1343 return nb_cli_rollback_configuration(vty
, transaction_id
);
1346 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1348 #endif /* HAVE_CONFIG_ROLLBACKS */
1351 /* Debug CLI commands. */
1356 "Northbound Debugging\n")
1358 debug_northbound
= 1;
1365 "no debug northbound",
1367 "Northbound Debugging\n")
1369 debug_northbound
= 0;
1374 static int nb_debug_config_write(struct vty
*vty
)
1376 if (debug_northbound
)
1377 vty_out(vty
, "debug northbound\n");
1382 static struct cmd_node nb_debug_node
= {NORTHBOUND_DEBUG_NODE
, "", 1};
1384 void nb_cli_install_default(int node
)
1386 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL
)
1389 install_element(node
, &config_commit_cmd
);
1390 install_element(node
, &config_commit_comment_cmd
);
1391 install_element(node
, &config_commit_check_cmd
);
1392 install_element(node
, &config_update_cmd
);
1393 install_element(node
, &config_discard_cmd
);
1394 install_element(node
, &show_config_running_cmd
);
1395 install_element(node
, &show_config_candidate_cmd
);
1396 install_element(node
, &show_config_compare_cmd
);
1397 install_element(node
, &show_config_transaction_cmd
);
1400 /* YANG module autocomplete. */
1401 static void yang_module_autocomplete(vector comps
, struct cmd_token
*token
)
1403 const struct lys_module
*module
;
1404 struct yang_translator
*module_tr
;
1408 while ((module
= ly_ctx_get_module_iter(ly_native_ctx
, &idx
)))
1409 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1411 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
) {
1413 while ((module
= ly_ctx_get_module_iter(module_tr
->ly_ctx
,
1416 XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1420 /* YANG module translator autocomplete. */
1421 static void yang_translator_autocomplete(vector comps
, struct cmd_token
*token
)
1423 struct yang_translator
*module_tr
;
1425 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
)
1426 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module_tr
->family
));
1429 static const struct cmd_variable_handler yang_var_handlers
[] = {
1430 {.varname
= "module_name", .completions
= yang_module_autocomplete
},
1431 {.varname
= "translator_family",
1432 .completions
= yang_translator_autocomplete
},
1433 {.completions
= NULL
}};
1435 void nb_cli_init(void)
1437 /* Initialize the shared candidate configuration. */
1438 vty_shared_candidate_config
= nb_config_new(NULL
);
1440 /* Install debug commands */
1441 install_node(&nb_debug_node
, nb_debug_config_write
);
1442 install_element(ENABLE_NODE
, &debug_nb_cmd
);
1443 install_element(ENABLE_NODE
, &no_debug_nb_cmd
);
1444 install_element(CONFIG_NODE
, &debug_nb_cmd
);
1445 install_element(CONFIG_NODE
, &no_debug_nb_cmd
);
1447 /* Install commands specific to the transaction-base mode. */
1448 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL
) {
1449 install_element(ENABLE_NODE
, &config_exclusive_cmd
);
1450 install_element(ENABLE_NODE
, &config_private_cmd
);
1451 install_element(ENABLE_NODE
, &show_config_running_cmd
);
1452 install_element(ENABLE_NODE
,
1453 &show_config_compare_without_candidate_cmd
);
1454 install_element(ENABLE_NODE
, &show_config_transaction_cmd
);
1455 install_element(ENABLE_NODE
, &rollback_config_cmd
);
1456 install_element(ENABLE_NODE
, &clear_config_transactions_cmd
);
1458 install_element(CONFIG_NODE
, &config_load_cmd
);
1459 install_element(CONFIG_NODE
,
1460 &config_database_max_transactions_cmd
);
1463 /* Other commands. */
1464 install_element(CONFIG_NODE
, &yang_module_translator_load_cmd
);
1465 install_element(CONFIG_NODE
, &yang_module_translator_unload_cmd
);
1466 install_element(ENABLE_NODE
, &show_yang_module_cmd
);
1467 install_element(ENABLE_NODE
, &show_yang_module_detail_cmd
);
1468 install_element(ENABLE_NODE
, &show_yang_module_translator_cmd
);
1469 cmd_variable_handler_register(yang_var_handlers
);
1472 void nb_cli_terminate(void)
1474 nb_config_free(vty_shared_candidate_config
);