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 SET_FLAG(options
, LYP_FORMAT
| LYP_WITHSIBLINGS
);
376 SET_FLAG(options
, LYP_WD_ALL
);
378 SET_FLAG(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 static int nb_cli_oper_data_cb(const struct lys_node
*snode
,
1101 struct yang_translator
*translator
,
1102 struct yang_data
*data
, void *arg
)
1104 struct lyd_node
*dnode
= arg
;
1105 struct ly_ctx
*ly_ctx
;
1110 ret
= yang_translate_xpath(translator
,
1111 YANG_TRANSLATE_FROM_NATIVE
,
1112 data
->xpath
, sizeof(data
->xpath
));
1114 case YANG_TRANSLATE_SUCCESS
:
1116 case YANG_TRANSLATE_NOTFOUND
:
1118 case YANG_TRANSLATE_FAILURE
:
1122 ly_ctx
= translator
->ly_ctx
;
1124 ly_ctx
= ly_native_ctx
;
1127 dnode
= lyd_new_path(dnode
, ly_ctx
, data
->xpath
, (void *)data
->value
, 0,
1128 LYD_PATH_OPT_UPDATE
);
1129 if (!dnode
&& ly_errno
) {
1130 flog_warn(EC_LIB_LIBYANG
, "%s: lyd_new_path() failed",
1136 yang_data_free(data
);
1140 yang_data_free(data
);
1144 DEFPY (show_yang_operational_data
,
1145 show_yang_operational_data_cmd
,
1146 "show yang operational-data XPATH$xpath\
1148 format <json$json|xml$xml>\
1149 |translate WORD$translator_family\
1152 "YANG information\n"
1153 "Show YANG operational data\n"
1154 "XPath expression specifying the YANG data path\n"
1155 "Set the output format\n"
1156 "JavaScript Object Notation\n"
1157 "Extensible Markup Language\n"
1158 "Translate operational data\n"
1159 "YANG module translator\n")
1162 struct yang_translator
*translator
= NULL
;
1163 struct ly_ctx
*ly_ctx
;
1164 struct lyd_node
*dnode
;
1172 if (translator_family
) {
1173 translator
= yang_translator_find(translator_family
);
1175 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1180 ly_ctx
= translator
->ly_ctx
;
1182 ly_ctx
= ly_native_ctx
;
1185 dnode
= yang_dnode_new(ly_ctx
, false);
1186 if (nb_oper_data_iterate(xpath
, translator
, 0, nb_cli_oper_data_cb
,
1189 vty_out(vty
, "%% Failed to fetch operational data.\n");
1190 yang_dnode_free(dnode
);
1193 lyd_validate(&dnode
, LYD_OPT_DATA
| LYD_OPT_DATA_NO_YANGLIB
, ly_ctx
);
1195 /* Display the data. */
1196 if (lyd_print_mem(&strp
, dnode
, format
,
1197 LYP_FORMAT
| LYP_WITHSIBLINGS
| LYP_WD_ALL
)
1200 vty_out(vty
, "%% Failed to display operational data.\n");
1201 yang_dnode_free(dnode
);
1204 vty_out(vty
, "%s", strp
);
1206 yang_dnode_free(dnode
);
1211 DEFPY (show_yang_module
,
1212 show_yang_module_cmd
,
1213 "show yang module [module-translator WORD$translator_family]",
1215 "YANG information\n"
1216 "Show loaded modules\n"
1217 "YANG module translator\n"
1218 "YANG module translator\n")
1220 struct ly_ctx
*ly_ctx
;
1221 struct yang_translator
*translator
= NULL
;
1222 const struct lys_module
*module
;
1226 if (translator_family
) {
1227 translator
= yang_translator_find(translator_family
);
1229 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1233 ly_ctx
= translator
->ly_ctx
;
1235 ly_ctx
= ly_native_ctx
;
1237 /* Prepare table. */
1238 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1239 ttable_add_row(tt
, "Module|Version|Revision|Flags|Namespace");
1240 tt
->style
.cell
.rpad
= 2;
1241 tt
->style
.corner
= '+';
1243 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1245 while ((module
= ly_ctx_get_module_iter(ly_ctx
, &idx
))) {
1248 snprintf(flags
, sizeof(flags
), "%c%c",
1249 module
->implemented
? 'I' : ' ',
1250 (module
->deviated
== 1) ? 'D' : ' ');
1252 ttable_add_row(tt
, "%s|%s|%s|%s|%s", module
->name
,
1253 (module
->version
== 2) ? "1.1" : "1.0",
1254 (module
->rev_size
> 0) ? module
->rev
[0].date
1259 /* Dump the generated table. */
1260 if (tt
->nrows
> 1) {
1263 vty_out(vty
, " Flags: I - Implemented, D - Deviated\n\n");
1265 table
= ttable_dump(tt
, "\n");
1266 vty_out(vty
, "%s\n", table
);
1267 XFREE(MTYPE_TMP
, table
);
1269 vty_out(vty
, "No YANG modules to display.\n\n");
1276 DEFPY (show_yang_module_detail
,
1277 show_yang_module_detail_cmd
,
1279 [module-translator WORD$translator_family]\
1280 WORD$module_name <summary|tree$tree|yang$yang|yin$yin>",
1282 "YANG information\n"
1283 "Show loaded modules\n"
1284 "YANG module translator\n"
1285 "YANG module translator\n"
1287 "Display summary information about the module\n"
1288 "Display module in the tree (RFC 8340) format\n"
1289 "Display module in the YANG format\n"
1290 "Display module in the YIN format\n")
1292 struct ly_ctx
*ly_ctx
;
1293 struct yang_translator
*translator
= NULL
;
1294 const struct lys_module
*module
;
1295 LYS_OUTFORMAT format
;
1298 if (translator_family
) {
1299 translator
= yang_translator_find(translator_family
);
1301 vty_out(vty
, "%% Module translator \"%s\" not found\n",
1305 ly_ctx
= translator
->ly_ctx
;
1307 ly_ctx
= ly_native_ctx
;
1309 module
= ly_ctx_get_module(ly_ctx
, module_name
, NULL
, 0);
1311 vty_out(vty
, "%% Module \"%s\" not found\n", module_name
);
1316 format
= LYS_OUT_YANG
;
1318 format
= LYS_OUT_YIN
;
1320 format
= LYS_OUT_TREE
;
1322 format
= LYS_OUT_INFO
;
1324 if (lys_print_mem(&strp
, module
, format
, NULL
, 0, 0) == 0) {
1325 vty_out(vty
, "%s\n", strp
);
1329 vty_out(vty
, "%% Error generating module information\n");
1336 DEFPY (show_yang_module_translator
,
1337 show_yang_module_translator_cmd
,
1338 "show yang module-translator",
1340 "YANG information\n"
1341 "Show loaded YANG module translators\n")
1343 struct yang_translator
*translator
;
1346 /* Prepare table. */
1347 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
1348 ttable_add_row(tt
, "Family|Module|Deviations|Coverage (%%)");
1349 tt
->style
.cell
.rpad
= 2;
1350 tt
->style
.corner
= '+';
1352 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
1354 RB_FOREACH (translator
, yang_translators
, &yang_translators
) {
1355 struct yang_tmodule
*tmodule
;
1356 struct listnode
*ln
;
1358 for (ALL_LIST_ELEMENTS_RO(translator
->modules
, ln
, tmodule
)) {
1359 ttable_add_row(tt
, "%s|%s|%s|%.2f", translator
->family
,
1360 tmodule
->module
->name
,
1361 tmodule
->deviations
->name
,
1366 /* Dump the generated table. */
1367 if (tt
->nrows
> 1) {
1370 table
= ttable_dump(tt
, "\n");
1371 vty_out(vty
, "%s\n", table
);
1372 XFREE(MTYPE_TMP
, table
);
1374 vty_out(vty
, "No YANG module translators to display.\n\n");
1381 #ifdef HAVE_CONFIG_ROLLBACKS
1382 static int nb_cli_rollback_configuration(struct vty
*vty
,
1383 uint32_t transaction_id
)
1385 struct nb_config
*candidate
;
1389 candidate
= nb_db_transaction_load(transaction_id
);
1391 vty_out(vty
, "%% Transaction %u does not exist.\n\n",
1396 snprintf(comment
, sizeof(comment
), "Rollback to transaction %u",
1399 ret
= nb_candidate_commit(candidate
, NB_CLIENT_CLI
, true, comment
,
1401 nb_config_free(candidate
);
1405 "%% Configuration was successfully rolled back.\n\n");
1407 case NB_ERR_NO_CHANGES
:
1409 "%% Aborting - no configuration changes detected.\n\n");
1412 vty_out(vty
, "%% Rollback failed.\n\n");
1413 vty_out(vty
, "Please check the logs for more details.\n");
1417 #endif /* HAVE_CONFIG_ROLLBACKS */
1419 DEFPY (rollback_config
,
1420 rollback_config_cmd
,
1421 "rollback configuration (1-4294967296)$transaction_id",
1422 "Rollback to a previous state\n"
1423 "Running configuration\n"
1426 #ifdef HAVE_CONFIG_ROLLBACKS
1427 return nb_cli_rollback_configuration(vty
, transaction_id
);
1430 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1432 #endif /* HAVE_CONFIG_ROLLBACKS */
1435 /* Debug CLI commands. */
1440 "Northbound Debugging\n")
1442 debug_northbound
= 1;
1449 "no debug northbound",
1451 "Northbound Debugging\n")
1453 debug_northbound
= 0;
1458 static int nb_debug_config_write(struct vty
*vty
)
1460 if (debug_northbound
)
1461 vty_out(vty
, "debug northbound\n");
1466 static struct cmd_node nb_debug_node
= {NORTHBOUND_DEBUG_NODE
, "", 1};
1468 void nb_cli_install_default(int node
)
1470 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL
)
1473 install_element(node
, &config_commit_cmd
);
1474 install_element(node
, &config_commit_comment_cmd
);
1475 install_element(node
, &config_commit_check_cmd
);
1476 install_element(node
, &config_update_cmd
);
1477 install_element(node
, &config_discard_cmd
);
1478 install_element(node
, &show_config_running_cmd
);
1479 install_element(node
, &show_config_candidate_cmd
);
1480 install_element(node
, &show_config_compare_cmd
);
1481 install_element(node
, &show_config_transaction_cmd
);
1484 /* YANG module autocomplete. */
1485 static void yang_module_autocomplete(vector comps
, struct cmd_token
*token
)
1487 const struct lys_module
*module
;
1488 struct yang_translator
*module_tr
;
1492 while ((module
= ly_ctx_get_module_iter(ly_native_ctx
, &idx
)))
1493 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1495 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
) {
1497 while ((module
= ly_ctx_get_module_iter(module_tr
->ly_ctx
,
1500 XSTRDUP(MTYPE_COMPLETION
, module
->name
));
1504 /* YANG module translator autocomplete. */
1505 static void yang_translator_autocomplete(vector comps
, struct cmd_token
*token
)
1507 struct yang_translator
*module_tr
;
1509 RB_FOREACH (module_tr
, yang_translators
, &yang_translators
)
1510 vector_set(comps
, XSTRDUP(MTYPE_COMPLETION
, module_tr
->family
));
1513 static const struct cmd_variable_handler yang_var_handlers
[] = {
1514 {.varname
= "module_name", .completions
= yang_module_autocomplete
},
1515 {.varname
= "translator_family",
1516 .completions
= yang_translator_autocomplete
},
1517 {.completions
= NULL
}};
1519 void nb_cli_init(void)
1521 /* Initialize the shared candidate configuration. */
1522 vty_shared_candidate_config
= nb_config_new(NULL
);
1524 /* Install debug commands */
1525 install_node(&nb_debug_node
, nb_debug_config_write
);
1526 install_element(ENABLE_NODE
, &debug_nb_cmd
);
1527 install_element(ENABLE_NODE
, &no_debug_nb_cmd
);
1528 install_element(CONFIG_NODE
, &debug_nb_cmd
);
1529 install_element(CONFIG_NODE
, &no_debug_nb_cmd
);
1531 /* Install commands specific to the transaction-base mode. */
1532 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL
) {
1533 install_element(ENABLE_NODE
, &config_exclusive_cmd
);
1534 install_element(ENABLE_NODE
, &config_private_cmd
);
1535 install_element(ENABLE_NODE
, &show_config_running_cmd
);
1536 install_element(ENABLE_NODE
,
1537 &show_config_compare_without_candidate_cmd
);
1538 install_element(ENABLE_NODE
, &show_config_transaction_cmd
);
1539 install_element(ENABLE_NODE
, &rollback_config_cmd
);
1540 install_element(ENABLE_NODE
, &clear_config_transactions_cmd
);
1542 install_element(CONFIG_NODE
, &config_load_cmd
);
1543 install_element(CONFIG_NODE
,
1544 &config_database_max_transactions_cmd
);
1547 /* Other commands. */
1548 install_element(CONFIG_NODE
, &yang_module_translator_load_cmd
);
1549 install_element(CONFIG_NODE
, &yang_module_translator_unload_cmd
);
1550 install_element(ENABLE_NODE
, &show_yang_operational_data_cmd
);
1551 install_element(ENABLE_NODE
, &show_yang_module_cmd
);
1552 install_element(ENABLE_NODE
, &show_yang_module_detail_cmd
);
1553 install_element(ENABLE_NODE
, &show_yang_module_translator_cmd
);
1554 cmd_variable_handler_register(yang_var_handlers
);
1557 void nb_cli_terminate(void)
1559 nb_config_free(vty_shared_candidate_config
);