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 SET_FLAG(options
, LYP_FORMAT
| LYP_WITHSIBLINGS
);
403 SET_FLAG(options
, LYP_WD_ALL
);
405 SET_FLAG(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 return vty_config_enter(vty
, true, true);
525 /* Configure using a private candidate configuration. */
526 DEFUN (config_private
,
529 "Configuration from vty interface\n"
530 "Configure using a private candidate configuration\n")
532 return vty_config_enter(vty
, true, false);
535 DEFPY (config_commit
,
537 "commit [force$force]",
538 "Commit changes into the running configuration\n"
539 "Force commit even if the candidate is outdated\n")
541 return nb_cli_commit(vty
, !!force
, NULL
);
544 DEFPY (config_commit_comment
,
545 config_commit_comment_cmd
,
546 "commit [force$force] comment LINE...",
547 "Commit changes into the running configuration\n"
548 "Force commit even if the candidate is outdated\n"
549 "Assign a comment to this commit\n"
550 "Comment for this commit (Max 80 characters)\n")
556 argv_find(argv
, argc
, "LINE", &idx
);
557 comment
= argv_concat(argv
, argc
, idx
);
558 ret
= nb_cli_commit(vty
, !!force
, comment
);
559 XFREE(MTYPE_TMP
, comment
);
564 DEFPY (config_commit_check
,
565 config_commit_check_cmd
,
567 "Commit changes into the running configuration\n"
568 "Check if the configuration changes are valid\n")
572 ret
= nb_candidate_validate(vty
->candidate_config
);
575 "%% Failed to validate candidate configuration.\n\n");
576 vty_show_libyang_errors(vty
, ly_native_ctx
);
580 vty_out(vty
, "%% Candidate configuration validated successfully.\n\n");
585 DEFPY (config_update
,
588 "Update candidate configuration\n")
590 if (!nb_candidate_needs_update(vty
->candidate_config
)) {
591 vty_out(vty
, "%% Update is not necessary.\n\n");
595 if (nb_candidate_update(vty
->candidate_config
) != NB_OK
) {
597 "%% Failed to update the candidate configuration.\n\n");
598 vty_out(vty
, "Please check the logs for more details.\n");
602 nb_config_replace(vty
->candidate_config_base
, running_config
, true);
604 vty_out(vty
, "%% Candidate configuration updated successfully.\n\n");
609 DEFPY (config_discard
,
612 "Discard changes in the candidate configuration\n")
614 nb_config_replace(vty
->candidate_config
, vty
->candidate_config_base
,
624 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
625 |transaction (1-4294967296)$tid\
628 "Configuration related settings\n"
629 "Load configuration into candidate\n"
630 "Load configuration file into candidate\n"
631 "Load configuration file in JSON format\n"
632 "Load configuration file in XML format\n"
633 "Translate configuration file\n"
634 "YANG module translator\n"
635 "Configuration file name (full path)\n"
636 "Load configuration from transaction into candidate\n"
638 "Replace instead of merge\n")
641 enum nb_cfg_format format
;
642 struct yang_translator
*translator
= NULL
;
645 format
= NB_CFG_FMT_JSON
;
647 format
= NB_CFG_FMT_XML
;
649 format
= NB_CFG_FMT_CMDS
;
651 if (translator_family
) {
652 translator
= yang_translator_find(translator_family
);
655 "%% Module translator \"%s\" not found\n",
661 return nb_cli_candidate_load_file(vty
, format
, translator
,
662 filename
, !!replace
);
665 return nb_cli_candidate_load_transaction(vty
, tid
, !!replace
);
668 DEFPY (show_config_running
,
669 show_config_running_cmd
,
670 "show configuration running\
671 [<json$json|xml$xml> [translate WORD$translator_family]]\
672 [with-defaults$with_defaults]",
674 "Configuration information\n"
675 "Running configuration\n"
676 "Change output format to JSON\n"
677 "Change output format to XML\n"
679 "YANG module translator\n"
680 "Show default values\n")
683 enum nb_cfg_format format
;
684 struct yang_translator
*translator
= NULL
;
687 format
= NB_CFG_FMT_JSON
;
689 format
= NB_CFG_FMT_XML
;
691 format
= NB_CFG_FMT_CMDS
;
693 if (translator_family
) {
694 translator
= yang_translator_find(translator_family
);
696 vty_out(vty
, "%% Module translator \"%s\" not found\n",
702 nb_cli_show_config(vty
, running_config
, format
, translator
,
708 DEFPY (show_config_candidate
,
709 show_config_candidate_cmd
,
710 "show configuration candidate\
711 [<json$json|xml$xml> [translate WORD$translator_family]]\
713 with-defaults$with_defaults\
717 "Configuration information\n"
718 "Candidate configuration\n"
719 "Change output format to JSON\n"
720 "Change output format to XML\n"
722 "YANG module translator\n"
723 "Show default values\n"
724 "Show changes applied in the candidate configuration\n")
727 enum nb_cfg_format format
;
728 struct yang_translator
*translator
= NULL
;
731 format
= NB_CFG_FMT_JSON
;
733 format
= NB_CFG_FMT_XML
;
735 format
= NB_CFG_FMT_CMDS
;
737 if (translator_family
) {
738 translator
= yang_translator_find(translator_family
);
740 vty_out(vty
, "%% Module translator \"%s\" not found\n",
747 return nb_cli_show_config_compare(
748 vty
, vty
->candidate_config_base
, vty
->candidate_config
,
751 nb_cli_show_config(vty
, vty
->candidate_config
, format
, translator
,
757 DEFPY (show_config_compare
,
758 show_config_compare_cmd
,
759 "show configuration compare\
761 candidate$c1_candidate\
763 |transaction (1-4294967296)$c1_tid\
766 candidate$c2_candidate\
768 |transaction (1-4294967296)$c2_tid\
770 [<json$json|xml$xml> [translate WORD$translator_family]]",
772 "Configuration information\n"
773 "Compare two different configurations\n"
774 "Candidate configuration\n"
775 "Running configuration\n"
776 "Configuration transaction\n"
778 "Candidate configuration\n"
779 "Running configuration\n"
780 "Configuration transaction\n"
782 "Change output format to JSON\n"
783 "Change output format to XML\n"
785 "YANG module translator\n")
787 enum nb_cfg_format format
;
788 struct yang_translator
*translator
= NULL
;
789 struct nb_config
*config1
, *config_transaction1
= NULL
;
790 struct nb_config
*config2
, *config_transaction2
= NULL
;
791 int ret
= CMD_WARNING
;
794 config1
= vty
->candidate_config
;
796 config1
= running_config
;
798 config_transaction1
= nb_db_transaction_load(c1_tid
);
799 if (!config_transaction1
) {
800 vty_out(vty
, "%% Transaction %u does not exist\n\n",
801 (unsigned int)c1_tid
);
804 config1
= config_transaction1
;
808 config2
= vty
->candidate_config
;
810 config2
= running_config
;
812 config_transaction2
= nb_db_transaction_load(c2_tid
);
813 if (!config_transaction2
) {
814 vty_out(vty
, "%% Transaction %u does not exist\n\n",
815 (unsigned int)c2_tid
);
818 config2
= config_transaction2
;
822 format
= NB_CFG_FMT_JSON
;
824 format
= NB_CFG_FMT_XML
;
826 format
= NB_CFG_FMT_CMDS
;
828 if (translator_family
) {
829 translator
= yang_translator_find(translator_family
);
831 vty_out(vty
, "%% Module translator \"%s\" not found\n",
837 ret
= nb_cli_show_config_compare(vty
, config1
, config2
, format
,
840 if (config_transaction1
)
841 nb_config_free(config_transaction1
);
842 if (config_transaction2
)
843 nb_config_free(config_transaction2
);
849 * Stripped down version of the "show configuration compare" command.
850 * The "candidate" option is not present so the command can be installed in
853 ALIAS (show_config_compare
,
854 show_config_compare_without_candidate_cmd
,
855 "show configuration compare\
858 |transaction (1-4294967296)$c1_tid\
862 |transaction (1-4294967296)$c2_tid\
864 [<json$json|xml$xml> [translate WORD$translator_family]]",
866 "Configuration information\n"
867 "Compare two different configurations\n"
868 "Running configuration\n"
869 "Configuration transaction\n"
871 "Running configuration\n"
872 "Configuration transaction\n"
874 "Change output format to JSON\n"
875 "Change output format to XML\n"
877 "YANG module translator\n")
879 DEFPY (clear_config_transactions
,
880 clear_config_transactions_cmd
,
881 "clear configuration transactions oldest (1-100)$n",
883 "Configuration activity\n"
884 "Delete transactions from the transactions log\n"
885 "Delete oldest <n> transactions\n"
886 "Number of transactions to delete\n")
888 #ifdef HAVE_CONFIG_ROLLBACKS
889 if (nb_db_clear_transactions(n
) != NB_OK
) {
890 vty_out(vty
, "%% Failed to delete transactions.\n\n");
895 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
896 #endif /* HAVE_CONFIG_ROLLBACKS */
901 DEFPY (config_database_max_transactions
,
902 config_database_max_transactions_cmd
,
903 "configuration database max-transactions (1-100)$max",
904 "Configuration related settings\n"
905 "Configuration database\n"
906 "Set the maximum number of transactions to store\n"
907 "Number of transactions\n")
909 #ifdef HAVE_CONFIG_ROLLBACKS
910 if (nb_db_set_max_transactions(max
) != NB_OK
) {
912 "%% Failed to update the maximum number of transactions.\n\n");
916 "%% Maximum number of transactions updated successfully.\n\n");
919 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
920 #endif /* HAVE_CONFIG_ROLLBACKS */
925 DEFPY (yang_module_translator_load
,
926 yang_module_translator_load_cmd
,
927 "yang module-translator load FILENAME$filename",
928 "YANG related settings\n"
929 "YANG module translator\n"
930 "Load YANG module translator\n"
931 "File name (full path)\n")
933 struct yang_translator
*translator
;
935 translator
= yang_translator_load(filename
);
937 vty_out(vty
, "%% Failed to load \"%s\"\n\n", filename
);
938 vty_out(vty
, "Please check the logs for more details.\n");
942 vty_out(vty
, "%% Module translator \"%s\" loaded successfully.\n\n",
948 DEFPY (yang_module_translator_unload_family
,
949 yang_module_translator_unload_cmd
,
950 "yang module-translator unload WORD$translator_family",
951 "YANG related settings\n"
952 "YANG module translator\n"
953 "Unload YANG module translator\n"
954 "Name of the module translator\n")
956 struct yang_translator
*translator
;
958 translator
= yang_translator_find(translator_family
);
960 vty_out(vty
, "%% Module translator \"%s\" not found\n",
965 yang_translator_unload(translator
);
970 #ifdef HAVE_CONFIG_ROLLBACKS
971 static void nb_cli_show_transactions_cb(void *arg
, int transaction_id
,
972 const char *client_name
,
973 const char *date
, const char *comment
)
975 struct ttable
*tt
= arg
;
977 ttable_add_row(tt
, "%d|%s|%s|%s", transaction_id
, client_name
, date
,
981 static int nb_cli_show_transactions(struct vty
*vty
)
986 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
987 ttable_add_row(tt
, "Transaction ID|Client|Date|Comment");
988 tt
->style
.cell
.rpad
= 2;
989 tt
->style
.corner
= '+';
991 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
993 /* Fetch transactions from the northbound database. */
994 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb
, tt
)
997 "%% Failed to fetch configuration transactions.\n");
1001 /* Dump the generated table. */
1002 if (tt
->nrows
> 1) {
1005 table
= ttable_dump(tt
, "\n");
1006 vty_out(vty
, "%s\n", table
);
1007 XFREE(MTYPE_TMP
, table
);
1009 vty_out(vty
, "No configuration transactions to display.\n\n");
1015 #endif /* HAVE_CONFIG_ROLLBACKS */
1017 DEFPY (show_config_transaction
,
1018 show_config_transaction_cmd
,
1019 "show configuration transaction\
1021 (1-4294967296)$transaction_id\
1022 [<json$json|xml$xml> [translate WORD$translator_family]]\
1024 with-defaults$with_defaults\
1029 "Configuration information\n"
1030 "Configuration transaction\n"
1032 "Change output format to JSON\n"
1033 "Change output format to XML\n"
1034 "Translate output\n"
1035 "YANG module translator\n"
1036 "Show default values\n"
1037 "Show changes compared to the previous transaction\n")
1039 #ifdef HAVE_CONFIG_ROLLBACKS
1040 if (transaction_id
) {
1041 struct nb_config
*config
;
1042 enum nb_cfg_format format
;
1043 struct yang_translator
*translator
= NULL
;
1046 format
= NB_CFG_FMT_JSON
;
1048 format
= NB_CFG_FMT_XML
;
1050 format
= NB_CFG_FMT_CMDS
;
1052 if (translator_family
) {
1053 translator
= yang_translator_find(translator_family
);
1056 "%% Module translator \"%s\" not found\n",
1062 config
= nb_db_transaction_load(transaction_id
);
1064 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1065 (unsigned int)transaction_id
);
1070 struct nb_config
*prev_config
;
1073 /* NOTE: this can be NULL. */
1075 nb_db_transaction_load(transaction_id
- 1);
1077 ret
= nb_cli_show_config_compare(
1078 vty
, prev_config
, config
, format
, translator
);
1080 nb_config_free(prev_config
);
1081 nb_config_free(config
);
1086 nb_cli_show_config(vty
, config
, format
, translator
,
1088 nb_config_free(config
);
1093 return nb_cli_show_transactions(vty
);
1096 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1098 #endif /* HAVE_CONFIG_ROLLBACKS */
1101 static int nb_cli_oper_data_cb(const struct lys_node
*snode
,
1102 struct yang_translator
*translator
,
1103 struct yang_data
*data
, void *arg
)
1105 struct lyd_node
*dnode
= arg
;
1106 struct ly_ctx
*ly_ctx
;
1111 ret
= yang_translate_xpath(translator
,
1112 YANG_TRANSLATE_FROM_NATIVE
,
1113 data
->xpath
, sizeof(data
->xpath
));
1115 case YANG_TRANSLATE_SUCCESS
:
1117 case YANG_TRANSLATE_NOTFOUND
:
1119 case YANG_TRANSLATE_FAILURE
:
1123 ly_ctx
= translator
->ly_ctx
;
1125 ly_ctx
= ly_native_ctx
;
1128 dnode
= lyd_new_path(dnode
, ly_ctx
, data
->xpath
, (void *)data
->value
, 0,
1129 LYD_PATH_OPT_UPDATE
);
1130 if (!dnode
&& ly_errno
) {
1131 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_new_path() failed",
1137 yang_data_free(data
);
1141 yang_data_free(data
);
1145 DEFPY (show_yang_operational_data
,
1146 show_yang_operational_data_cmd
,
1147 "show yang operational-data XPATH$xpath\
1149 format <json$json|xml$xml>\
1150 |translate WORD$translator_family\
1153 "YANG information\n"
1154 "Show YANG operational data\n"
1155 "XPath expression specifying the YANG data path\n"
1156 "Set the output format\n"
1157 "JavaScript Object Notation\n"
1158 "Extensible Markup Language\n"
1159 "Translate operational data\n"
1160 "YANG module translator\n")
1163 struct yang_translator
*translator
= NULL
;
1164 struct ly_ctx
*ly_ctx
;
1165 struct lyd_node
*dnode
;
1173 if (translator_family
) {
1174 translator
= yang_translator_find(translator_family
);
1176 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1181 ly_ctx
= translator
->ly_ctx
;
1183 ly_ctx
= ly_native_ctx
;
1186 dnode
= yang_dnode_new(ly_ctx
, false);
1187 if (nb_oper_data_iterate(xpath
, translator
, 0, nb_cli_oper_data_cb
,
1190 vty_out(vty
, "%% Failed to fetch operational data.\n");
1191 yang_dnode_free(dnode
);
1194 lyd_validate(&dnode
, LYD_OPT_DATA
| LYD_OPT_DATA_NO_YANGLIB
, ly_ctx
);
1196 /* Display the data. */
1197 if (lyd_print_mem(&strp
, dnode
, format
,
1198 LYP_FORMAT
| LYP_WITHSIBLINGS
| LYP_WD_ALL
)
1201 vty_out(vty
, "%% Failed to display operational data.\n");
1202 yang_dnode_free(dnode
);
1205 vty_out(vty
, "%s", strp
);
1207 yang_dnode_free(dnode
);
1212 DEFPY (show_yang_module
,
1213 show_yang_module_cmd
,
1214 "show yang module [module-translator WORD$translator_family]",
1216 "YANG information\n"
1217 "Show loaded modules\n"
1218 "YANG module translator\n"
1219 "YANG module translator\n")
1221 struct ly_ctx
*ly_ctx
;
1222 struct yang_translator
*translator
= NULL
;
1223 const struct lys_module
*module
;
1227 if (translator_family
) {
1228 translator
= yang_translator_find(translator_family
);
1230 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1234 ly_ctx
= translator
->ly_ctx
;
1236 ly_ctx
= ly_native_ctx
;
1238 /* Prepare table. */
1239 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1240 ttable_add_row(tt
, "Module|Version|Revision|Flags|Namespace");
1241 tt
->style
.cell
.rpad
= 2;
1242 tt
->style
.corner
= '+';
1244 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1246 while ((module
= ly_ctx_get_module_iter(ly_ctx
, &idx
))) {
1249 snprintf(flags
, sizeof(flags
), "%c%c",
1250 module
->implemented
? 'I' : ' ',
1251 (module
->deviated
== 1) ? 'D' : ' ');
1253 ttable_add_row(tt
, "%s|%s|%s|%s|%s", module
->name
,
1254 (module
->version
== 2) ? "1.1" : "1.0",
1255 (module
->rev_size
> 0) ? module
->rev
[0].date
1260 /* Dump the generated table. */
1261 if (tt
->nrows
> 1) {
1264 vty_out(vty
, " Flags: I - Implemented, D - Deviated\n\n");
1266 table
= ttable_dump(tt
, "\n");
1267 vty_out(vty
, "%s\n", table
);
1268 XFREE(MTYPE_TMP
, table
);
1270 vty_out(vty
, "No YANG modules to display.\n\n");
1277 DEFPY (show_yang_module_detail
,
1278 show_yang_module_detail_cmd
,
1280 [module-translator WORD$translator_family]\
1281 WORD$module_name <summary|tree$tree|yang$yang|yin$yin>",
1283 "YANG information\n"
1284 "Show loaded modules\n"
1285 "YANG module translator\n"
1286 "YANG module translator\n"
1288 "Display summary information about the module\n"
1289 "Display module in the tree (RFC 8340) format\n"
1290 "Display module in the YANG format\n"
1291 "Display module in the YIN format\n")
1293 struct ly_ctx
*ly_ctx
;
1294 struct yang_translator
*translator
= NULL
;
1295 const struct lys_module
*module
;
1296 LYS_OUTFORMAT format
;
1299 if (translator_family
) {
1300 translator
= yang_translator_find(translator_family
);
1302 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1306 ly_ctx
= translator
->ly_ctx
;
1308 ly_ctx
= ly_native_ctx
;
1310 module
= ly_ctx_get_module(ly_ctx
, module_name
, NULL
, 0);
1312 vty_out(vty
, "%% Module \"%s\" not found\n", module_name
);
1317 format
= LYS_OUT_YANG
;
1319 format
= LYS_OUT_YIN
;
1321 format
= LYS_OUT_TREE
;
1323 format
= LYS_OUT_INFO
;
1325 if (lys_print_mem(&strp
, module
, format
, NULL
, 0, 0) == 0) {
1326 vty_out(vty
, "%s\n", strp
);
1330 vty_out(vty
, "%% Error generating module information\n");
1337 DEFPY (show_yang_module_translator
,
1338 show_yang_module_translator_cmd
,
1339 "show yang module-translator",
1341 "YANG information\n"
1342 "Show loaded YANG module translators\n")
1344 struct yang_translator
*translator
;
1347 /* Prepare table. */
1348 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1349 ttable_add_row(tt
, "Family|Module|Deviations|Coverage (%%)");
1350 tt
->style
.cell
.rpad
= 2;
1351 tt
->style
.corner
= '+';
1353 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1355 RB_FOREACH (translator
, yang_translators
, &yang_translators
) {
1356 struct yang_tmodule
*tmodule
;
1357 struct listnode
*ln
;
1359 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
1360 ttable_add_row(tt
, "%s|%s|%s|%.2f", translator
->family
,
1361 tmodule
->module
->name
,
1362 tmodule
->deviations
->name
,
1367 /* Dump the generated table. */
1368 if (tt
->nrows
> 1) {
1371 table
= ttable_dump(tt
, "\n");
1372 vty_out(vty
, "%s\n", table
);
1373 XFREE(MTYPE_TMP
, table
);
1375 vty_out(vty
, "No YANG module translators to display.\n\n");
1382 #ifdef HAVE_CONFIG_ROLLBACKS
1383 static int nb_cli_rollback_configuration(struct vty
*vty
,
1384 uint32_t transaction_id
)
1386 struct nb_config
*candidate
;
1390 candidate
= nb_db_transaction_load(transaction_id
);
1392 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1397 snprintf(comment
, sizeof(comment
), "Rollback to transaction %u",
1400 ret
= nb_candidate_commit(candidate
, NB_CLIENT_CLI
, true, comment
,
1402 nb_config_free(candidate
);
1406 "%% Configuration was successfully rolled back.\n\n");
1408 case NB_ERR_NO_CHANGES
:
1410 "%% Aborting - no configuration changes detected.\n\n");
1413 vty_out(vty
, "%% Rollback failed.\n\n");
1414 vty_out(vty
, "Please check the logs for more details.\n");
1418 #endif /* HAVE_CONFIG_ROLLBACKS */
1420 DEFPY (rollback_config
,
1421 rollback_config_cmd
,
1422 "rollback configuration (1-4294967296)$transaction_id",
1423 "Rollback to a previous state\n"
1424 "Running configuration\n"
1427 #ifdef HAVE_CONFIG_ROLLBACKS
1428 return nb_cli_rollback_configuration(vty
, transaction_id
);
1431 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1433 #endif /* HAVE_CONFIG_ROLLBACKS */
1436 /* Debug CLI commands. */
1441 "Northbound Debugging\n")
1443 debug_northbound
= 1;
1450 "no debug northbound",
1452 "Northbound Debugging\n")
1454 debug_northbound
= 0;
1459 static int nb_debug_config_write(struct vty
*vty
)
1461 if (debug_northbound
)
1462 vty_out(vty
, "debug northbound\n");
1467 static struct cmd_node nb_debug_node
= {NORTHBOUND_DEBUG_NODE
, "", 1};
1469 void nb_cli_install_default(int node
)
1471 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL
)
1474 install_element(node
, &config_commit_cmd
);
1475 install_element(node
, &config_commit_comment_cmd
);
1476 install_element(node
, &config_commit_check_cmd
);
1477 install_element(node
, &config_update_cmd
);
1478 install_element(node
, &config_discard_cmd
);
1479 install_element(node
, &show_config_running_cmd
);
1480 install_element(node
, &show_config_candidate_cmd
);
1481 install_element(node
, &show_config_compare_cmd
);
1482 install_element(node
, &show_config_transaction_cmd
);
1485 /* YANG module autocomplete. */
1486 static void yang_module_autocomplete(vector comps
, struct cmd_token
*token
)
1488 const struct lys_module
*module
;
1489 struct yang_translator
*module_tr
;
1493 while ((module
= ly_ctx_get_module_iter(ly_native_ctx
, &idx
)))
1494 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1496 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
) {
1498 while ((module
= ly_ctx_get_module_iter(module_tr
->ly_ctx
,
1501 XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1505 /* YANG module translator autocomplete. */
1506 static void yang_translator_autocomplete(vector comps
, struct cmd_token
*token
)
1508 struct yang_translator
*module_tr
;
1510 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
)
1511 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module_tr
->family
));
1514 static const struct cmd_variable_handler yang_var_handlers
[] = {
1515 {.varname
= "module_name", .completions
= yang_module_autocomplete
},
1516 {.varname
= "translator_family",
1517 .completions
= yang_translator_autocomplete
},
1518 {.completions
= NULL
}};
1520 void nb_cli_init(void)
1522 /* Initialize the shared candidate configuration. */
1523 vty_shared_candidate_config
= nb_config_new(NULL
);
1525 /* Install debug commands */
1526 install_node(&nb_debug_node
, nb_debug_config_write
);
1527 install_element(ENABLE_NODE
, &debug_nb_cmd
);
1528 install_element(ENABLE_NODE
, &no_debug_nb_cmd
);
1529 install_element(CONFIG_NODE
, &debug_nb_cmd
);
1530 install_element(CONFIG_NODE
, &no_debug_nb_cmd
);
1532 /* Install commands specific to the transaction-base mode. */
1533 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL
) {
1534 install_element(ENABLE_NODE
, &config_exclusive_cmd
);
1535 install_element(ENABLE_NODE
, &config_private_cmd
);
1536 install_element(ENABLE_NODE
, &show_config_running_cmd
);
1537 install_element(ENABLE_NODE
,
1538 &show_config_compare_without_candidate_cmd
);
1539 install_element(ENABLE_NODE
, &show_config_transaction_cmd
);
1540 install_element(ENABLE_NODE
, &rollback_config_cmd
);
1541 install_element(ENABLE_NODE
, &clear_config_transactions_cmd
);
1543 install_element(CONFIG_NODE
, &config_load_cmd
);
1544 install_element(CONFIG_NODE
,
1545 &config_database_max_transactions_cmd
);
1548 /* Other commands. */
1549 install_element(CONFIG_NODE
, &yang_module_translator_load_cmd
);
1550 install_element(CONFIG_NODE
, &yang_module_translator_unload_cmd
);
1551 install_element(ENABLE_NODE
, &show_yang_operational_data_cmd
);
1552 install_element(ENABLE_NODE
, &show_yang_module_cmd
);
1553 install_element(ENABLE_NODE
, &show_yang_module_detail_cmd
);
1554 install_element(ENABLE_NODE
, &show_yang_module_translator_cmd
);
1555 cmd_variable_handler_register(yang_var_handlers
);
1558 void nb_cli_terminate(void)
1560 nb_config_free(vty_shared_candidate_config
);