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