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 int nb_cli_cfg_change(struct vty
*vty
, char *xpath_base
,
60 struct cli_config_change changes
[], size_t size
)
62 struct nb_config
*candidate_transitory
;
69 * Create a copy of the candidate configuration. For consistency, we
70 * need to ensure that either all changes made by the command are
71 * accepted or none are.
73 candidate_transitory
= nb_config_dup(vty
->candidate_config
);
75 /* Edit candidate configuration. */
76 for (size_t i
= 0; i
< size
; i
++) {
77 struct cli_config_change
*change
= &changes
[i
];
78 struct nb_node
*nb_node
;
79 char xpath
[XPATH_MAXLEN
];
80 struct yang_data
*data
;
82 /* Handle relative XPaths. */
83 memset(xpath
, 0, sizeof(xpath
));
84 if (vty
->xpath_index
> 0
85 && ((xpath_base
&& xpath_base
[0] == '.')
86 || change
->xpath
[0] == '.'))
87 strlcpy(xpath
, VTY_CURR_XPATH
, sizeof(xpath
));
89 if (xpath_base
[0] == '.')
91 strlcat(xpath
, xpath_base
, sizeof(xpath
));
93 if (change
->xpath
[0] == '.')
94 strlcat(xpath
, change
->xpath
+ 1, sizeof(xpath
));
96 strlcpy(xpath
, change
->xpath
, sizeof(xpath
));
98 nb_node
= nb_node_find(xpath
);
100 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
101 "%s: unknown data path: %s", __func__
, xpath
);
106 /* If the value is not set, get the default if it exists. */
107 if (change
->value
== NULL
)
108 change
->value
= yang_snode_get_default(nb_node
->snode
);
109 data
= yang_data_new(xpath
, change
->value
);
112 * Ignore "not found" errors when editing the candidate
115 ret
= nb_candidate_edit(candidate_transitory
, nb_node
,
116 change
->operation
, xpath
, NULL
, data
);
117 yang_data_free(data
);
118 if (ret
!= NB_OK
&& ret
!= NB_ERR_NOT_FOUND
) {
120 EC_LIB_NB_CANDIDATE_EDIT_ERROR
,
121 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
122 __func__
, nb_operation_name(change
->operation
),
130 nb_config_free(candidate_transitory
);
132 switch (frr_get_cli_mode()) {
133 case FRR_CLI_CLASSIC
:
134 vty_out(vty
, "%% Configuration failed.\n\n");
136 case FRR_CLI_TRANSACTIONAL
:
138 "%% Failed to edit candidate configuration.\n\n");
141 vty_show_libyang_errors(vty
, ly_native_ctx
);
143 return CMD_WARNING_CONFIG_FAILED
;
146 nb_config_replace(vty
->candidate_config
, candidate_transitory
, false);
148 /* Do an implicit "commit" when using the classic CLI mode. */
149 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
) {
150 ret
= nb_candidate_commit(vty
->candidate_config
, NB_CLIENT_CLI
,
152 if (ret
!= NB_OK
&& ret
!= NB_ERR_NO_CHANGES
) {
153 vty_out(vty
, "%% Configuration failed: %s.\n\n",
156 "Please check the logs for more details.\n");
158 /* Regenerate candidate for consistency. */
159 nb_config_replace(vty
->candidate_config
, running_config
,
161 return CMD_WARNING_CONFIG_FAILED
;
168 int nb_cli_rpc(const char *xpath
, struct list
*input
, struct list
*output
)
170 struct nb_node
*nb_node
;
173 nb_node
= nb_node_find(xpath
);
175 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
176 "%s: unknown data path: %s", __func__
, xpath
);
180 ret
= nb_node
->cbs
.rpc(xpath
, input
, output
);
189 static int nb_cli_commit(struct vty
*vty
, bool force
, char *comment
)
191 uint32_t transaction_id
;
194 if (vty_exclusive_lock
!= NULL
&& vty_exclusive_lock
!= vty
) {
195 vty_out(vty
, "%% Configuration is locked by another VTY.\n\n");
199 if (!force
&& nb_candidate_needs_update(vty
->candidate_config
)) {
201 "%% Candidate configuration needs to be updated before commit.\n\n");
203 "Use the \"update\" command or \"commit force\".\n");
207 ret
= nb_candidate_commit(vty
->candidate_config
, NB_CLIENT_CLI
, true,
208 comment
, &transaction_id
);
210 /* Map northbound return code to CLI return code. */
213 nb_config_replace(vty
->candidate_config_base
, running_config
,
216 "%% Configuration committed successfully (Transaction ID #%u).\n\n",
219 case NB_ERR_NO_CHANGES
:
220 vty_out(vty
, "%% No configuration changes to commit.\n\n");
224 "%% Failed to commit candidate configuration: %s.\n\n",
226 vty_out(vty
, "Please check the logs for more details.\n");
231 static int nb_cli_candidate_load_file(struct vty
*vty
,
232 enum nb_cfg_format format
,
233 struct yang_translator
*translator
,
234 const char *path
, bool replace
)
236 struct nb_config
*loaded_config
= NULL
;
237 struct lyd_node
*dnode
;
238 struct ly_ctx
*ly_ctx
;
242 case NB_CFG_FMT_CMDS
:
243 loaded_config
= nb_config_new(NULL
);
244 if (!vty_read_config(loaded_config
, path
, config_default
)) {
245 vty_out(vty
, "%% Failed to load configuration.\n\n");
247 "Please check the logs for more details.\n");
248 nb_config_free(loaded_config
);
252 case NB_CFG_FMT_JSON
:
254 ly_format
= (format
== NB_CFG_FMT_JSON
) ? LYD_JSON
: LYD_XML
;
256 ly_ctx
= translator
? translator
->ly_ctx
: ly_native_ctx
;
257 dnode
= lyd_parse_path(ly_ctx
, path
, ly_format
, LYD_OPT_EDIT
);
259 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_parse_path() failed",
261 vty_out(vty
, "%% Failed to load configuration:\n\n");
262 vty_show_libyang_errors(vty
, ly_ctx
);
266 && yang_translate_dnode(translator
,
267 YANG_TRANSLATE_TO_NATIVE
, &dnode
)
268 != YANG_TRANSLATE_SUCCESS
) {
269 vty_out(vty
, "%% Failed to translate configuration\n");
270 yang_dnode_free(dnode
);
273 loaded_config
= nb_config_new(dnode
);
278 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
279 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
282 "%% Failed to merge the loaded configuration:\n\n");
283 vty_show_libyang_errors(vty
, ly_native_ctx
);
290 static int nb_cli_candidate_load_transaction(struct vty
*vty
,
291 uint32_t transaction_id
,
294 struct nb_config
*loaded_config
;
296 loaded_config
= nb_db_transaction_load(transaction_id
);
297 if (!loaded_config
) {
298 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
304 nb_config_replace(vty
->candidate_config
, loaded_config
, false);
305 else if (nb_config_merge(vty
->candidate_config
, loaded_config
, false)
308 "%% Failed to merge the loaded configuration:\n\n");
309 vty_show_libyang_errors(vty
, ly_native_ctx
);
316 void nb_cli_show_dnode_cmds(struct vty
*vty
, struct lyd_node
*root
,
319 struct lyd_node
*next
, *child
;
321 LY_TREE_DFS_BEGIN (root
, next
, child
) {
322 struct nb_node
*nb_node
;
324 nb_node
= child
->schema
->priv
;
325 if (!nb_node
->cbs
.cli_show
)
328 /* Skip default values. */
329 if (!with_defaults
&& yang_dnode_is_default_recursive(child
))
332 (*nb_node
->cbs
.cli_show
)(vty
, child
, with_defaults
);
334 LY_TREE_DFS_END(root
, next
, child
);
338 static void nb_cli_show_config_cmds(struct vty
*vty
, struct nb_config
*config
,
341 struct lyd_node
*root
;
343 vty_out(vty
, "Configuration:\n");
345 vty_out(vty
, "frr version %s\n", FRR_VER_SHORT
);
346 vty_out(vty
, "frr defaults %s\n", DFLT_NAME
);
348 LY_TREE_FOR (config
->dnode
, root
)
349 nb_cli_show_dnode_cmds(vty
, root
, with_defaults
);
352 vty_out(vty
, "end\n");
355 static int nb_cli_show_config_libyang(struct vty
*vty
, LYD_FORMAT format
,
356 struct nb_config
*config
,
357 struct yang_translator
*translator
,
360 struct lyd_node
*dnode
;
364 dnode
= yang_dnode_dup(config
->dnode
);
366 && yang_translate_dnode(translator
, YANG_TRANSLATE_FROM_NATIVE
,
368 != YANG_TRANSLATE_SUCCESS
) {
369 vty_out(vty
, "%% Failed to translate configuration\n");
370 yang_dnode_free(dnode
);
374 options
= LYP_FORMAT
| LYP_WITHSIBLINGS
;
376 options
|= LYP_WD_ALL
;
378 options
|= LYP_WD_TRIM
;
380 if (lyd_print_mem(&strp
, dnode
, format
, options
) == 0 && strp
) {
381 vty_out(vty
, "%s", strp
);
385 yang_dnode_free(dnode
);
390 static int nb_cli_show_config(struct vty
*vty
, struct nb_config
*config
,
391 enum nb_cfg_format format
,
392 struct yang_translator
*translator
,
396 case NB_CFG_FMT_CMDS
:
397 nb_cli_show_config_cmds(vty
, config
, with_defaults
);
399 case NB_CFG_FMT_JSON
:
400 return nb_cli_show_config_libyang(vty
, LYD_JSON
, config
,
401 translator
, with_defaults
);
403 return nb_cli_show_config_libyang(vty
, LYD_XML
, config
,
404 translator
, with_defaults
);
410 static int nb_write_config(struct nb_config
*config
, enum nb_cfg_format format
,
411 struct yang_translator
*translator
, char *path
,
415 struct vty
*file_vty
;
418 snprintf(path
, pathlen
, "/tmp/frr.tmp.XXXXXXXX");
421 flog_warn(EC_LIB_SYSTEM_CALL
, "%s: mkstemp() failed: %s",
422 __func__
, safe_strerror(errno
));
426 /* Make vty for configuration file. */
427 file_vty
= vty_new();
429 file_vty
->type
= VTY_FILE
;
431 ret
= nb_cli_show_config(file_vty
, config
, format
, translator
,
438 static int nb_cli_show_config_compare(struct vty
*vty
,
439 struct nb_config
*config1
,
440 struct nb_config
*config2
,
441 enum nb_cfg_format format
,
442 struct yang_translator
*translator
)
444 char config1_path
[256];
445 char config2_path
[256];
446 char command
[BUFSIZ
];
451 if (nb_write_config(config1
, format
, translator
, config1_path
,
452 sizeof(config1_path
))
454 vty_out(vty
, "%% Failed to process configurations.\n\n");
457 if (nb_write_config(config2
, format
, translator
, config2_path
,
458 sizeof(config2_path
))
460 vty_out(vty
, "%% Failed to process configurations.\n\n");
461 unlink(config1_path
);
465 snprintf(command
, sizeof(command
), "diff -u %s %s", config1_path
,
467 fp
= popen(command
, "r");
469 vty_out(vty
, "%% Failed to generate configuration diff.\n\n");
470 unlink(config1_path
);
471 unlink(config2_path
);
474 /* Print diff line by line. */
475 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
478 vty_out(vty
, "%s", line
);
482 unlink(config1_path
);
483 unlink(config2_path
);
488 /* Configure exclusively from this terminal. */
489 DEFUN (config_exclusive
,
490 config_exclusive_cmd
,
491 "configure exclusive",
492 "Configuration from vty interface\n"
493 "Configure exclusively from this terminal\n")
495 if (vty_config_exclusive_lock(vty
))
496 vty
->node
= CONFIG_NODE
;
498 vty_out(vty
, "VTY configuration is locked by other VTY\n");
499 return CMD_WARNING_CONFIG_FAILED
;
502 vty
->private_config
= true;
503 vty
->candidate_config
= nb_config_dup(running_config
);
504 vty
->candidate_config_base
= nb_config_dup(running_config
);
506 "Warning: uncommitted changes will be discarded on exit.\n\n");
511 /* Configure using a private candidate configuration. */
512 DEFUN (config_private
,
515 "Configuration from vty interface\n"
516 "Configure using a private candidate configuration\n")
518 if (vty_config_lock(vty
))
519 vty
->node
= CONFIG_NODE
;
521 vty_out(vty
, "VTY configuration is locked by other VTY\n");
522 return CMD_WARNING_CONFIG_FAILED
;
525 vty
->private_config
= true;
526 vty
->candidate_config
= nb_config_dup(running_config
);
527 vty
->candidate_config_base
= nb_config_dup(running_config
);
529 "Warning: uncommitted changes will be discarded on exit.\n\n");
534 DEFPY (config_commit
,
536 "commit [force$force]",
537 "Commit changes into the running configuration\n"
538 "Force commit even if the candidate is outdated\n")
540 return nb_cli_commit(vty
, !!force
, NULL
);
543 DEFPY (config_commit_comment
,
544 config_commit_comment_cmd
,
545 "commit [force$force] comment LINE...",
546 "Commit changes into the running configuration\n"
547 "Force commit even if the candidate is outdated\n"
548 "Assign a comment to this commit\n"
549 "Comment for this commit (Max 80 characters)\n")
555 argv_find(argv
, argc
, "LINE", &idx
);
556 comment
= argv_concat(argv
, argc
, idx
);
557 ret
= nb_cli_commit(vty
, !!force
, comment
);
558 XFREE(MTYPE_TMP
, comment
);
563 DEFPY (config_commit_check
,
564 config_commit_check_cmd
,
566 "Commit changes into the running configuration\n"
567 "Check if the configuration changes are valid\n")
571 ret
= nb_candidate_validate(vty
->candidate_config
);
574 "%% Failed to validate candidate configuration.\n\n");
575 vty_show_libyang_errors(vty
, ly_native_ctx
);
579 vty_out(vty
, "%% Candidate configuration validated successfully.\n\n");
584 DEFPY (config_update
,
587 "Update candidate configuration\n")
589 if (!nb_candidate_needs_update(vty
->candidate_config
)) {
590 vty_out(vty
, "%% Update is not necessary.\n\n");
594 if (nb_candidate_update(vty
->candidate_config
) != NB_OK
) {
596 "%% Failed to update the candidate configuration.\n\n");
597 vty_out(vty
, "Please check the logs for more details.\n");
601 nb_config_replace(vty
->candidate_config_base
, running_config
, true);
603 vty_out(vty
, "%% Candidate configuration updated successfully.\n\n");
608 DEFPY (config_discard
,
611 "Discard changes in the candidate configuration\n")
613 nb_config_replace(vty
->candidate_config
, vty
->candidate_config_base
,
623 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
624 |transaction (1-4294967296)$tid\
627 "Configuration related settings\n"
628 "Load configuration into candidate\n"
629 "Load configuration file into candidate\n"
630 "Load configuration file in JSON format\n"
631 "Load configuration file in XML format\n"
632 "Translate configuration file\n"
633 "YANG module translator\n"
634 "Configuration file name (full path)\n"
635 "Load configuration from transaction into candidate\n"
637 "Replace instead of merge\n")
640 enum nb_cfg_format format
;
641 struct yang_translator
*translator
= NULL
;
644 format
= NB_CFG_FMT_JSON
;
646 format
= NB_CFG_FMT_XML
;
648 format
= NB_CFG_FMT_CMDS
;
650 if (translator_family
) {
651 translator
= yang_translator_find(translator_family
);
654 "%% Module translator \"%s\" not found\n",
660 return nb_cli_candidate_load_file(vty
, format
, translator
,
661 filename
, !!replace
);
664 return nb_cli_candidate_load_transaction(vty
, tid
, !!replace
);
667 DEFPY (show_config_running
,
668 show_config_running_cmd
,
669 "show configuration running\
670 [<json$json|xml$xml> [translate WORD$translator_family]]\
671 [with-defaults$with_defaults]",
673 "Configuration information\n"
674 "Running configuration\n"
675 "Change output format to JSON\n"
676 "Change output format to XML\n"
678 "YANG module translator\n"
679 "Show default values\n")
682 enum nb_cfg_format format
;
683 struct yang_translator
*translator
= NULL
;
686 format
= NB_CFG_FMT_JSON
;
688 format
= NB_CFG_FMT_XML
;
690 format
= NB_CFG_FMT_CMDS
;
692 if (translator_family
) {
693 translator
= yang_translator_find(translator_family
);
695 vty_out(vty
, "%% Module translator \"%s\" not found\n",
701 nb_cli_show_config(vty
, running_config
, format
, translator
,
707 DEFPY (show_config_candidate
,
708 show_config_candidate_cmd
,
709 "show configuration candidate\
710 [<json$json|xml$xml> [translate WORD$translator_family]]\
712 with-defaults$with_defaults\
716 "Configuration information\n"
717 "Candidate configuration\n"
718 "Change output format to JSON\n"
719 "Change output format to XML\n"
721 "YANG module translator\n"
722 "Show default values\n"
723 "Show changes applied in the candidate configuration\n")
726 enum nb_cfg_format format
;
727 struct yang_translator
*translator
= NULL
;
730 format
= NB_CFG_FMT_JSON
;
732 format
= NB_CFG_FMT_XML
;
734 format
= NB_CFG_FMT_CMDS
;
736 if (translator_family
) {
737 translator
= yang_translator_find(translator_family
);
739 vty_out(vty
, "%% Module translator \"%s\" not found\n",
746 return nb_cli_show_config_compare(
747 vty
, vty
->candidate_config_base
, vty
->candidate_config
,
750 nb_cli_show_config(vty
, vty
->candidate_config
, format
, translator
,
756 DEFPY (show_config_compare
,
757 show_config_compare_cmd
,
758 "show configuration compare\
760 candidate$c1_candidate\
762 |transaction (1-4294967296)$c1_tid\
765 candidate$c2_candidate\
767 |transaction (1-4294967296)$c2_tid\
769 [<json$json|xml$xml> [translate WORD$translator_family]]",
771 "Configuration information\n"
772 "Compare two different configurations\n"
773 "Candidate configuration\n"
774 "Running configuration\n"
775 "Configuration transaction\n"
777 "Candidate configuration\n"
778 "Running configuration\n"
779 "Configuration transaction\n"
781 "Change output format to JSON\n"
782 "Change output format to XML\n"
784 "YANG module translator\n")
786 enum nb_cfg_format format
;
787 struct yang_translator
*translator
= NULL
;
788 struct nb_config
*config1
, *config_transaction1
= NULL
;
789 struct nb_config
*config2
, *config_transaction2
= NULL
;
790 int ret
= CMD_WARNING
;
793 config1
= vty
->candidate_config
;
795 config1
= running_config
;
797 config_transaction1
= nb_db_transaction_load(c1_tid
);
798 if (!config_transaction1
) {
799 vty_out(vty
, "%% Transaction %u does not exist\n\n",
800 (unsigned int)c1_tid
);
803 config1
= config_transaction1
;
807 config2
= vty
->candidate_config
;
809 config2
= running_config
;
811 config_transaction2
= nb_db_transaction_load(c2_tid
);
812 if (!config_transaction2
) {
813 vty_out(vty
, "%% Transaction %u does not exist\n\n",
814 (unsigned int)c2_tid
);
817 config2
= config_transaction2
;
821 format
= NB_CFG_FMT_JSON
;
823 format
= NB_CFG_FMT_XML
;
825 format
= NB_CFG_FMT_CMDS
;
827 if (translator_family
) {
828 translator
= yang_translator_find(translator_family
);
830 vty_out(vty
, "%% Module translator \"%s\" not found\n",
836 ret
= nb_cli_show_config_compare(vty
, config1
, config2
, format
,
839 if (config_transaction1
)
840 nb_config_free(config_transaction1
);
841 if (config_transaction2
)
842 nb_config_free(config_transaction2
);
848 * Stripped down version of the "show configuration compare" command.
849 * The "candidate" option is not present so the command can be installed in
852 ALIAS (show_config_compare
,
853 show_config_compare_without_candidate_cmd
,
854 "show configuration compare\
857 |transaction (1-4294967296)$c1_tid\
861 |transaction (1-4294967296)$c2_tid\
863 [<json$json|xml$xml> [translate WORD$translator_family]]",
865 "Configuration information\n"
866 "Compare two different configurations\n"
867 "Running configuration\n"
868 "Configuration transaction\n"
870 "Running configuration\n"
871 "Configuration transaction\n"
873 "Change output format to JSON\n"
874 "Change output format to XML\n"
876 "YANG module translator\n")
878 DEFPY (clear_config_transactions
,
879 clear_config_transactions_cmd
,
880 "clear configuration transactions oldest (1-100)$n",
882 "Configuration activity\n"
883 "Delete transactions from the transactions log\n"
884 "Delete oldest <n> transactions\n"
885 "Number of transactions to delete\n")
887 #ifdef HAVE_CONFIG_ROLLBACKS
888 if (nb_db_clear_transactions(n
) != NB_OK
) {
889 vty_out(vty
, "%% Failed to delete transactions.\n\n");
894 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
895 #endif /* HAVE_CONFIG_ROLLBACKS */
900 DEFPY (config_database_max_transactions
,
901 config_database_max_transactions_cmd
,
902 "configuration database max-transactions (1-100)$max",
903 "Configuration related settings\n"
904 "Configuration database\n"
905 "Set the maximum number of transactions to store\n"
906 "Number of transactions\n")
908 #ifdef HAVE_CONFIG_ROLLBACKS
909 if (nb_db_set_max_transactions(max
) != NB_OK
) {
911 "%% Failed to update the maximum number of transactions.\n\n");
915 "%% Maximum number of transactions updated successfully.\n\n");
918 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
919 #endif /* HAVE_CONFIG_ROLLBACKS */
924 DEFPY (yang_module_translator_load
,
925 yang_module_translator_load_cmd
,
926 "yang module-translator load FILENAME$filename",
927 "YANG related settings\n"
928 "YANG module translator\n"
929 "Load YANG module translator\n"
930 "File name (full path)\n")
932 struct yang_translator
*translator
;
934 translator
= yang_translator_load(filename
);
936 vty_out(vty
, "%% Failed to load \"%s\"\n\n", filename
);
937 vty_out(vty
, "Please check the logs for more details.\n");
941 vty_out(vty
, "%% Module translator \"%s\" loaded successfully.\n\n",
947 DEFPY (yang_module_translator_unload_family
,
948 yang_module_translator_unload_cmd
,
949 "yang module-translator unload WORD$translator_family",
950 "YANG related settings\n"
951 "YANG module translator\n"
952 "Unload YANG module translator\n"
953 "Name of the module translator\n")
955 struct yang_translator
*translator
;
957 translator
= yang_translator_find(translator_family
);
959 vty_out(vty
, "%% Module translator \"%s\" not found\n",
964 yang_translator_unload(translator
);
969 #ifdef HAVE_CONFIG_ROLLBACKS
970 static void nb_cli_show_transactions_cb(void *arg
, int transaction_id
,
971 const char *client_name
,
972 const char *date
, const char *comment
)
974 struct ttable
*tt
= arg
;
976 ttable_add_row(tt
, "%d|%s|%s|%s", transaction_id
, client_name
, date
,
980 static int nb_cli_show_transactions(struct vty
*vty
)
985 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
986 ttable_add_row(tt
, "Transaction ID|Client|Date|Comment");
987 tt
->style
.cell
.rpad
= 2;
988 tt
->style
.corner
= '+';
990 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
992 /* Fetch transactions from the northbound database. */
993 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb
, tt
)
996 "%% Failed to fetch configuration transactions.\n");
1000 /* Dump the generated table. */
1001 if (tt
->nrows
> 1) {
1004 table
= ttable_dump(tt
, "\n");
1005 vty_out(vty
, "%s\n", table
);
1006 XFREE(MTYPE_TMP
, table
);
1008 vty_out(vty
, "No configuration transactions to display.\n\n");
1014 #endif /* HAVE_CONFIG_ROLLBACKS */
1016 DEFPY (show_config_transaction
,
1017 show_config_transaction_cmd
,
1018 "show configuration transaction\
1020 (1-4294967296)$transaction_id\
1021 [<json$json|xml$xml> [translate WORD$translator_family]]\
1023 with-defaults$with_defaults\
1028 "Configuration information\n"
1029 "Configuration transaction\n"
1031 "Change output format to JSON\n"
1032 "Change output format to XML\n"
1033 "Translate output\n"
1034 "YANG module translator\n"
1035 "Show default values\n"
1036 "Show changes compared to the previous transaction\n")
1038 #ifdef HAVE_CONFIG_ROLLBACKS
1039 if (transaction_id
) {
1040 struct nb_config
*config
;
1041 enum nb_cfg_format format
;
1042 struct yang_translator
*translator
= NULL
;
1045 format
= NB_CFG_FMT_JSON
;
1047 format
= NB_CFG_FMT_XML
;
1049 format
= NB_CFG_FMT_CMDS
;
1051 if (translator_family
) {
1052 translator
= yang_translator_find(translator_family
);
1055 "%% Module translator \"%s\" not found\n",
1061 config
= nb_db_transaction_load(transaction_id
);
1063 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1064 (unsigned int)transaction_id
);
1069 struct nb_config
*prev_config
;
1072 /* NOTE: this can be NULL. */
1074 nb_db_transaction_load(transaction_id
- 1);
1076 ret
= nb_cli_show_config_compare(
1077 vty
, prev_config
, config
, format
, translator
);
1079 nb_config_free(prev_config
);
1080 nb_config_free(config
);
1085 nb_cli_show_config(vty
, config
, format
, translator
,
1087 nb_config_free(config
);
1092 return nb_cli_show_transactions(vty
);
1095 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1097 #endif /* HAVE_CONFIG_ROLLBACKS */
1100 DEFPY (show_yang_module
,
1101 show_yang_module_cmd
,
1102 "show yang module [module-translator WORD$translator_family]",
1104 "YANG information\n"
1105 "Show loaded modules\n"
1106 "YANG module translator\n"
1107 "YANG module translator\n")
1109 struct ly_ctx
*ly_ctx
;
1110 struct yang_translator
*translator
= NULL
;
1111 const struct lys_module
*module
;
1115 if (translator_family
) {
1116 translator
= yang_translator_find(translator_family
);
1118 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1122 ly_ctx
= translator
->ly_ctx
;
1124 ly_ctx
= ly_native_ctx
;
1126 /* Prepare table. */
1127 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1128 ttable_add_row(tt
, "Module|Version|Revision|Flags|Namespace");
1129 tt
->style
.cell
.rpad
= 2;
1130 tt
->style
.corner
= '+';
1132 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1134 while ((module
= ly_ctx_get_module_iter(ly_ctx
, &idx
))) {
1137 snprintf(flags
, sizeof(flags
), "%c%c",
1138 module
->implemented
? 'I' : ' ',
1139 (module
->deviated
== 1) ? 'D' : ' ');
1141 ttable_add_row(tt
, "%s|%s|%s|%s|%s", module
->name
,
1142 (module
->version
== 2) ? "1.1" : "1.0",
1143 (module
->rev_size
> 0) ? module
->rev
[0].date
1148 /* Dump the generated table. */
1149 if (tt
->nrows
> 1) {
1152 vty_out(vty
, " Flags: I - Implemented, D - Deviated\n\n");
1154 table
= ttable_dump(tt
, "\n");
1155 vty_out(vty
, "%s\n", table
);
1156 XFREE(MTYPE_TMP
, table
);
1158 vty_out(vty
, "No YANG modules to display.\n\n");
1165 DEFPY (show_yang_module_detail
,
1166 show_yang_module_detail_cmd
,
1168 [module-translator WORD$translator_family]\
1169 WORD$module_name <summary|tree$tree|yang$yang|yin$yin>",
1171 "YANG information\n"
1172 "Show loaded modules\n"
1173 "YANG module translator\n"
1174 "YANG module translator\n"
1176 "Display summary information about the module\n"
1177 "Display module in the tree (RFC 8340) format\n"
1178 "Display module in the YANG format\n"
1179 "Display module in the YIN format\n")
1181 struct ly_ctx
*ly_ctx
;
1182 struct yang_translator
*translator
= NULL
;
1183 const struct lys_module
*module
;
1184 LYS_OUTFORMAT format
;
1187 if (translator_family
) {
1188 translator
= yang_translator_find(translator_family
);
1190 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1194 ly_ctx
= translator
->ly_ctx
;
1196 ly_ctx
= ly_native_ctx
;
1198 module
= ly_ctx_get_module(ly_ctx
, module_name
, NULL
, 0);
1200 vty_out(vty
, "%% Module \"%s\" not found\n", module_name
);
1205 format
= LYS_OUT_YANG
;
1207 format
= LYS_OUT_YIN
;
1209 format
= LYS_OUT_TREE
;
1211 format
= LYS_OUT_INFO
;
1213 if (lys_print_mem(&strp
, module
, format
, NULL
, 0, 0) == 0) {
1214 vty_out(vty
, "%s\n", strp
);
1218 vty_out(vty
, "%% Error generating module information\n");
1225 DEFPY (show_yang_module_translator
,
1226 show_yang_module_translator_cmd
,
1227 "show yang module-translator",
1229 "YANG information\n"
1230 "Show loaded YANG module translators\n")
1232 struct yang_translator
*translator
;
1235 /* Prepare table. */
1236 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1237 ttable_add_row(tt
, "Family|Module|Deviations|Coverage (%%)");
1238 tt
->style
.cell
.rpad
= 2;
1239 tt
->style
.corner
= '+';
1241 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1243 RB_FOREACH (translator
, yang_translators
, &yang_translators
) {
1244 struct yang_tmodule
*tmodule
;
1245 struct listnode
*ln
;
1247 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
1248 ttable_add_row(tt
, "%s|%s|%s|%.2f", translator
->family
,
1249 tmodule
->module
->name
,
1250 tmodule
->deviations
->name
,
1255 /* Dump the generated table. */
1256 if (tt
->nrows
> 1) {
1259 table
= ttable_dump(tt
, "\n");
1260 vty_out(vty
, "%s\n", table
);
1261 XFREE(MTYPE_TMP
, table
);
1263 vty_out(vty
, "No YANG module translators to display.\n\n");
1270 #ifdef HAVE_CONFIG_ROLLBACKS
1271 static int nb_cli_rollback_configuration(struct vty
*vty
,
1272 uint32_t transaction_id
)
1274 struct nb_config
*candidate
;
1278 candidate
= nb_db_transaction_load(transaction_id
);
1280 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1285 snprintf(comment
, sizeof(comment
), "Rollback to transaction %u",
1288 ret
= nb_candidate_commit(candidate
, NB_CLIENT_CLI
, true, comment
,
1290 nb_config_free(candidate
);
1294 "%% Configuration was successfully rolled back.\n\n");
1296 case NB_ERR_NO_CHANGES
:
1298 "%% Aborting - no configuration changes detected.\n\n");
1301 vty_out(vty
, "%% Rollback failed.\n\n");
1302 vty_out(vty
, "Please check the logs for more details.\n");
1306 #endif /* HAVE_CONFIG_ROLLBACKS */
1308 DEFPY (rollback_config
,
1309 rollback_config_cmd
,
1310 "rollback configuration (1-4294967296)$transaction_id",
1311 "Rollback to a previous state\n"
1312 "Running configuration\n"
1315 #ifdef HAVE_CONFIG_ROLLBACKS
1316 return nb_cli_rollback_configuration(vty
, transaction_id
);
1319 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1321 #endif /* HAVE_CONFIG_ROLLBACKS */
1324 /* Debug CLI commands. */
1329 "Northbound Debugging\n")
1331 debug_northbound
= 1;
1338 "no debug northbound",
1340 "Northbound Debugging\n")
1342 debug_northbound
= 0;
1347 static int nb_debug_config_write(struct vty
*vty
)
1349 if (debug_northbound
)
1350 vty_out(vty
, "debug northbound\n");
1355 static struct cmd_node nb_debug_node
= {NORTHBOUND_DEBUG_NODE
, "", 1};
1357 void nb_cli_install_default(int node
)
1359 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL
)
1362 install_element(node
, &config_commit_cmd
);
1363 install_element(node
, &config_commit_comment_cmd
);
1364 install_element(node
, &config_commit_check_cmd
);
1365 install_element(node
, &config_update_cmd
);
1366 install_element(node
, &config_discard_cmd
);
1367 install_element(node
, &show_config_running_cmd
);
1368 install_element(node
, &show_config_candidate_cmd
);
1369 install_element(node
, &show_config_compare_cmd
);
1370 install_element(node
, &show_config_transaction_cmd
);
1373 /* YANG module autocomplete. */
1374 static void yang_module_autocomplete(vector comps
, struct cmd_token
*token
)
1376 const struct lys_module
*module
;
1377 struct yang_translator
*module_tr
;
1381 while ((module
= ly_ctx_get_module_iter(ly_native_ctx
, &idx
)))
1382 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1384 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
) {
1386 while ((module
= ly_ctx_get_module_iter(module_tr
->ly_ctx
,
1389 XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1393 /* YANG module translator autocomplete. */
1394 static void yang_translator_autocomplete(vector comps
, struct cmd_token
*token
)
1396 struct yang_translator
*module_tr
;
1398 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
)
1399 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module_tr
->family
));
1402 static const struct cmd_variable_handler yang_var_handlers
[] = {
1403 {.varname
= "module_name", .completions
= yang_module_autocomplete
},
1404 {.varname
= "translator_family",
1405 .completions
= yang_translator_autocomplete
},
1406 {.completions
= NULL
}};
1408 void nb_cli_init(void)
1410 /* Initialize the shared candidate configuration. */
1411 vty_shared_candidate_config
= nb_config_new(NULL
);
1413 /* Install debug commands */
1414 install_node(&nb_debug_node
, nb_debug_config_write
);
1415 install_element(ENABLE_NODE
, &debug_nb_cmd
);
1416 install_element(ENABLE_NODE
, &no_debug_nb_cmd
);
1417 install_element(CONFIG_NODE
, &debug_nb_cmd
);
1418 install_element(CONFIG_NODE
, &no_debug_nb_cmd
);
1420 /* Install commands specific to the transaction-base mode. */
1421 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL
) {
1422 install_element(ENABLE_NODE
, &config_exclusive_cmd
);
1423 install_element(ENABLE_NODE
, &config_private_cmd
);
1424 install_element(ENABLE_NODE
, &show_config_running_cmd
);
1425 install_element(ENABLE_NODE
,
1426 &show_config_compare_without_candidate_cmd
);
1427 install_element(ENABLE_NODE
, &show_config_transaction_cmd
);
1428 install_element(ENABLE_NODE
, &rollback_config_cmd
);
1429 install_element(ENABLE_NODE
, &clear_config_transactions_cmd
);
1431 install_element(CONFIG_NODE
, &config_load_cmd
);
1432 install_element(CONFIG_NODE
,
1433 &config_database_max_transactions_cmd
);
1436 /* Other commands. */
1437 install_element(CONFIG_NODE
, &yang_module_translator_load_cmd
);
1438 install_element(CONFIG_NODE
, &yang_module_translator_unload_cmd
);
1439 install_element(ENABLE_NODE
, &show_yang_module_cmd
);
1440 install_element(ENABLE_NODE
, &show_yang_module_detail_cmd
);
1441 install_element(ENABLE_NODE
, &show_yang_module_translator_cmd
);
1442 cmd_variable_handler_register(yang_var_handlers
);
1445 void nb_cli_terminate(void)
1447 nb_config_free(vty_shared_candidate_config
);