Coverage Report

Created: 2026-09-14 07:13

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