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