]> git.proxmox.com Git - mirror_frr.git/blame - lib/northbound_cli.c
Merge pull request #4595 from ton31337/feature/autocomplete/bgp_peer-groups_7.1
[mirror_frr.git] / lib / northbound_cli.c
CommitLineData
1c2facd1
RW
1/*
2 * Copyright (C) 2018 NetDEF, Inc.
3 * Renato Westphal
4 *
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)
8 * any later version.
9 *
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
13 * more details.
14 *
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
18 */
19
20#include <zebra.h>
21
22#include "libfrr.h"
23#include "version.h"
24#include "log.h"
25#include "lib_errors.h"
26#include "command.h"
27#include "termtable.h"
28#include "db.h"
9eb2c0a1 29#include "debug.h"
1c2facd1
RW
30#include "yang_translator.h"
31#include "northbound.h"
32#include "northbound_cli.h"
33#include "northbound_db.h"
34#ifndef VTYSH_EXTRACT_PL
35#include "lib/northbound_cli_clippy.c"
36#endif
37
9eb2c0a1
RW
38struct debug nb_dbg_cbs_config = {0, "Northbound callbacks: configuration"};
39struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"};
40struct debug nb_dbg_cbs_rpc = {0, "Northbound callbacks: RPCs"};
41struct debug nb_dbg_notif = {0, "Northbound notifications"};
42struct debug nb_dbg_events = {0, "Northbound events"};
43
1c2facd1 44struct nb_config *vty_shared_candidate_config;
fbdc1c0a 45static struct thread_master *master;
1c2facd1
RW
46
47static void vty_show_libyang_errors(struct vty *vty, struct ly_ctx *ly_ctx)
48{
49 struct ly_err_item *ei;
50 const char *path;
51
52 ei = ly_err_first(ly_ctx);
53 if (!ei)
54 return;
55
56 for (; ei; ei = ei->next)
57 vty_out(vty, "%s\n", ei->msg);
58
59 path = ly_errpath(ly_ctx);
60 if (path)
61 vty_out(vty, "YANG path: %s\n", path);
62
63 ly_err_clean(ly_ctx, NULL);
64}
65
a6233bfc
RW
66void nb_cli_enqueue_change(struct vty *vty, const char *xpath,
67 enum nb_operation operation, const char *value)
68{
69 struct vty_cfg_change *change;
70
71 if (vty->num_cfg_changes == VTY_MAXCFGCHANGES) {
72 /* Not expected to happen. */
73 vty_out(vty,
74 "%% Exceeded the maximum number of changes (%u) for a single command\n\n",
75 VTY_MAXCFGCHANGES);
76 return;
77 }
78
79 change = &vty->cfg_changes[vty->num_cfg_changes++];
8427e0e6 80 strlcpy(change->xpath, xpath, sizeof(change->xpath));
a6233bfc
RW
81 change->operation = operation;
82 change->value = value;
83}
84
85int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
1c2facd1
RW
86{
87 struct nb_config *candidate_transitory;
3cb4162c 88 char xpath_base[XPATH_MAXLEN] = {};
1c2facd1
RW
89 bool error = false;
90 int ret;
91
92 VTY_CHECK_XPATH;
93
94 /*
95 * Create a copy of the candidate configuration. For consistency, we
96 * need to ensure that either all changes made by the command are
97 * accepted or none are.
98 */
99 candidate_transitory = nb_config_dup(vty->candidate_config);
100
a6233bfc 101 /* Parse the base XPath format string. */
3cb4162c
RW
102 if (xpath_base_fmt) {
103 va_list ap;
104
105 va_start(ap, xpath_base_fmt);
106 vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap);
107 va_end(ap);
108 }
a6233bfc 109
1c2facd1 110 /* Edit candidate configuration. */
a6233bfc
RW
111 for (size_t i = 0; i < vty->num_cfg_changes; i++) {
112 struct vty_cfg_change *change = &vty->cfg_changes[i];
1c2facd1
RW
113 struct nb_node *nb_node;
114 char xpath[XPATH_MAXLEN];
115 struct yang_data *data;
116
117 /* Handle relative XPaths. */
118 memset(xpath, 0, sizeof(xpath));
119 if (vty->xpath_index > 0
a6233bfc 120 && ((xpath_base_fmt && xpath_base[0] == '.')
1c2facd1
RW
121 || change->xpath[0] == '.'))
122 strlcpy(xpath, VTY_CURR_XPATH, sizeof(xpath));
a6233bfc 123 if (xpath_base_fmt) {
1c2facd1 124 if (xpath_base[0] == '.')
a6233bfc
RW
125 strlcat(xpath, xpath_base + 1, sizeof(xpath));
126 else
127 strlcat(xpath, xpath_base, sizeof(xpath));
1c2facd1
RW
128 }
129 if (change->xpath[0] == '.')
130 strlcat(xpath, change->xpath + 1, sizeof(xpath));
131 else
132 strlcpy(xpath, change->xpath, sizeof(xpath));
133
a6233bfc 134 /* Find the northbound node associated to the data path. */
1c2facd1
RW
135 nb_node = nb_node_find(xpath);
136 if (!nb_node) {
137 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
138 "%s: unknown data path: %s", __func__, xpath);
139 error = true;
140 break;
141 }
142
143 /* If the value is not set, get the default if it exists. */
144 if (change->value == NULL)
145 change->value = yang_snode_get_default(nb_node->snode);
146 data = yang_data_new(xpath, change->value);
147
148 /*
149 * Ignore "not found" errors when editing the candidate
150 * configuration.
151 */
152 ret = nb_candidate_edit(candidate_transitory, nb_node,
153 change->operation, xpath, NULL, data);
154 yang_data_free(data);
155 if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) {
156 flog_warn(
157 EC_LIB_NB_CANDIDATE_EDIT_ERROR,
158 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
159 __func__, nb_operation_name(change->operation),
160 xpath);
161 error = true;
162 break;
163 }
164 }
165
166 if (error) {
167 nb_config_free(candidate_transitory);
168
169 switch (frr_get_cli_mode()) {
170 case FRR_CLI_CLASSIC:
171 vty_out(vty, "%% Configuration failed.\n\n");
172 break;
173 case FRR_CLI_TRANSACTIONAL:
174 vty_out(vty,
175 "%% Failed to edit candidate configuration.\n\n");
176 break;
177 }
178 vty_show_libyang_errors(vty, ly_native_ctx);
179
180 return CMD_WARNING_CONFIG_FAILED;
181 }
182
183 nb_config_replace(vty->candidate_config, candidate_transitory, false);
184
185 /* Do an implicit "commit" when using the classic CLI mode. */
186 if (frr_get_cli_mode() == FRR_CLI_CLASSIC) {
187 ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI,
364ad673 188 vty, false, NULL, NULL);
1c2facd1
RW
189 if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
190 vty_out(vty, "%% Configuration failed: %s.\n\n",
191 nb_err_name(ret));
192 vty_out(vty,
193 "Please check the logs for more details.\n");
194
195 /* Regenerate candidate for consistency. */
83981138
RW
196 pthread_rwlock_rdlock(&running_config->lock);
197 {
198 nb_config_replace(vty->candidate_config,
199 running_config, true);
200 }
201 pthread_rwlock_unlock(&running_config->lock);
202
1c2facd1
RW
203 return CMD_WARNING_CONFIG_FAILED;
204 }
205 }
206
207 return CMD_SUCCESS;
208}
209
210int nb_cli_rpc(const char *xpath, struct list *input, struct list *output)
211{
212 struct nb_node *nb_node;
213 int ret;
214
215 nb_node = nb_node_find(xpath);
216 if (!nb_node) {
217 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
218 "%s: unknown data path: %s", __func__, xpath);
219 return CMD_WARNING;
220 }
221
9eb2c0a1 222 ret = nb_callback_rpc(nb_node, xpath, input, output);
1c2facd1
RW
223 switch (ret) {
224 case NB_OK:
225 return CMD_SUCCESS;
226 default:
227 return CMD_WARNING;
228 }
229}
230
fbdc1c0a
RW
231void nb_cli_confirmed_commit_clean(struct vty *vty)
232{
233 THREAD_TIMER_OFF(vty->t_confirmed_commit_timeout);
234 nb_config_free(vty->confirmed_commit_rollback);
235 vty->confirmed_commit_rollback = NULL;
236}
237
238int nb_cli_confirmed_commit_rollback(struct vty *vty)
1c2facd1
RW
239{
240 uint32_t transaction_id;
241 int ret;
242
fbdc1c0a
RW
243 /* Perform the rollback. */
244 ret = nb_candidate_commit(
364ad673 245 vty->confirmed_commit_rollback, NB_CLIENT_CLI, vty, true,
fbdc1c0a
RW
246 "Rollback to previous configuration - confirmed commit has timed out",
247 &transaction_id);
248 if (ret == NB_OK)
249 vty_out(vty,
250 "Rollback performed successfully (Transaction ID #%u).\n",
251 transaction_id);
252 else
253 vty_out(vty, "Failed to rollback to previous configuration.\n");
254
255 return ret;
256}
257
258static int nb_cli_confirmed_commit_timeout(struct thread *thread)
259{
260 struct vty *vty = THREAD_ARG(thread);
261
262 /* XXX: broadcast this message to all logged-in users? */
263 vty_out(vty,
264 "\nConfirmed commit has timed out, rolling back to previous configuration.\n\n");
265
266 nb_cli_confirmed_commit_rollback(vty);
267 nb_cli_confirmed_commit_clean(vty);
268
269 return 0;
270}
271
272static int nb_cli_commit(struct vty *vty, bool force,
273 unsigned int confirmed_timeout, char *comment)
274{
f65fb6b4 275 uint32_t transaction_id = 0;
fbdc1c0a
RW
276 int ret;
277
278 /* Check if there's a pending confirmed commit. */
279 if (vty->t_confirmed_commit_timeout) {
280 if (confirmed_timeout) {
281 /* Reset timeout if "commit confirmed" is used again. */
282 vty_out(vty,
283 "%% Resetting confirmed-commit timeout to %u minute(s)\n\n",
284 confirmed_timeout);
285
286 THREAD_TIMER_OFF(vty->t_confirmed_commit_timeout);
287 thread_add_timer(master,
288 nb_cli_confirmed_commit_timeout, vty,
289 confirmed_timeout * 60,
290 &vty->t_confirmed_commit_timeout);
291 } else {
292 /* Accept commit confirmation. */
293 vty_out(vty, "%% Commit complete.\n\n");
294 nb_cli_confirmed_commit_clean(vty);
295 }
296 return CMD_SUCCESS;
297 }
298
fbdc1c0a 299 /* "force" parameter. */
1c2facd1
RW
300 if (!force && nb_candidate_needs_update(vty->candidate_config)) {
301 vty_out(vty,
302 "%% Candidate configuration needs to be updated before commit.\n\n");
303 vty_out(vty,
304 "Use the \"update\" command or \"commit force\".\n");
305 return CMD_WARNING;
306 }
307
fbdc1c0a
RW
308 /* "confirm" parameter. */
309 if (confirmed_timeout) {
83981138
RW
310 pthread_rwlock_rdlock(&running_config->lock);
311 {
312 vty->confirmed_commit_rollback =
313 nb_config_dup(running_config);
314 }
315 pthread_rwlock_unlock(&running_config->lock);
fbdc1c0a
RW
316
317 vty->t_confirmed_commit_timeout = NULL;
318 thread_add_timer(master, nb_cli_confirmed_commit_timeout, vty,
319 confirmed_timeout * 60,
320 &vty->t_confirmed_commit_timeout);
321 }
322
364ad673
RW
323 ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, vty,
324 true, comment, &transaction_id);
1c2facd1
RW
325
326 /* Map northbound return code to CLI return code. */
327 switch (ret) {
328 case NB_OK:
83981138
RW
329 pthread_rwlock_rdlock(&running_config->lock);
330 {
331 nb_config_replace(vty->candidate_config_base,
332 running_config, true);
333 }
334 pthread_rwlock_unlock(&running_config->lock);
335
1c2facd1
RW
336 vty_out(vty,
337 "%% Configuration committed successfully (Transaction ID #%u).\n\n",
338 transaction_id);
339 return CMD_SUCCESS;
340 case NB_ERR_NO_CHANGES:
341 vty_out(vty, "%% No configuration changes to commit.\n\n");
342 return CMD_SUCCESS;
343 default:
344 vty_out(vty,
345 "%% Failed to commit candidate configuration: %s.\n\n",
346 nb_err_name(ret));
347 vty_out(vty, "Please check the logs for more details.\n");
348 return CMD_WARNING;
349 }
350}
351
352static int nb_cli_candidate_load_file(struct vty *vty,
353 enum nb_cfg_format format,
354 struct yang_translator *translator,
355 const char *path, bool replace)
356{
357 struct nb_config *loaded_config = NULL;
358 struct lyd_node *dnode;
359 struct ly_ctx *ly_ctx;
360 int ly_format;
361
362 switch (format) {
363 case NB_CFG_FMT_CMDS:
364 loaded_config = nb_config_new(NULL);
365 if (!vty_read_config(loaded_config, path, config_default)) {
366 vty_out(vty, "%% Failed to load configuration.\n\n");
367 vty_out(vty,
368 "Please check the logs for more details.\n");
369 nb_config_free(loaded_config);
370 return CMD_WARNING;
371 }
372 break;
373 case NB_CFG_FMT_JSON:
374 case NB_CFG_FMT_XML:
375 ly_format = (format == NB_CFG_FMT_JSON) ? LYD_JSON : LYD_XML;
376
377 ly_ctx = translator ? translator->ly_ctx : ly_native_ctx;
378 dnode = lyd_parse_path(ly_ctx, path, ly_format, LYD_OPT_EDIT);
379 if (!dnode) {
380 flog_warn(EC_LIB_LIBYANG, "%s: lyd_parse_path() failed",
381 __func__);
382 vty_out(vty, "%% Failed to load configuration:\n\n");
383 vty_show_libyang_errors(vty, ly_ctx);
384 return CMD_WARNING;
385 }
386 if (translator
387 && yang_translate_dnode(translator,
388 YANG_TRANSLATE_TO_NATIVE, &dnode)
389 != YANG_TRANSLATE_SUCCESS) {
390 vty_out(vty, "%% Failed to translate configuration\n");
391 yang_dnode_free(dnode);
392 return CMD_WARNING;
393 }
394 loaded_config = nb_config_new(dnode);
395 break;
396 }
397
398 if (replace)
399 nb_config_replace(vty->candidate_config, loaded_config, false);
400 else if (nb_config_merge(vty->candidate_config, loaded_config, false)
401 != NB_OK) {
402 vty_out(vty,
403 "%% Failed to merge the loaded configuration:\n\n");
404 vty_show_libyang_errors(vty, ly_native_ctx);
405 return CMD_WARNING;
406 }
407
408 return CMD_SUCCESS;
409}
410
411static int nb_cli_candidate_load_transaction(struct vty *vty,
412 uint32_t transaction_id,
413 bool replace)
414{
415 struct nb_config *loaded_config;
416
417 loaded_config = nb_db_transaction_load(transaction_id);
418 if (!loaded_config) {
419 vty_out(vty, "%% Transaction %u does not exist.\n\n",
420 transaction_id);
421 return CMD_WARNING;
422 }
423
424 if (replace)
425 nb_config_replace(vty->candidate_config, loaded_config, false);
426 else if (nb_config_merge(vty->candidate_config, loaded_config, false)
427 != NB_OK) {
428 vty_out(vty,
429 "%% Failed to merge the loaded configuration:\n\n");
430 vty_show_libyang_errors(vty, ly_native_ctx);
431 return CMD_WARNING;
432 }
433
434 return CMD_SUCCESS;
435}
436
437void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *root,
438 bool with_defaults)
439{
440 struct lyd_node *next, *child;
441
442 LY_TREE_DFS_BEGIN (root, next, child) {
443 struct nb_node *nb_node;
444
445 nb_node = child->schema->priv;
446 if (!nb_node->cbs.cli_show)
447 goto next;
448
449 /* Skip default values. */
450 if (!with_defaults && yang_dnode_is_default_recursive(child))
451 goto next;
452
453 (*nb_node->cbs.cli_show)(vty, child, with_defaults);
454 next:
455 LY_TREE_DFS_END(root, next, child);
456 }
457}
458
459static void nb_cli_show_config_cmds(struct vty *vty, struct nb_config *config,
460 bool with_defaults)
461{
462 struct lyd_node *root;
463
464 vty_out(vty, "Configuration:\n");
465 vty_out(vty, "!\n");
466 vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
467 vty_out(vty, "frr defaults %s\n", DFLT_NAME);
468
469 LY_TREE_FOR (config->dnode, root)
470 nb_cli_show_dnode_cmds(vty, root, with_defaults);
471
472 vty_out(vty, "!\n");
473 vty_out(vty, "end\n");
474}
475
476static int nb_cli_show_config_libyang(struct vty *vty, LYD_FORMAT format,
477 struct nb_config *config,
478 struct yang_translator *translator,
479 bool with_defaults)
480{
481 struct lyd_node *dnode;
482 char *strp;
db452508 483 int options = 0;
1c2facd1
RW
484
485 dnode = yang_dnode_dup(config->dnode);
486 if (translator
487 && yang_translate_dnode(translator, YANG_TRANSLATE_FROM_NATIVE,
488 &dnode)
489 != YANG_TRANSLATE_SUCCESS) {
490 vty_out(vty, "%% Failed to translate configuration\n");
491 yang_dnode_free(dnode);
492 return CMD_WARNING;
493 }
494
db452508 495 SET_FLAG(options, LYP_FORMAT | LYP_WITHSIBLINGS);
1c2facd1 496 if (with_defaults)
db452508 497 SET_FLAG(options, LYP_WD_ALL);
1c2facd1 498 else
db452508 499 SET_FLAG(options, LYP_WD_TRIM);
1c2facd1
RW
500
501 if (lyd_print_mem(&strp, dnode, format, options) == 0 && strp) {
502 vty_out(vty, "%s", strp);
503 free(strp);
504 }
505
506 yang_dnode_free(dnode);
507
508 return CMD_SUCCESS;
509}
510
511static int nb_cli_show_config(struct vty *vty, struct nb_config *config,
512 enum nb_cfg_format format,
513 struct yang_translator *translator,
514 bool with_defaults)
515{
516 switch (format) {
517 case NB_CFG_FMT_CMDS:
518 nb_cli_show_config_cmds(vty, config, with_defaults);
519 break;
520 case NB_CFG_FMT_JSON:
521 return nb_cli_show_config_libyang(vty, LYD_JSON, config,
522 translator, with_defaults);
523 case NB_CFG_FMT_XML:
524 return nb_cli_show_config_libyang(vty, LYD_XML, config,
525 translator, with_defaults);
526 }
527
528 return CMD_SUCCESS;
529}
530
531static int nb_write_config(struct nb_config *config, enum nb_cfg_format format,
532 struct yang_translator *translator, char *path,
533 size_t pathlen)
534{
535 int fd;
536 struct vty *file_vty;
537 int ret = 0;
538
539 snprintf(path, pathlen, "/tmp/frr.tmp.XXXXXXXX");
540 fd = mkstemp(path);
541 if (fd < 0) {
542 flog_warn(EC_LIB_SYSTEM_CALL, "%s: mkstemp() failed: %s",
543 __func__, safe_strerror(errno));
544 return -1;
545 }
546
547 /* Make vty for configuration file. */
548 file_vty = vty_new();
549 file_vty->wfd = fd;
550 file_vty->type = VTY_FILE;
551 if (config)
552 ret = nb_cli_show_config(file_vty, config, format, translator,
553 false);
554 vty_close(file_vty);
555
556 return ret;
557}
558
559static int nb_cli_show_config_compare(struct vty *vty,
560 struct nb_config *config1,
561 struct nb_config *config2,
562 enum nb_cfg_format format,
563 struct yang_translator *translator)
564{
565 char config1_path[256];
566 char config2_path[256];
567 char command[BUFSIZ];
568 FILE *fp;
569 char line[1024];
570 int lineno = 0;
571
572 if (nb_write_config(config1, format, translator, config1_path,
573 sizeof(config1_path))
574 != 0) {
575 vty_out(vty, "%% Failed to process configurations.\n\n");
576 return CMD_WARNING;
577 }
578 if (nb_write_config(config2, format, translator, config2_path,
579 sizeof(config2_path))
580 != 0) {
581 vty_out(vty, "%% Failed to process configurations.\n\n");
582 unlink(config1_path);
583 return CMD_WARNING;
584 }
585
586 snprintf(command, sizeof(command), "diff -u %s %s", config1_path,
587 config2_path);
588 fp = popen(command, "r");
589 if (!fp) {
590 vty_out(vty, "%% Failed to generate configuration diff.\n\n");
591 unlink(config1_path);
592 unlink(config2_path);
593 return CMD_WARNING;
594 }
595 /* Print diff line by line. */
596 while (fgets(line, sizeof(line), fp) != NULL) {
597 if (lineno++ < 2)
598 continue;
599 vty_out(vty, "%s", line);
600 }
601 pclose(fp);
602
603 unlink(config1_path);
604 unlink(config2_path);
605
606 return CMD_SUCCESS;
607}
608
609/* Configure exclusively from this terminal. */
610DEFUN (config_exclusive,
611 config_exclusive_cmd,
612 "configure exclusive",
613 "Configuration from vty interface\n"
614 "Configure exclusively from this terminal\n")
615{
f344c66e 616 return vty_config_enter(vty, true, true);
1c2facd1
RW
617}
618
619/* Configure using a private candidate configuration. */
620DEFUN (config_private,
621 config_private_cmd,
622 "configure private",
623 "Configuration from vty interface\n"
624 "Configure using a private candidate configuration\n")
625{
f344c66e 626 return vty_config_enter(vty, true, false);
1c2facd1
RW
627}
628
629DEFPY (config_commit,
630 config_commit_cmd,
fbdc1c0a 631 "commit [{force$force|confirmed (1-60)}]",
1c2facd1 632 "Commit changes into the running configuration\n"
fbdc1c0a
RW
633 "Force commit even if the candidate is outdated\n"
634 "Rollback this commit unless there is a confirming commit\n"
635 "Timeout in minutes for the commit to be confirmed\n")
1c2facd1 636{
fbdc1c0a 637 return nb_cli_commit(vty, !!force, confirmed, NULL);
1c2facd1
RW
638}
639
640DEFPY (config_commit_comment,
641 config_commit_comment_cmd,
fbdc1c0a 642 "commit [{force$force|confirmed (1-60)}] comment LINE...",
1c2facd1
RW
643 "Commit changes into the running configuration\n"
644 "Force commit even if the candidate is outdated\n"
fbdc1c0a
RW
645 "Rollback this commit unless there is a confirming commit\n"
646 "Timeout in minutes for the commit to be confirmed\n"
1c2facd1
RW
647 "Assign a comment to this commit\n"
648 "Comment for this commit (Max 80 characters)\n")
649{
650 char *comment;
651 int idx = 0;
652 int ret;
653
654 argv_find(argv, argc, "LINE", &idx);
655 comment = argv_concat(argv, argc, idx);
fbdc1c0a 656 ret = nb_cli_commit(vty, !!force, confirmed, comment);
1c2facd1
RW
657 XFREE(MTYPE_TMP, comment);
658
659 return ret;
660}
661
662DEFPY (config_commit_check,
663 config_commit_check_cmd,
664 "commit check",
665 "Commit changes into the running configuration\n"
666 "Check if the configuration changes are valid\n")
667{
668 int ret;
669
670 ret = nb_candidate_validate(vty->candidate_config);
671 if (ret != NB_OK) {
672 vty_out(vty,
673 "%% Failed to validate candidate configuration.\n\n");
674 vty_show_libyang_errors(vty, ly_native_ctx);
675 return CMD_WARNING;
676 }
677
678 vty_out(vty, "%% Candidate configuration validated successfully.\n\n");
679
680 return CMD_SUCCESS;
681}
682
683DEFPY (config_update,
684 config_update_cmd,
685 "update",
686 "Update candidate configuration\n")
687{
688 if (!nb_candidate_needs_update(vty->candidate_config)) {
689 vty_out(vty, "%% Update is not necessary.\n\n");
690 return CMD_SUCCESS;
691 }
692
693 if (nb_candidate_update(vty->candidate_config) != NB_OK) {
694 vty_out(vty,
695 "%% Failed to update the candidate configuration.\n\n");
696 vty_out(vty, "Please check the logs for more details.\n");
697 return CMD_WARNING;
698 }
699
83981138
RW
700 pthread_rwlock_rdlock(&running_config->lock);
701 {
702 nb_config_replace(vty->candidate_config_base, running_config,
703 true);
704 }
705 pthread_rwlock_unlock(&running_config->lock);
1c2facd1
RW
706
707 vty_out(vty, "%% Candidate configuration updated successfully.\n\n");
708
709 return CMD_SUCCESS;
710}
711
712DEFPY (config_discard,
713 config_discard_cmd,
714 "discard",
715 "Discard changes in the candidate configuration\n")
716{
717 nb_config_replace(vty->candidate_config, vty->candidate_config_base,
718 true);
719
720 return CMD_SUCCESS;
721}
722
723DEFPY (config_load,
724 config_load_cmd,
725 "configuration load\
726 <\
727 file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
728 |transaction (1-4294967296)$tid\
729 >\
730 [replace$replace]",
731 "Configuration related settings\n"
732 "Load configuration into candidate\n"
733 "Load configuration file into candidate\n"
734 "Load configuration file in JSON format\n"
735 "Load configuration file in XML format\n"
736 "Translate configuration file\n"
737 "YANG module translator\n"
738 "Configuration file name (full path)\n"
739 "Load configuration from transaction into candidate\n"
740 "Transaction ID\n"
741 "Replace instead of merge\n")
742{
743 if (filename) {
744 enum nb_cfg_format format;
745 struct yang_translator *translator = NULL;
746
747 if (json)
748 format = NB_CFG_FMT_JSON;
749 else if (xml)
750 format = NB_CFG_FMT_XML;
751 else
752 format = NB_CFG_FMT_CMDS;
753
754 if (translator_family) {
755 translator = yang_translator_find(translator_family);
756 if (!translator) {
757 vty_out(vty,
758 "%% Module translator \"%s\" not found\n",
759 translator_family);
760 return CMD_WARNING;
761 }
762 }
763
764 return nb_cli_candidate_load_file(vty, format, translator,
765 filename, !!replace);
766 }
767
768 return nb_cli_candidate_load_transaction(vty, tid, !!replace);
769}
770
771DEFPY (show_config_running,
772 show_config_running_cmd,
773 "show configuration running\
774 [<json$json|xml$xml> [translate WORD$translator_family]]\
775 [with-defaults$with_defaults]",
776 SHOW_STR
777 "Configuration information\n"
778 "Running configuration\n"
779 "Change output format to JSON\n"
780 "Change output format to XML\n"
781 "Translate output\n"
782 "YANG module translator\n"
783 "Show default values\n")
784
785{
786 enum nb_cfg_format format;
787 struct yang_translator *translator = NULL;
788
789 if (json)
790 format = NB_CFG_FMT_JSON;
791 else if (xml)
792 format = NB_CFG_FMT_XML;
793 else
794 format = NB_CFG_FMT_CMDS;
795
796 if (translator_family) {
797 translator = yang_translator_find(translator_family);
798 if (!translator) {
799 vty_out(vty, "%% Module translator \"%s\" not found\n",
800 translator_family);
801 return CMD_WARNING;
802 }
803 }
804
83981138
RW
805 pthread_rwlock_rdlock(&running_config->lock);
806 {
807 nb_cli_show_config(vty, running_config, format, translator,
808 !!with_defaults);
809 }
810 pthread_rwlock_unlock(&running_config->lock);
1c2facd1
RW
811
812 return CMD_SUCCESS;
813}
814
815DEFPY (show_config_candidate,
816 show_config_candidate_cmd,
817 "show configuration candidate\
818 [<json$json|xml$xml> [translate WORD$translator_family]]\
819 [<\
820 with-defaults$with_defaults\
821 |changes$changes\
822 >]",
823 SHOW_STR
824 "Configuration information\n"
825 "Candidate configuration\n"
826 "Change output format to JSON\n"
827 "Change output format to XML\n"
828 "Translate output\n"
829 "YANG module translator\n"
830 "Show default values\n"
831 "Show changes applied in the candidate configuration\n")
832
833{
834 enum nb_cfg_format format;
835 struct yang_translator *translator = NULL;
836
837 if (json)
838 format = NB_CFG_FMT_JSON;
839 else if (xml)
840 format = NB_CFG_FMT_XML;
841 else
842 format = NB_CFG_FMT_CMDS;
843
844 if (translator_family) {
845 translator = yang_translator_find(translator_family);
846 if (!translator) {
847 vty_out(vty, "%% Module translator \"%s\" not found\n",
848 translator_family);
849 return CMD_WARNING;
850 }
851 }
852
853 if (changes)
854 return nb_cli_show_config_compare(
855 vty, vty->candidate_config_base, vty->candidate_config,
856 format, translator);
857
858 nb_cli_show_config(vty, vty->candidate_config, format, translator,
859 !!with_defaults);
860
861 return CMD_SUCCESS;
862}
863
18bf258a
RW
864DEFPY (show_config_candidate_section,
865 show_config_candidate_section_cmd,
866 "show",
867 SHOW_STR)
868{
869 struct lyd_node *dnode;
870
871 /* Top-level configuration node, display everything. */
872 if (vty->xpath_index == 0)
873 return nb_cli_show_config(vty, vty->candidate_config,
874 NB_CFG_FMT_CMDS, NULL, false);
875
876 /* Display only the current section of the candidate configuration. */
877 dnode = yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH);
878 if (!dnode)
879 /* Shouldn't happen. */
880 return CMD_WARNING;
881
882 nb_cli_show_dnode_cmds(vty, dnode, 0);
883 vty_out(vty, "!\n");
884
885 return CMD_SUCCESS;
886}
887
1c2facd1
RW
888DEFPY (show_config_compare,
889 show_config_compare_cmd,
890 "show configuration compare\
891 <\
892 candidate$c1_candidate\
893 |running$c1_running\
894 |transaction (1-4294967296)$c1_tid\
895 >\
896 <\
897 candidate$c2_candidate\
898 |running$c2_running\
899 |transaction (1-4294967296)$c2_tid\
900 >\
901 [<json$json|xml$xml> [translate WORD$translator_family]]",
902 SHOW_STR
903 "Configuration information\n"
904 "Compare two different configurations\n"
905 "Candidate configuration\n"
906 "Running configuration\n"
907 "Configuration transaction\n"
908 "Transaction ID\n"
909 "Candidate configuration\n"
910 "Running configuration\n"
911 "Configuration transaction\n"
912 "Transaction ID\n"
913 "Change output format to JSON\n"
914 "Change output format to XML\n"
915 "Translate output\n"
916 "YANG module translator\n")
917{
918 enum nb_cfg_format format;
919 struct yang_translator *translator = NULL;
920 struct nb_config *config1, *config_transaction1 = NULL;
921 struct nb_config *config2, *config_transaction2 = NULL;
922 int ret = CMD_WARNING;
923
83981138
RW
924 /*
925 * For simplicity, lock the running configuration regardless if it's
926 * going to be used or not.
927 */
928 pthread_rwlock_rdlock(&running_config->lock);
929 {
930 if (c1_candidate)
931 config1 = vty->candidate_config;
932 else if (c1_running)
933 config1 = running_config;
934 else {
935 config_transaction1 = nb_db_transaction_load(c1_tid);
936 if (!config_transaction1) {
937 vty_out(vty,
938 "%% Transaction %u does not exist\n\n",
939 (unsigned int)c1_tid);
940 goto exit;
941 }
942 config1 = config_transaction1;
1c2facd1 943 }
83981138
RW
944
945 if (c2_candidate)
946 config2 = vty->candidate_config;
947 else if (c2_running)
948 config2 = running_config;
949 else {
950 config_transaction2 = nb_db_transaction_load(c2_tid);
951 if (!config_transaction2) {
952 vty_out(vty,
953 "%% Transaction %u does not exist\n\n",
954 (unsigned int)c2_tid);
955 goto exit;
956 }
957 config2 = config_transaction2;
1c2facd1 958 }
1c2facd1 959
83981138
RW
960 if (json)
961 format = NB_CFG_FMT_JSON;
962 else if (xml)
963 format = NB_CFG_FMT_XML;
964 else
965 format = NB_CFG_FMT_CMDS;
1c2facd1 966
83981138
RW
967 if (translator_family) {
968 translator = yang_translator_find(translator_family);
969 if (!translator) {
970 vty_out(vty,
971 "%% Module translator \"%s\" not found\n",
972 translator_family);
973 goto exit;
974 }
1c2facd1 975 }
1c2facd1 976
83981138
RW
977 ret = nb_cli_show_config_compare(vty, config1, config2, format,
978 translator);
979 exit:
980 if (config_transaction1)
981 nb_config_free(config_transaction1);
982 if (config_transaction2)
983 nb_config_free(config_transaction2);
984 }
985 pthread_rwlock_unlock(&running_config->lock);
1c2facd1
RW
986
987 return ret;
988}
989
990/*
991 * Stripped down version of the "show configuration compare" command.
992 * The "candidate" option is not present so the command can be installed in
993 * the enable node.
994 */
995ALIAS (show_config_compare,
996 show_config_compare_without_candidate_cmd,
997 "show configuration compare\
998 <\
999 running$c1_running\
1000 |transaction (1-4294967296)$c1_tid\
1001 >\
1002 <\
1003 running$c2_running\
1004 |transaction (1-4294967296)$c2_tid\
1005 >\
1006 [<json$json|xml$xml> [translate WORD$translator_family]]",
1007 SHOW_STR
1008 "Configuration information\n"
1009 "Compare two different configurations\n"
1010 "Running configuration\n"
1011 "Configuration transaction\n"
1012 "Transaction ID\n"
1013 "Running configuration\n"
1014 "Configuration transaction\n"
1015 "Transaction ID\n"
1016 "Change output format to JSON\n"
1017 "Change output format to XML\n"
1018 "Translate output\n"
1019 "YANG module translator\n")
1020
1021DEFPY (clear_config_transactions,
1022 clear_config_transactions_cmd,
1023 "clear configuration transactions oldest (1-100)$n",
1024 CLEAR_STR
1025 "Configuration activity\n"
1026 "Delete transactions from the transactions log\n"
1027 "Delete oldest <n> transactions\n"
1028 "Number of transactions to delete\n")
1029{
1030#ifdef HAVE_CONFIG_ROLLBACKS
1031 if (nb_db_clear_transactions(n) != NB_OK) {
1032 vty_out(vty, "%% Failed to delete transactions.\n\n");
1033 return CMD_WARNING;
1034 }
1035#else
1036 vty_out(vty,
1037 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1038#endif /* HAVE_CONFIG_ROLLBACKS */
1039
1040 return CMD_SUCCESS;
1041}
1042
1043DEFPY (config_database_max_transactions,
1044 config_database_max_transactions_cmd,
1045 "configuration database max-transactions (1-100)$max",
1046 "Configuration related settings\n"
1047 "Configuration database\n"
1048 "Set the maximum number of transactions to store\n"
1049 "Number of transactions\n")
1050{
1051#ifdef HAVE_CONFIG_ROLLBACKS
1052 if (nb_db_set_max_transactions(max) != NB_OK) {
1053 vty_out(vty,
1054 "%% Failed to update the maximum number of transactions.\n\n");
1055 return CMD_WARNING;
1056 }
1057 vty_out(vty,
1058 "%% Maximum number of transactions updated successfully.\n\n");
1059#else
1060 vty_out(vty,
1061 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1062#endif /* HAVE_CONFIG_ROLLBACKS */
1063
1064 return CMD_SUCCESS;
1065}
1066
1067DEFPY (yang_module_translator_load,
1068 yang_module_translator_load_cmd,
1069 "yang module-translator load FILENAME$filename",
1070 "YANG related settings\n"
1071 "YANG module translator\n"
1072 "Load YANG module translator\n"
1073 "File name (full path)\n")
1074{
1075 struct yang_translator *translator;
1076
1077 translator = yang_translator_load(filename);
1078 if (!translator) {
1079 vty_out(vty, "%% Failed to load \"%s\"\n\n", filename);
1080 vty_out(vty, "Please check the logs for more details.\n");
1081 return CMD_WARNING;
1082 }
1083
1084 vty_out(vty, "%% Module translator \"%s\" loaded successfully.\n\n",
1085 translator->family);
1086
1087 return CMD_SUCCESS;
1088}
1089
1090DEFPY (yang_module_translator_unload_family,
1091 yang_module_translator_unload_cmd,
1092 "yang module-translator unload WORD$translator_family",
1093 "YANG related settings\n"
1094 "YANG module translator\n"
1095 "Unload YANG module translator\n"
1096 "Name of the module translator\n")
1097{
1098 struct yang_translator *translator;
1099
1100 translator = yang_translator_find(translator_family);
1101 if (!translator) {
1102 vty_out(vty, "%% Module translator \"%s\" not found\n",
1103 translator_family);
1104 return CMD_WARNING;
1105 }
1106
1107 yang_translator_unload(translator);
1108
1109 return CMD_SUCCESS;
1110}
1111
1112#ifdef HAVE_CONFIG_ROLLBACKS
1113static void nb_cli_show_transactions_cb(void *arg, int transaction_id,
1114 const char *client_name,
1115 const char *date, const char *comment)
1116{
1117 struct ttable *tt = arg;
1118
1119 ttable_add_row(tt, "%d|%s|%s|%s", transaction_id, client_name, date,
1120 comment);
1121}
1122
1123static int nb_cli_show_transactions(struct vty *vty)
1124{
1125 struct ttable *tt;
1126
1127 /* Prepare table. */
1128 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
1129 ttable_add_row(tt, "Transaction ID|Client|Date|Comment");
1130 tt->style.cell.rpad = 2;
1131 tt->style.corner = '+';
1132 ttable_restyle(tt);
1133 ttable_rowseps(tt, 0, BOTTOM, true, '-');
1134
1135 /* Fetch transactions from the northbound database. */
1136 if (nb_db_transactions_iterate(nb_cli_show_transactions_cb, tt)
1137 != NB_OK) {
1138 vty_out(vty,
1139 "%% Failed to fetch configuration transactions.\n");
1140 return CMD_WARNING;
1141 }
1142
1143 /* Dump the generated table. */
1144 if (tt->nrows > 1) {
1145 char *table;
1146
1147 table = ttable_dump(tt, "\n");
1148 vty_out(vty, "%s\n", table);
1149 XFREE(MTYPE_TMP, table);
1150 } else
1151 vty_out(vty, "No configuration transactions to display.\n\n");
1152
1153 ttable_del(tt);
1154
1155 return CMD_SUCCESS;
1156}
1157#endif /* HAVE_CONFIG_ROLLBACKS */
1158
1159DEFPY (show_config_transaction,
1160 show_config_transaction_cmd,
1161 "show configuration transaction\
1162 [\
1163 (1-4294967296)$transaction_id\
1164 [<json$json|xml$xml> [translate WORD$translator_family]]\
1165 [<\
1166 with-defaults$with_defaults\
1167 |changes$changes\
1168 >]\
1169 ]",
1170 SHOW_STR
1171 "Configuration information\n"
1172 "Configuration transaction\n"
1173 "Transaction ID\n"
1174 "Change output format to JSON\n"
1175 "Change output format to XML\n"
1176 "Translate output\n"
1177 "YANG module translator\n"
1178 "Show default values\n"
1179 "Show changes compared to the previous transaction\n")
1180{
1181#ifdef HAVE_CONFIG_ROLLBACKS
1182 if (transaction_id) {
1183 struct nb_config *config;
1184 enum nb_cfg_format format;
1185 struct yang_translator *translator = NULL;
1186
1187 if (json)
1188 format = NB_CFG_FMT_JSON;
1189 else if (xml)
1190 format = NB_CFG_FMT_XML;
1191 else
1192 format = NB_CFG_FMT_CMDS;
1193
1194 if (translator_family) {
1195 translator = yang_translator_find(translator_family);
1196 if (!translator) {
1197 vty_out(vty,
1198 "%% Module translator \"%s\" not found\n",
1199 translator_family);
1200 return CMD_WARNING;
1201 }
1202 }
1203
1204 config = nb_db_transaction_load(transaction_id);
1205 if (!config) {
1206 vty_out(vty, "%% Transaction %u does not exist.\n\n",
1207 (unsigned int)transaction_id);
1208 return CMD_WARNING;
1209 }
1210
1211 if (changes) {
1212 struct nb_config *prev_config;
1213 int ret;
1214
1215 /* NOTE: this can be NULL. */
1216 prev_config =
1217 nb_db_transaction_load(transaction_id - 1);
1218
1219 ret = nb_cli_show_config_compare(
1220 vty, prev_config, config, format, translator);
1221 if (prev_config)
1222 nb_config_free(prev_config);
1223 nb_config_free(config);
1224
1225 return ret;
1226 }
1227
1228 nb_cli_show_config(vty, config, format, translator,
1229 !!with_defaults);
1230 nb_config_free(config);
1231
1232 return CMD_SUCCESS;
1233 }
1234
1235 return nb_cli_show_transactions(vty);
1236#else
1237 vty_out(vty,
1238 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1239 return CMD_WARNING;
1240#endif /* HAVE_CONFIG_ROLLBACKS */
1241}
1242
1a4bc045
RW
1243static int nb_cli_oper_data_cb(const struct lys_node *snode,
1244 struct yang_translator *translator,
1245 struct yang_data *data, void *arg)
1246{
1247 struct lyd_node *dnode = arg;
1248 struct ly_ctx *ly_ctx;
1249
1250 if (translator) {
1251 int ret;
1252
1253 ret = yang_translate_xpath(translator,
1254 YANG_TRANSLATE_FROM_NATIVE,
1255 data->xpath, sizeof(data->xpath));
1256 switch (ret) {
1257 case YANG_TRANSLATE_SUCCESS:
1258 break;
1259 case YANG_TRANSLATE_NOTFOUND:
1260 goto exit;
1261 case YANG_TRANSLATE_FAILURE:
1262 goto error;
1263 }
1264
1265 ly_ctx = translator->ly_ctx;
1266 } else
1267 ly_ctx = ly_native_ctx;
1268
1269 ly_errno = 0;
1270 dnode = lyd_new_path(dnode, ly_ctx, data->xpath, (void *)data->value, 0,
1271 LYD_PATH_OPT_UPDATE);
1272 if (!dnode && ly_errno) {
1273 flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
1274 __func__);
1275 goto error;
1276 }
1277
1278exit:
1279 yang_data_free(data);
1280 return NB_OK;
1281
1282error:
1283 yang_data_free(data);
1284 return NB_ERR;
1285}
1286
1287DEFPY (show_yang_operational_data,
1288 show_yang_operational_data_cmd,
1289 "show yang operational-data XPATH$xpath\
1290 [{\
1291 format <json$json|xml$xml>\
1292 |translate WORD$translator_family\
1293 }]",
1294 SHOW_STR
1295 "YANG information\n"
1296 "Show YANG operational data\n"
1297 "XPath expression specifying the YANG data path\n"
1298 "Set the output format\n"
1299 "JavaScript Object Notation\n"
1300 "Extensible Markup Language\n"
1301 "Translate operational data\n"
1302 "YANG module translator\n")
1303{
1304 LYD_FORMAT format;
1305 struct yang_translator *translator = NULL;
1306 struct ly_ctx *ly_ctx;
1307 struct lyd_node *dnode;
1308 char *strp;
1309
1310 if (xml)
1311 format = LYD_XML;
1312 else
1313 format = LYD_JSON;
1314
1315 if (translator_family) {
1316 translator = yang_translator_find(translator_family);
1317 if (!translator) {
1318 vty_out(vty, "%% Module translator \"%s\" not found\n",
1319 translator_family);
1320 return CMD_WARNING;
1321 }
1322
1323 ly_ctx = translator->ly_ctx;
1324 } else
1325 ly_ctx = ly_native_ctx;
1326
1327 /* Obtain data. */
1328 dnode = yang_dnode_new(ly_ctx, false);
1329 if (nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb,
1330 dnode)
1331 != NB_OK) {
1332 vty_out(vty, "%% Failed to fetch operational data.\n");
1333 yang_dnode_free(dnode);
1334 return CMD_WARNING;
1335 }
37345802 1336 lyd_validate(&dnode, LYD_OPT_GET, ly_ctx);
1a4bc045
RW
1337
1338 /* Display the data. */
1339 if (lyd_print_mem(&strp, dnode, format,
1340 LYP_FORMAT | LYP_WITHSIBLINGS | LYP_WD_ALL)
1341 != 0
1342 || !strp) {
1343 vty_out(vty, "%% Failed to display operational data.\n");
1344 yang_dnode_free(dnode);
1345 return CMD_WARNING;
1346 }
1347 vty_out(vty, "%s", strp);
1348 free(strp);
1349 yang_dnode_free(dnode);
1350
1351 return CMD_SUCCESS;
1352}
1353
1c2facd1
RW
1354DEFPY (show_yang_module,
1355 show_yang_module_cmd,
1356 "show yang module [module-translator WORD$translator_family]",
1357 SHOW_STR
1358 "YANG information\n"
1359 "Show loaded modules\n"
1360 "YANG module translator\n"
1361 "YANG module translator\n")
1362{
1363 struct ly_ctx *ly_ctx;
1364 struct yang_translator *translator = NULL;
1365 const struct lys_module *module;
1366 struct ttable *tt;
1367 uint32_t idx = 0;
1368
1369 if (translator_family) {
1370 translator = yang_translator_find(translator_family);
1371 if (!translator) {
1372 vty_out(vty, "%% Module translator \"%s\" not found\n",
1373 translator_family);
1374 return CMD_WARNING;
1375 }
1376 ly_ctx = translator->ly_ctx;
1377 } else
1378 ly_ctx = ly_native_ctx;
1379
1380 /* Prepare table. */
1381 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
1382 ttable_add_row(tt, "Module|Version|Revision|Flags|Namespace");
1383 tt->style.cell.rpad = 2;
1384 tt->style.corner = '+';
1385 ttable_restyle(tt);
1386 ttable_rowseps(tt, 0, BOTTOM, true, '-');
1387
1388 while ((module = ly_ctx_get_module_iter(ly_ctx, &idx))) {
1389 char flags[8];
1390
1391 snprintf(flags, sizeof(flags), "%c%c",
1392 module->implemented ? 'I' : ' ',
1393 (module->deviated == 1) ? 'D' : ' ');
1394
1395 ttable_add_row(tt, "%s|%s|%s|%s|%s", module->name,
1396 (module->version == 2) ? "1.1" : "1.0",
1397 (module->rev_size > 0) ? module->rev[0].date
1398 : "-",
1399 flags, module->ns);
1400 }
1401
1402 /* Dump the generated table. */
1403 if (tt->nrows > 1) {
1404 char *table;
1405
1406 vty_out(vty, " Flags: I - Implemented, D - Deviated\n\n");
1407
1408 table = ttable_dump(tt, "\n");
1409 vty_out(vty, "%s\n", table);
1410 XFREE(MTYPE_TMP, table);
1411 } else
1412 vty_out(vty, "No YANG modules to display.\n\n");
1413
1414 ttable_del(tt);
1415
1416 return CMD_SUCCESS;
1417}
1418
1419DEFPY (show_yang_module_detail,
1420 show_yang_module_detail_cmd,
1421 "show yang module\
1422 [module-translator WORD$translator_family]\
1423 WORD$module_name <summary|tree$tree|yang$yang|yin$yin>",
1424 SHOW_STR
1425 "YANG information\n"
1426 "Show loaded modules\n"
1427 "YANG module translator\n"
1428 "YANG module translator\n"
1429 "Module name\n"
1430 "Display summary information about the module\n"
1431 "Display module in the tree (RFC 8340) format\n"
1432 "Display module in the YANG format\n"
1433 "Display module in the YIN format\n")
1434{
1435 struct ly_ctx *ly_ctx;
1436 struct yang_translator *translator = NULL;
1437 const struct lys_module *module;
1438 LYS_OUTFORMAT format;
1439 char *strp;
1440
1441 if (translator_family) {
1442 translator = yang_translator_find(translator_family);
1443 if (!translator) {
1444 vty_out(vty, "%% Module translator \"%s\" not found\n",
1445 translator_family);
1446 return CMD_WARNING;
1447 }
1448 ly_ctx = translator->ly_ctx;
1449 } else
1450 ly_ctx = ly_native_ctx;
1451
1452 module = ly_ctx_get_module(ly_ctx, module_name, NULL, 0);
1453 if (!module) {
1454 vty_out(vty, "%% Module \"%s\" not found\n", module_name);
1455 return CMD_WARNING;
1456 }
1457
1458 if (yang)
1459 format = LYS_OUT_YANG;
1460 else if (yin)
1461 format = LYS_OUT_YIN;
1462 else if (tree)
1463 format = LYS_OUT_TREE;
1464 else
1465 format = LYS_OUT_INFO;
1466
1467 if (lys_print_mem(&strp, module, format, NULL, 0, 0) == 0) {
1468 vty_out(vty, "%s\n", strp);
1469 free(strp);
1470 } else {
1471 /* Unexpected. */
1472 vty_out(vty, "%% Error generating module information\n");
1473 return CMD_WARNING;
1474 }
1475
1476 return CMD_SUCCESS;
1477}
1478
1479DEFPY (show_yang_module_translator,
1480 show_yang_module_translator_cmd,
1481 "show yang module-translator",
1482 SHOW_STR
1483 "YANG information\n"
1484 "Show loaded YANG module translators\n")
1485{
1486 struct yang_translator *translator;
1487 struct ttable *tt;
1488
1489 /* Prepare table. */
1490 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
1491 ttable_add_row(tt, "Family|Module|Deviations|Coverage (%%)");
1492 tt->style.cell.rpad = 2;
1493 tt->style.corner = '+';
1494 ttable_restyle(tt);
1495 ttable_rowseps(tt, 0, BOTTOM, true, '-');
1496
1497 RB_FOREACH (translator, yang_translators, &yang_translators) {
1498 struct yang_tmodule *tmodule;
1499 struct listnode *ln;
1500
1501 for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
1502 ttable_add_row(tt, "%s|%s|%s|%.2f", translator->family,
1503 tmodule->module->name,
1504 tmodule->deviations->name,
1505 tmodule->coverage);
1506 }
1507 }
1508
1509 /* Dump the generated table. */
1510 if (tt->nrows > 1) {
1511 char *table;
1512
1513 table = ttable_dump(tt, "\n");
1514 vty_out(vty, "%s\n", table);
1515 XFREE(MTYPE_TMP, table);
1516 } else
1517 vty_out(vty, "No YANG module translators to display.\n\n");
1518
1519 ttable_del(tt);
1520
1521 return CMD_SUCCESS;
1522}
1523
1524#ifdef HAVE_CONFIG_ROLLBACKS
1525static int nb_cli_rollback_configuration(struct vty *vty,
1526 uint32_t transaction_id)
1527{
1528 struct nb_config *candidate;
1529 char comment[80];
1530 int ret;
1531
1532 candidate = nb_db_transaction_load(transaction_id);
1533 if (!candidate) {
1534 vty_out(vty, "%% Transaction %u does not exist.\n\n",
1535 transaction_id);
1536 return CMD_WARNING;
1537 }
1538
1539 snprintf(comment, sizeof(comment), "Rollback to transaction %u",
1540 transaction_id);
1541
364ad673 1542 ret = nb_candidate_commit(candidate, NB_CLIENT_CLI, vty, true, comment,
1c2facd1
RW
1543 NULL);
1544 nb_config_free(candidate);
1545 switch (ret) {
1546 case NB_OK:
1547 vty_out(vty,
1548 "%% Configuration was successfully rolled back.\n\n");
1549 return CMD_SUCCESS;
1550 case NB_ERR_NO_CHANGES:
1551 vty_out(vty,
1552 "%% Aborting - no configuration changes detected.\n\n");
1553 return CMD_WARNING;
1554 default:
1555 vty_out(vty, "%% Rollback failed.\n\n");
1556 vty_out(vty, "Please check the logs for more details.\n");
1557 return CMD_WARNING;
1558 }
1559}
1560#endif /* HAVE_CONFIG_ROLLBACKS */
1561
1562DEFPY (rollback_config,
1563 rollback_config_cmd,
1564 "rollback configuration (1-4294967296)$transaction_id",
1565 "Rollback to a previous state\n"
1566 "Running configuration\n"
1567 "Transaction ID\n")
1568{
1569#ifdef HAVE_CONFIG_ROLLBACKS
1570 return nb_cli_rollback_configuration(vty, transaction_id);
1571#else
1572 vty_out(vty,
1573 "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1574 return CMD_SUCCESS;
1575#endif /* HAVE_CONFIG_ROLLBACKS */
1576}
1577
1578/* Debug CLI commands. */
9eb2c0a1
RW
1579static struct debug *nb_debugs[] = {
1580 &nb_dbg_cbs_config, &nb_dbg_cbs_state, &nb_dbg_cbs_rpc,
1581 &nb_dbg_notif, &nb_dbg_events,
1582};
1583
1584static const char *const nb_debugs_conflines[] = {
1585 "debug northbound callbacks configuration",
1586 "debug northbound callbacks state",
1587 "debug northbound callbacks rpc",
1588 "debug northbound notifications",
1589 "debug northbound events",
1590};
1591
1592DEFINE_HOOK(nb_client_debug_set_all, (uint32_t flags, bool set), (flags, set));
1593
1594static void nb_debug_set_all(uint32_t flags, bool set)
1c2facd1 1595{
9eb2c0a1
RW
1596 for (unsigned int i = 0; i < array_size(nb_debugs); i++) {
1597 DEBUG_FLAGS_SET(nb_debugs[i], flags, set);
1c2facd1 1598
9eb2c0a1
RW
1599 /* If all modes have been turned off, don't preserve options. */
1600 if (!DEBUG_MODE_CHECK(nb_debugs[i], DEBUG_MODE_ALL))
1601 DEBUG_CLEAR(nb_debugs[i]);
1602 }
1603
1604 hook_call(nb_client_debug_set_all, flags, set);
1c2facd1
RW
1605}
1606
9eb2c0a1
RW
1607DEFPY (debug_nb,
1608 debug_nb_cmd,
1609 "[no] debug northbound\
1610 [<\
1611 callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\
1612 |notifications$notifications\
1613 |events$events\
1614 >]",
1615 NO_STR
1616 DEBUG_STR
1617 "Northbound debugging\n"
1618 "Callbacks\n"
1619 "Configuration\n"
1620 "State\n"
1621 "RPC\n"
1622 "Notifications\n"
1623 "Events\n")
1c2facd1 1624{
9eb2c0a1
RW
1625 uint32_t mode = DEBUG_NODE2MODE(vty->node);
1626
1627 if (cbs) {
1628 bool none = (!cbs_cfg && !cbs_state && !cbs_rpc);
1629
1630 if (none || cbs_cfg)
1631 DEBUG_MODE_SET(&nb_dbg_cbs_config, mode, !no);
1632 if (none || cbs_state)
1633 DEBUG_MODE_SET(&nb_dbg_cbs_state, mode, !no);
1634 if (none || cbs_rpc)
1635 DEBUG_MODE_SET(&nb_dbg_cbs_rpc, mode, !no);
1636 }
1637 if (notifications)
1638 DEBUG_MODE_SET(&nb_dbg_notif, mode, !no);
1639 if (events)
1640 DEBUG_MODE_SET(&nb_dbg_events, mode, !no);
1641
1642 /* no specific debug --> act on all of them */
1643 if (strmatch(argv[argc - 1]->text, "northbound"))
1644 nb_debug_set_all(mode, !no);
1c2facd1
RW
1645
1646 return CMD_SUCCESS;
1647}
1648
9eb2c0a1
RW
1649DEFINE_HOOK(nb_client_debug_config_write, (struct vty *vty), (vty));
1650
1c2facd1
RW
1651static int nb_debug_config_write(struct vty *vty)
1652{
9eb2c0a1
RW
1653 for (unsigned int i = 0; i < array_size(nb_debugs); i++)
1654 if (DEBUG_MODE_CHECK(nb_debugs[i], DEBUG_MODE_CONF))
1655 vty_out(vty, "%s\n", nb_debugs_conflines[i]);
1656
1657 hook_call(nb_client_debug_config_write, vty);
1c2facd1
RW
1658
1659 return 1;
1660}
1661
9eb2c0a1 1662static struct debug_callbacks nb_dbg_cbs = {.debug_set_all = nb_debug_set_all};
1c2facd1
RW
1663static struct cmd_node nb_debug_node = {NORTHBOUND_DEBUG_NODE, "", 1};
1664
1665void nb_cli_install_default(int node)
1666{
18bf258a
RW
1667 install_element(node, &show_config_candidate_section_cmd);
1668
1c2facd1
RW
1669 if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL)
1670 return;
1671
1672 install_element(node, &config_commit_cmd);
1673 install_element(node, &config_commit_comment_cmd);
1674 install_element(node, &config_commit_check_cmd);
1675 install_element(node, &config_update_cmd);
1676 install_element(node, &config_discard_cmd);
1677 install_element(node, &show_config_running_cmd);
1678 install_element(node, &show_config_candidate_cmd);
1679 install_element(node, &show_config_compare_cmd);
1680 install_element(node, &show_config_transaction_cmd);
1681}
1682
1683/* YANG module autocomplete. */
1684static void yang_module_autocomplete(vector comps, struct cmd_token *token)
1685{
1686 const struct lys_module *module;
1687 struct yang_translator *module_tr;
1688 uint32_t idx;
1689
1690 idx = 0;
1691 while ((module = ly_ctx_get_module_iter(ly_native_ctx, &idx)))
1692 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, module->name));
1693
1694 RB_FOREACH (module_tr, yang_translators, &yang_translators) {
1695 idx = 0;
1696 while ((module = ly_ctx_get_module_iter(module_tr->ly_ctx,
1697 &idx)))
1698 vector_set(comps,
1699 XSTRDUP(MTYPE_COMPLETION, module->name));
1700 }
1701}
1702
1703/* YANG module translator autocomplete. */
1704static void yang_translator_autocomplete(vector comps, struct cmd_token *token)
1705{
1706 struct yang_translator *module_tr;
1707
1708 RB_FOREACH (module_tr, yang_translators, &yang_translators)
1709 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, module_tr->family));
1710}
1711
1712static const struct cmd_variable_handler yang_var_handlers[] = {
1713 {.varname = "module_name", .completions = yang_module_autocomplete},
1714 {.varname = "translator_family",
1715 .completions = yang_translator_autocomplete},
1716 {.completions = NULL}};
1717
fbdc1c0a 1718void nb_cli_init(struct thread_master *tm)
1c2facd1 1719{
fbdc1c0a
RW
1720 master = tm;
1721
1c2facd1
RW
1722 /* Initialize the shared candidate configuration. */
1723 vty_shared_candidate_config = nb_config_new(NULL);
1724
1725 /* Install debug commands */
9eb2c0a1 1726 debug_init(&nb_dbg_cbs);
1c2facd1
RW
1727 install_node(&nb_debug_node, nb_debug_config_write);
1728 install_element(ENABLE_NODE, &debug_nb_cmd);
1c2facd1 1729 install_element(CONFIG_NODE, &debug_nb_cmd);
1c2facd1
RW
1730
1731 /* Install commands specific to the transaction-base mode. */
1732 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) {
1733 install_element(ENABLE_NODE, &config_exclusive_cmd);
1734 install_element(ENABLE_NODE, &config_private_cmd);
1735 install_element(ENABLE_NODE, &show_config_running_cmd);
1736 install_element(ENABLE_NODE,
1737 &show_config_compare_without_candidate_cmd);
1738 install_element(ENABLE_NODE, &show_config_transaction_cmd);
1739 install_element(ENABLE_NODE, &rollback_config_cmd);
1740 install_element(ENABLE_NODE, &clear_config_transactions_cmd);
1741
1742 install_element(CONFIG_NODE, &config_load_cmd);
1743 install_element(CONFIG_NODE,
1744 &config_database_max_transactions_cmd);
1745 }
1746
1747 /* Other commands. */
1748 install_element(CONFIG_NODE, &yang_module_translator_load_cmd);
1749 install_element(CONFIG_NODE, &yang_module_translator_unload_cmd);
1a4bc045 1750 install_element(ENABLE_NODE, &show_yang_operational_data_cmd);
1c2facd1
RW
1751 install_element(ENABLE_NODE, &show_yang_module_cmd);
1752 install_element(ENABLE_NODE, &show_yang_module_detail_cmd);
1753 install_element(ENABLE_NODE, &show_yang_module_translator_cmd);
1754 cmd_variable_handler_register(yang_var_handlers);
1755}
1756
1757void nb_cli_terminate(void)
1758{
1759 nb_config_free(vty_shared_candidate_config);
1760}