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