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