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