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