Line data Source code
1 : /*
2 : * Copyright (C) 2018 NetDEF, Inc.
3 : * Renato Westphal
4 : *
5 : * This program is free software; you can redistribute it and/or modify it
6 : * under the terms of the GNU General Public License as published by the Free
7 : * Software Foundation; either version 2 of the License, or (at your option)
8 : * any later version.
9 : *
10 : * This program is distributed in the hope that it will be useful, but WITHOUT
11 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 : * more details.
14 : *
15 : * You should have received a copy of the GNU General Public License along
16 : * with this program; see the file COPYING; if not, write to the Free Software
17 : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 : */
19 :
20 : #include <zebra.h>
21 :
22 : #include "libfrr.h"
23 : #include "lib/version.h"
24 : #include "defaults.h"
25 : #include "log.h"
26 : #include "lib_errors.h"
27 : #include "command.h"
28 : #include "termtable.h"
29 : #include "db.h"
30 : #include "debug.h"
31 : #include "yang_translator.h"
32 : #include "northbound.h"
33 : #include "northbound_cli.h"
34 : #include "northbound_db.h"
35 : #include "lib/northbound_cli_clippy.c"
36 :
37 : struct debug nb_dbg_cbs_config = {0, "Northbound callbacks: configuration"};
38 : struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"};
39 : struct debug nb_dbg_cbs_rpc = {0, "Northbound callbacks: RPCs"};
40 : struct debug nb_dbg_notif = {0, "Northbound notifications"};
41 : struct debug nb_dbg_events = {0, "Northbound events"};
42 : struct debug nb_dbg_libyang = {0, "libyang debugging"};
43 :
44 : struct nb_config *vty_shared_candidate_config;
45 : static struct thread_master *master;
46 :
47 0 : static void vty_show_nb_errors(struct vty *vty, int error, const char *errmsg)
48 : {
49 0 : vty_out(vty, "Error type: %s\n", nb_err_name(error));
50 0 : if (strlen(errmsg) > 0)
51 0 : vty_out(vty, "Error description: %s\n", errmsg);
52 0 : }
53 :
54 60 : static int nb_cli_classic_commit(struct vty *vty)
55 : {
56 60 : struct nb_context context = {};
57 60 : char errmsg[BUFSIZ] = {0};
58 60 : int ret;
59 :
60 60 : context.client = NB_CLIENT_CLI;
61 60 : context.user = vty;
62 60 : ret = nb_candidate_commit(&context, vty->candidate_config, true, NULL,
63 : NULL, errmsg, sizeof(errmsg));
64 60 : switch (ret) {
65 60 : case NB_OK:
66 : /* Successful commit. Print warnings (if any). */
67 60 : if (strlen(errmsg) > 0)
68 0 : vty_out(vty, "%s\n", errmsg);
69 : break;
70 : case NB_ERR_NO_CHANGES:
71 : break;
72 0 : default:
73 0 : vty_out(vty, "%% Configuration failed.\n\n");
74 0 : vty_show_nb_errors(vty, ret, errmsg);
75 0 : if (vty->pending_commit)
76 0 : 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 0 : nb_config_replace(vty->candidate_config, running_config, true);
82 0 : return CMD_WARNING_CONFIG_FAILED;
83 : }
84 :
85 : return CMD_SUCCESS;
86 : }
87 :
88 0 : static void nb_cli_pending_commit_clear(struct vty *vty)
89 : {
90 0 : vty->pending_commit = 0;
91 0 : XFREE(MTYPE_TMP, vty->pending_cmds_buf);
92 0 : vty->pending_cmds_buflen = 0;
93 0 : vty->pending_cmds_bufpos = 0;
94 0 : }
95 :
96 284 : int nb_cli_pending_commit_check(struct vty *vty)
97 : {
98 284 : int ret = CMD_SUCCESS;
99 :
100 284 : if (vty->pending_commit) {
101 0 : ret = nb_cli_classic_commit(vty);
102 0 : nb_cli_pending_commit_clear(vty);
103 : }
104 :
105 284 : return ret;
106 : }
107 :
108 0 : static int nb_cli_schedule_command(struct vty *vty)
109 : {
110 : /* Append command to dynamically sized buffer of scheduled commands. */
111 0 : if (!vty->pending_cmds_buf) {
112 0 : vty->pending_cmds_buflen = 4096;
113 0 : vty->pending_cmds_buf =
114 0 : XCALLOC(MTYPE_TMP, vty->pending_cmds_buflen);
115 : }
116 0 : if ((strlen(vty->buf) + 3)
117 0 : > (vty->pending_cmds_buflen - vty->pending_cmds_bufpos)) {
118 0 : vty->pending_cmds_buflen *= 2;
119 0 : vty->pending_cmds_buf =
120 0 : XREALLOC(MTYPE_TMP, vty->pending_cmds_buf,
121 : vty->pending_cmds_buflen);
122 : }
123 0 : strlcat(vty->pending_cmds_buf, "- ", vty->pending_cmds_buflen);
124 0 : vty->pending_cmds_bufpos = strlcat(vty->pending_cmds_buf, vty->buf,
125 : vty->pending_cmds_buflen);
126 :
127 : /* Schedule the commit operation. */
128 0 : vty->pending_commit = 1;
129 :
130 0 : return CMD_SUCCESS;
131 : }
132 :
133 60 : void nb_cli_enqueue_change(struct vty *vty, const char *xpath,
134 : enum nb_operation operation, const char *value)
135 : {
136 60 : struct vty_cfg_change *change;
137 :
138 60 : if (vty->num_cfg_changes == VTY_MAXCFGCHANGES) {
139 : /* Not expected to happen. */
140 0 : vty_out(vty,
141 : "%% Exceeded the maximum number of changes (%u) for a single command\n\n",
142 : VTY_MAXCFGCHANGES);
143 0 : return;
144 : }
145 :
146 60 : change = &vty->cfg_changes[vty->num_cfg_changes++];
147 60 : strlcpy(change->xpath, xpath, sizeof(change->xpath));
148 60 : change->operation = operation;
149 60 : change->value = value;
150 : }
151 :
152 60 : static int nb_cli_apply_changes_internal(struct vty *vty,
153 : const char *xpath_base,
154 : bool clear_pending)
155 : {
156 60 : bool error = false;
157 :
158 60 : if (xpath_base == NULL)
159 0 : xpath_base = "";
160 :
161 60 : VTY_CHECK_XPATH;
162 :
163 : /* Edit candidate configuration. */
164 120 : for (size_t i = 0; i < vty->num_cfg_changes; i++) {
165 60 : struct vty_cfg_change *change = &vty->cfg_changes[i];
166 60 : struct nb_node *nb_node;
167 60 : char xpath[XPATH_MAXLEN];
168 60 : struct yang_data *data;
169 60 : int ret;
170 :
171 : /* Handle relative XPaths. */
172 60 : memset(xpath, 0, sizeof(xpath));
173 60 : if (vty->xpath_index > 0
174 30 : && (xpath_base[0] == '.' || change->xpath[0] == '.'))
175 30 : strlcpy(xpath, VTY_CURR_XPATH, sizeof(xpath));
176 60 : if (xpath_base[0]) {
177 30 : if (xpath_base[0] == '.')
178 0 : strlcat(xpath, xpath_base + 1, sizeof(xpath));
179 : else
180 30 : strlcat(xpath, xpath_base, sizeof(xpath));
181 : }
182 60 : if (change->xpath[0] == '.')
183 60 : strlcat(xpath, change->xpath + 1, sizeof(xpath));
184 : else
185 0 : strlcpy(xpath, change->xpath, sizeof(xpath));
186 :
187 : /* Find the northbound node associated to the data path. */
188 60 : nb_node = nb_node_find(xpath);
189 60 : if (!nb_node) {
190 0 : flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
191 : "%s: unknown data path: %s", __func__, xpath);
192 0 : error = true;
193 0 : continue;
194 : }
195 :
196 : /* If the value is not set, get the default if it exists. */
197 60 : if (change->value == NULL)
198 30 : change->value = yang_snode_get_default(nb_node->snode);
199 60 : data = yang_data_new(xpath, change->value);
200 :
201 : /*
202 : * Ignore "not found" errors when editing the candidate
203 : * configuration.
204 : */
205 60 : ret = nb_candidate_edit(vty->candidate_config, nb_node,
206 : change->operation, xpath, NULL, data);
207 60 : yang_data_free(data);
208 60 : if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) {
209 0 : 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 0 : error = true;
215 0 : continue;
216 : }
217 : }
218 :
219 60 : if (error) {
220 0 : char buf[BUFSIZ];
221 :
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 0 : vty_out(vty, "%% Failed to edit configuration.\n\n");
228 0 : vty_out(vty, "%s",
229 : yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
230 : }
231 :
232 : /*
233 : * Maybe do an implicit commit when using the classic CLI mode.
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 : */
241 60 : if (frr_get_cli_mode() == FRR_CLI_CLASSIC) {
242 60 : if (clear_pending) {
243 30 : if (vty->pending_commit)
244 0 : return nb_cli_pending_commit_check(vty);
245 30 : } else if (vty->pending_allowed)
246 0 : return nb_cli_schedule_command(vty);
247 60 : assert(!vty->pending_commit);
248 60 : return nb_cli_classic_commit(vty);
249 : }
250 :
251 : return CMD_SUCCESS;
252 : }
253 :
254 30 : int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
255 : {
256 30 : char xpath_base[XPATH_MAXLEN] = {};
257 :
258 : /* Parse the base XPath format string. */
259 30 : if (xpath_base_fmt) {
260 0 : va_list ap;
261 :
262 0 : va_start(ap, xpath_base_fmt);
263 0 : vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap);
264 0 : va_end(ap);
265 : }
266 30 : return nb_cli_apply_changes_internal(vty, xpath_base, false);
267 : }
268 :
269 30 : int nb_cli_apply_changes_clear_pending(struct vty *vty,
270 : const char *xpath_base_fmt, ...)
271 : {
272 30 : char xpath_base[XPATH_MAXLEN] = {};
273 :
274 : /* Parse the base XPath format string. */
275 30 : if (xpath_base_fmt) {
276 30 : va_list ap;
277 :
278 30 : va_start(ap, xpath_base_fmt);
279 30 : vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap);
280 30 : va_end(ap);
281 : }
282 30 : return nb_cli_apply_changes_internal(vty, xpath_base, true);
283 : }
284 :
285 0 : int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input,
286 : struct list *output)
287 : {
288 0 : struct nb_node *nb_node;
289 0 : int ret;
290 0 : char errmsg[BUFSIZ] = {0};
291 :
292 0 : nb_node = nb_node_find(xpath);
293 0 : if (!nb_node) {
294 0 : flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
295 : "%s: unknown data path: %s", __func__, xpath);
296 0 : return CMD_WARNING;
297 : }
298 :
299 0 : ret = nb_callback_rpc(nb_node, xpath, input, output, errmsg,
300 : sizeof(errmsg));
301 0 : switch (ret) {
302 : case NB_OK:
303 : return CMD_SUCCESS;
304 0 : default:
305 0 : if (strlen(errmsg))
306 0 : vty_show_nb_errors(vty, ret, errmsg);
307 : return CMD_WARNING;
308 : }
309 : }
310 :
311 0 : void nb_cli_confirmed_commit_clean(struct vty *vty)
312 : {
313 0 : thread_cancel(&vty->t_confirmed_commit_timeout);
314 0 : nb_config_free(vty->confirmed_commit_rollback);
315 0 : vty->confirmed_commit_rollback = NULL;
316 0 : }
317 :
318 0 : int nb_cli_confirmed_commit_rollback(struct vty *vty)
319 : {
320 0 : struct nb_context context = {};
321 0 : uint32_t transaction_id;
322 0 : char errmsg[BUFSIZ] = {0};
323 0 : int ret;
324 :
325 : /* Perform the rollback. */
326 0 : context.client = NB_CLIENT_CLI;
327 0 : context.user = vty;
328 0 : ret = nb_candidate_commit(
329 : &context, vty->confirmed_commit_rollback, true,
330 : "Rollback to previous configuration - confirmed commit has timed out",
331 : &transaction_id, errmsg, sizeof(errmsg));
332 0 : if (ret == NB_OK) {
333 0 : vty_out(vty,
334 : "Rollback performed successfully (Transaction ID #%u).\n",
335 : transaction_id);
336 : /* Print warnings (if any). */
337 0 : if (strlen(errmsg) > 0)
338 0 : vty_out(vty, "%s\n", errmsg);
339 : } else {
340 0 : vty_out(vty,
341 : "Failed to rollback to previous configuration.\n\n");
342 0 : vty_show_nb_errors(vty, ret, errmsg);
343 : }
344 :
345 0 : return ret;
346 : }
347 :
348 0 : static void nb_cli_confirmed_commit_timeout(struct thread *thread)
349 : {
350 0 : struct vty *vty = THREAD_ARG(thread);
351 :
352 : /* XXX: broadcast this message to all logged-in users? */
353 0 : vty_out(vty,
354 : "\nConfirmed commit has timed out, rolling back to previous configuration.\n\n");
355 :
356 0 : nb_cli_confirmed_commit_rollback(vty);
357 0 : nb_cli_confirmed_commit_clean(vty);
358 0 : }
359 :
360 0 : static int nb_cli_commit(struct vty *vty, bool force,
361 : unsigned int confirmed_timeout, char *comment)
362 : {
363 0 : struct nb_context context = {};
364 0 : uint32_t transaction_id = 0;
365 0 : char errmsg[BUFSIZ] = {0};
366 0 : int ret;
367 :
368 : /* Check if there's a pending confirmed commit. */
369 0 : if (vty->t_confirmed_commit_timeout) {
370 0 : if (confirmed_timeout) {
371 : /* Reset timeout if "commit confirmed" is used again. */
372 0 : vty_out(vty,
373 : "%% Resetting confirmed-commit timeout to %u minute(s)\n\n",
374 : confirmed_timeout);
375 :
376 0 : thread_cancel(&vty->t_confirmed_commit_timeout);
377 0 : 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 0 : vty_out(vty, "%% Commit complete.\n\n");
384 0 : nb_cli_confirmed_commit_clean(vty);
385 : }
386 0 : return CMD_SUCCESS;
387 : }
388 :
389 : /* "force" parameter. */
390 0 : if (!force && nb_candidate_needs_update(vty->candidate_config)) {
391 0 : vty_out(vty,
392 : "%% Candidate configuration needs to be updated before commit.\n\n");
393 0 : vty_out(vty,
394 : "Use the \"update\" command or \"commit force\".\n");
395 0 : return CMD_WARNING;
396 : }
397 :
398 : /* "confirm" parameter. */
399 0 : if (confirmed_timeout) {
400 0 : vty->confirmed_commit_rollback = nb_config_dup(running_config);
401 :
402 0 : vty->t_confirmed_commit_timeout = NULL;
403 0 : thread_add_timer(master, nb_cli_confirmed_commit_timeout, vty,
404 : confirmed_timeout * 60,
405 : &vty->t_confirmed_commit_timeout);
406 : }
407 :
408 0 : context.client = NB_CLIENT_CLI;
409 0 : context.user = vty;
410 0 : ret = nb_candidate_commit(&context, vty->candidate_config, true,
411 : comment, &transaction_id, errmsg,
412 : sizeof(errmsg));
413 :
414 : /* Map northbound return code to CLI return code. */
415 0 : switch (ret) {
416 0 : case NB_OK:
417 0 : nb_config_replace(vty->candidate_config_base, running_config,
418 : true);
419 0 : vty_out(vty,
420 : "%% Configuration committed successfully (Transaction ID #%u).\n\n",
421 : transaction_id);
422 : /* Print warnings (if any). */
423 0 : if (strlen(errmsg) > 0)
424 0 : vty_out(vty, "%s\n", errmsg);
425 : return CMD_SUCCESS;
426 0 : case NB_ERR_NO_CHANGES:
427 0 : vty_out(vty, "%% No configuration changes to commit.\n\n");
428 0 : return CMD_SUCCESS;
429 0 : default:
430 0 : vty_out(vty,
431 : "%% Failed to commit candidate configuration.\n\n");
432 0 : vty_show_nb_errors(vty, ret, errmsg);
433 0 : return CMD_WARNING;
434 : }
435 : }
436 :
437 0 : static 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 0 : struct nb_config *loaded_config = NULL;
443 0 : struct lyd_node *dnode;
444 0 : struct ly_ctx *ly_ctx;
445 0 : int ly_format;
446 0 : char buf[BUFSIZ];
447 0 : LY_ERR err;
448 :
449 0 : switch (format) {
450 0 : case NB_CFG_FMT_CMDS:
451 0 : loaded_config = nb_config_new(NULL);
452 0 : if (!vty_read_config(loaded_config, path, config_default)) {
453 0 : vty_out(vty, "%% Failed to load configuration.\n\n");
454 0 : vty_out(vty,
455 : "Please check the logs for more details.\n");
456 0 : nb_config_free(loaded_config);
457 0 : return CMD_WARNING;
458 : }
459 : break;
460 0 : case NB_CFG_FMT_JSON:
461 : case NB_CFG_FMT_XML:
462 0 : ly_format = (format == NB_CFG_FMT_JSON) ? LYD_JSON : LYD_XML;
463 :
464 0 : ly_ctx = translator ? translator->ly_ctx : ly_native_ctx;
465 0 : err = lyd_parse_data_path(ly_ctx, path, ly_format,
466 : LYD_PARSE_ONLY | LYD_PARSE_NO_STATE,
467 : 0, &dnode);
468 0 : if (err || !dnode) {
469 0 : flog_warn(EC_LIB_LIBYANG, "%s: lyd_parse_path() failed",
470 : __func__);
471 0 : vty_out(vty, "%% Failed to load configuration:\n\n");
472 0 : vty_out(vty, "%s",
473 : yang_print_errors(ly_native_ctx, buf,
474 : sizeof(buf)));
475 0 : return CMD_WARNING;
476 : }
477 0 : if (translator
478 0 : && yang_translate_dnode(translator,
479 : YANG_TRANSLATE_TO_NATIVE, &dnode)
480 : != YANG_TRANSLATE_SUCCESS) {
481 0 : vty_out(vty, "%% Failed to translate configuration\n");
482 0 : yang_dnode_free(dnode);
483 0 : return CMD_WARNING;
484 : }
485 0 : loaded_config = nb_config_new(dnode);
486 0 : break;
487 : }
488 :
489 0 : if (replace)
490 0 : nb_config_replace(vty->candidate_config, loaded_config, false);
491 0 : else if (nb_config_merge(vty->candidate_config, loaded_config, false)
492 : != NB_OK) {
493 0 : vty_out(vty,
494 : "%% Failed to merge the loaded configuration:\n\n");
495 0 : vty_out(vty, "%s",
496 : yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
497 0 : return CMD_WARNING;
498 : }
499 :
500 : return CMD_SUCCESS;
501 : }
502 :
503 0 : static int nb_cli_candidate_load_transaction(struct vty *vty,
504 : uint32_t transaction_id,
505 : bool replace)
506 : {
507 0 : struct nb_config *loaded_config;
508 0 : char buf[BUFSIZ];
509 :
510 0 : loaded_config = nb_db_transaction_load(transaction_id);
511 0 : if (!loaded_config) {
512 0 : vty_out(vty, "%% Transaction %u does not exist.\n\n",
513 : transaction_id);
514 0 : return CMD_WARNING;
515 : }
516 :
517 0 : if (replace)
518 0 : nb_config_replace(vty->candidate_config, loaded_config, false);
519 0 : else if (nb_config_merge(vty->candidate_config, loaded_config, false)
520 : != NB_OK) {
521 0 : vty_out(vty,
522 : "%% Failed to merge the loaded configuration:\n\n");
523 0 : vty_out(vty, "%s",
524 : yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
525 0 : return CMD_WARNING;
526 : }
527 :
528 : return CMD_SUCCESS;
529 : }
530 :
531 : /* Prepare the configuration for display. */
532 0 : void nb_cli_show_config_prepare(struct nb_config *config, bool with_defaults)
533 : {
534 : /* Nothing to do for daemons that don't implement any YANG module. */
535 0 : if (config->dnode == NULL)
536 : return;
537 :
538 : /*
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.
543 : */
544 0 : if (config != running_config)
545 0 : (void)lyd_validate_all(&config->dnode, ly_native_ctx,
546 : LYD_VALIDATE_NO_STATE, NULL);
547 : }
548 :
549 0 : static int lyd_node_cmp(const struct lyd_node **dnode1,
550 : const struct lyd_node **dnode2)
551 : {
552 0 : struct nb_node *nb_node = (*dnode1)->schema->priv;
553 :
554 0 : return nb_node->cbs.cli_cmp(*dnode1, *dnode2);
555 : }
556 :
557 0 : static void show_dnode_children_cmds(struct vty *vty,
558 : const struct lyd_node *root,
559 : bool with_defaults)
560 : {
561 0 : struct nb_node *nb_node, *sort_node = NULL;
562 0 : struct listnode *listnode;
563 0 : struct lyd_node *child;
564 0 : struct list *sort_list;
565 0 : void *data;
566 :
567 0 : LY_LIST_FOR (lyd_child(root), child) {
568 0 : nb_node = child->schema->priv;
569 :
570 : /*
571 : * We finished processing current list,
572 : * it's time to print the config.
573 : */
574 0 : if (sort_node && nb_node != sort_node) {
575 0 : list_sort(sort_list,
576 : (int (*)(const void **,
577 : const void **))lyd_node_cmp);
578 :
579 0 : for (ALL_LIST_ELEMENTS_RO(sort_list, listnode, data))
580 0 : nb_cli_show_dnode_cmds(vty, data,
581 : with_defaults);
582 :
583 0 : list_delete(&sort_list);
584 0 : 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 0 : if (nb_node && nb_node->cbs.cli_cmp) {
593 0 : if (!sort_node) {
594 0 : sort_node = nb_node;
595 0 : sort_list = list_new();
596 : }
597 :
598 0 : listnode_add(sort_list, child);
599 0 : continue;
600 : }
601 :
602 0 : nb_cli_show_dnode_cmds(vty, child, with_defaults);
603 : }
604 :
605 0 : if (sort_node) {
606 0 : list_sort(sort_list,
607 : (int (*)(const void **, const void **))lyd_node_cmp);
608 :
609 0 : for (ALL_LIST_ELEMENTS_RO(sort_list, listnode, data))
610 0 : nb_cli_show_dnode_cmds(vty, data, with_defaults);
611 :
612 0 : list_delete(&sort_list);
613 0 : sort_node = NULL;
614 : }
615 0 : }
616 :
617 0 : void nb_cli_show_dnode_cmds(struct vty *vty, const struct lyd_node *root,
618 : bool with_defaults)
619 : {
620 0 : struct nb_node *nb_node;
621 :
622 0 : if (!with_defaults && yang_dnode_is_default_recursive(root))
623 : return;
624 :
625 0 : nb_node = root->schema->priv;
626 :
627 0 : if (nb_node && nb_node->cbs.cli_show)
628 0 : (*nb_node->cbs.cli_show)(vty, root, with_defaults);
629 :
630 0 : if (!(root->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)))
631 0 : show_dnode_children_cmds(vty, root, with_defaults);
632 :
633 0 : if (nb_node && nb_node->cbs.cli_show_end)
634 0 : (*nb_node->cbs.cli_show_end)(vty, root);
635 : }
636 :
637 0 : static void nb_cli_show_config_cmds(struct vty *vty, struct nb_config *config,
638 : bool with_defaults)
639 : {
640 0 : struct lyd_node *root;
641 :
642 0 : vty_out(vty, "Configuration:\n");
643 0 : vty_out(vty, "!\n");
644 0 : vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
645 0 : vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
646 :
647 0 : LY_LIST_FOR (config->dnode, root) {
648 0 : nb_cli_show_dnode_cmds(vty, root, with_defaults);
649 : }
650 :
651 0 : vty_out(vty, "!\n");
652 0 : vty_out(vty, "end\n");
653 0 : }
654 :
655 0 : static 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 0 : struct lyd_node *dnode;
661 0 : char *strp;
662 0 : int options = 0;
663 :
664 0 : dnode = yang_dnode_dup(config->dnode);
665 0 : if (translator
666 0 : && yang_translate_dnode(translator, YANG_TRANSLATE_FROM_NATIVE,
667 : &dnode)
668 : != YANG_TRANSLATE_SUCCESS) {
669 0 : vty_out(vty, "%% Failed to translate configuration\n");
670 0 : yang_dnode_free(dnode);
671 0 : return CMD_WARNING;
672 : }
673 :
674 0 : SET_FLAG(options, LYD_PRINT_WITHSIBLINGS);
675 0 : if (with_defaults)
676 : SET_FLAG(options, LYD_PRINT_WD_ALL);
677 : else
678 0 : SET_FLAG(options, LYD_PRINT_WD_TRIM);
679 :
680 0 : if (lyd_print_mem(&strp, dnode, format, options) == 0 && strp) {
681 0 : vty_out(vty, "%s", strp);
682 0 : free(strp);
683 : }
684 :
685 0 : yang_dnode_free(dnode);
686 :
687 0 : return CMD_SUCCESS;
688 : }
689 :
690 0 : static 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 : {
695 0 : nb_cli_show_config_prepare(config, with_defaults);
696 :
697 0 : switch (format) {
698 0 : case NB_CFG_FMT_CMDS:
699 0 : nb_cli_show_config_cmds(vty, config, with_defaults);
700 0 : break;
701 0 : case NB_CFG_FMT_JSON:
702 0 : return nb_cli_show_config_libyang(vty, LYD_JSON, config,
703 : translator, with_defaults);
704 0 : case NB_CFG_FMT_XML:
705 0 : return nb_cli_show_config_libyang(vty, LYD_XML, config,
706 : translator, with_defaults);
707 : }
708 :
709 : return CMD_SUCCESS;
710 : }
711 :
712 0 : static 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 0 : int fd;
717 0 : struct vty *file_vty;
718 0 : int ret = 0;
719 :
720 0 : snprintf(path, pathlen, "/tmp/frr.tmp.XXXXXXXX");
721 0 : fd = mkstemp(path);
722 0 : if (fd < 0) {
723 0 : flog_warn(EC_LIB_SYSTEM_CALL, "%s: mkstemp() failed: %s",
724 : __func__, safe_strerror(errno));
725 0 : return -1;
726 : }
727 0 : if (fchmod(fd, CONFIGFILE_MASK) != 0) {
728 0 : flog_warn(EC_LIB_SYSTEM_CALL,
729 : "%s: fchmod() failed: %s(%d):", __func__,
730 : safe_strerror(errno), errno);
731 0 : return -1;
732 : }
733 :
734 : /* Make vty for configuration file. */
735 0 : file_vty = vty_new();
736 0 : file_vty->wfd = fd;
737 0 : file_vty->type = VTY_FILE;
738 0 : if (config)
739 0 : ret = nb_cli_show_config(file_vty, config, format, translator,
740 : false);
741 0 : vty_close(file_vty);
742 :
743 0 : return ret;
744 : }
745 :
746 0 : static 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 0 : char config1_path[256];
753 0 : char config2_path[256];
754 0 : char command[BUFSIZ];
755 0 : FILE *fp;
756 0 : char line[1024];
757 0 : int lineno = 0;
758 :
759 0 : if (nb_write_config(config1, format, translator, config1_path,
760 : sizeof(config1_path))
761 : != 0) {
762 0 : vty_out(vty, "%% Failed to process configurations.\n\n");
763 0 : return CMD_WARNING;
764 : }
765 0 : if (nb_write_config(config2, format, translator, config2_path,
766 : sizeof(config2_path))
767 : != 0) {
768 0 : vty_out(vty, "%% Failed to process configurations.\n\n");
769 0 : unlink(config1_path);
770 0 : return CMD_WARNING;
771 : }
772 :
773 0 : snprintf(command, sizeof(command), "diff -u %s %s", config1_path,
774 : config2_path);
775 0 : fp = popen(command, "r");
776 0 : if (!fp) {
777 0 : vty_out(vty, "%% Failed to generate configuration diff.\n\n");
778 0 : unlink(config1_path);
779 0 : unlink(config2_path);
780 0 : return CMD_WARNING;
781 : }
782 : /* Print diff line by line. */
783 0 : while (fgets(line, sizeof(line), fp) != NULL) {
784 0 : if (lineno++ < 2)
785 0 : continue;
786 0 : vty_out(vty, "%s", line);
787 : }
788 0 : pclose(fp);
789 :
790 0 : unlink(config1_path);
791 0 : unlink(config2_path);
792 :
793 0 : return CMD_SUCCESS;
794 : }
795 :
796 : /* Configure exclusively from this terminal. */
797 0 : DEFUN (config_exclusive,
798 : config_exclusive_cmd,
799 : "configure exclusive",
800 : "Configuration from vty interface\n"
801 : "Configure exclusively from this terminal\n")
802 : {
803 0 : return vty_config_enter(vty, true, true);
804 : }
805 :
806 : /* Configure using a private candidate configuration. */
807 0 : DEFUN (config_private,
808 : config_private_cmd,
809 : "configure private",
810 : "Configuration from vty interface\n"
811 : "Configure using a private candidate configuration\n")
812 : {
813 0 : return vty_config_enter(vty, true, false);
814 : }
815 :
816 0 : DEFPY (config_commit,
817 : config_commit_cmd,
818 : "commit [{force$force|confirmed (1-60)}]",
819 : "Commit changes into the running configuration\n"
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")
823 : {
824 0 : return nb_cli_commit(vty, !!force, confirmed, NULL);
825 : }
826 :
827 0 : DEFPY (config_commit_comment,
828 : config_commit_comment_cmd,
829 : "commit [{force$force|confirmed (1-60)}] comment LINE...",
830 : "Commit changes into the running configuration\n"
831 : "Force commit even if the candidate is outdated\n"
832 : "Rollback this commit unless there is a confirming commit\n"
833 : "Timeout in minutes for the commit to be confirmed\n"
834 : "Assign a comment to this commit\n"
835 : "Comment for this commit (Max 80 characters)\n")
836 : {
837 0 : char *comment;
838 0 : int idx = 0;
839 0 : int ret;
840 :
841 0 : argv_find(argv, argc, "LINE", &idx);
842 0 : comment = argv_concat(argv, argc, idx);
843 0 : ret = nb_cli_commit(vty, !!force, confirmed, comment);
844 0 : XFREE(MTYPE_TMP, comment);
845 :
846 0 : return ret;
847 : }
848 :
849 0 : DEFPY (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 : {
855 0 : struct nb_context context = {};
856 0 : char errmsg[BUFSIZ] = {0};
857 0 : int ret;
858 :
859 0 : context.client = NB_CLIENT_CLI;
860 0 : context.user = vty;
861 0 : ret = nb_candidate_validate(&context, vty->candidate_config, errmsg,
862 : sizeof(errmsg));
863 0 : if (ret != NB_OK) {
864 0 : vty_out(vty,
865 : "%% Failed to validate candidate configuration.\n\n");
866 0 : vty_show_nb_errors(vty, ret, errmsg);
867 0 : return CMD_WARNING;
868 : }
869 :
870 0 : vty_out(vty, "%% Candidate configuration validated successfully.\n\n");
871 :
872 0 : return CMD_SUCCESS;
873 : }
874 :
875 0 : DEFPY (config_update,
876 : config_update_cmd,
877 : "update",
878 : "Update candidate configuration\n")
879 : {
880 0 : if (!nb_candidate_needs_update(vty->candidate_config)) {
881 0 : vty_out(vty, "%% Update is not necessary.\n\n");
882 0 : return CMD_SUCCESS;
883 : }
884 :
885 0 : if (nb_candidate_update(vty->candidate_config) != NB_OK) {
886 0 : vty_out(vty,
887 : "%% Failed to update the candidate configuration.\n\n");
888 0 : vty_out(vty, "Please check the logs for more details.\n");
889 0 : return CMD_WARNING;
890 : }
891 :
892 0 : nb_config_replace(vty->candidate_config_base, running_config, true);
893 :
894 0 : vty_out(vty, "%% Candidate configuration updated successfully.\n\n");
895 :
896 0 : return CMD_SUCCESS;
897 : }
898 :
899 0 : DEFPY (config_discard,
900 : config_discard_cmd,
901 : "discard",
902 : "Discard changes in the candidate configuration\n")
903 : {
904 0 : nb_config_replace(vty->candidate_config, vty->candidate_config_base,
905 : true);
906 :
907 0 : return CMD_SUCCESS;
908 : }
909 :
910 0 : DEFPY (config_load,
911 : config_load_cmd,
912 : "configuration load\
913 : <\
914 : file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
915 : |transaction (1-4294967295)$tid\
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 0 : if (filename) {
931 0 : enum nb_cfg_format format;
932 0 : struct yang_translator *translator = NULL;
933 :
934 0 : if (json)
935 : format = NB_CFG_FMT_JSON;
936 0 : else if (xml)
937 : format = NB_CFG_FMT_XML;
938 : else
939 0 : format = NB_CFG_FMT_CMDS;
940 :
941 0 : if (translator_family) {
942 0 : translator = yang_translator_find(translator_family);
943 0 : if (!translator) {
944 0 : vty_out(vty,
945 : "%% Module translator \"%s\" not found\n",
946 : translator_family);
947 0 : return CMD_WARNING;
948 : }
949 : }
950 :
951 0 : return nb_cli_candidate_load_file(vty, format, translator,
952 : filename, !!replace);
953 : }
954 :
955 0 : return nb_cli_candidate_load_transaction(vty, tid, !!replace);
956 : }
957 :
958 0 : DEFPY (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 0 : enum nb_cfg_format format;
974 0 : struct yang_translator *translator = NULL;
975 :
976 0 : if (json)
977 : format = NB_CFG_FMT_JSON;
978 0 : else if (xml)
979 : format = NB_CFG_FMT_XML;
980 : else
981 0 : format = NB_CFG_FMT_CMDS;
982 :
983 0 : if (translator_family) {
984 0 : translator = yang_translator_find(translator_family);
985 0 : if (!translator) {
986 0 : vty_out(vty, "%% Module translator \"%s\" not found\n",
987 : translator_family);
988 0 : return CMD_WARNING;
989 : }
990 : }
991 :
992 0 : nb_cli_show_config(vty, running_config, format, translator,
993 : !!with_defaults);
994 :
995 0 : return CMD_SUCCESS;
996 : }
997 :
998 0 : DEFPY (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 0 : enum nb_cfg_format format;
1018 0 : struct yang_translator *translator = NULL;
1019 :
1020 0 : if (json)
1021 : format = NB_CFG_FMT_JSON;
1022 0 : else if (xml)
1023 : format = NB_CFG_FMT_XML;
1024 : else
1025 0 : format = NB_CFG_FMT_CMDS;
1026 :
1027 0 : if (translator_family) {
1028 0 : translator = yang_translator_find(translator_family);
1029 0 : if (!translator) {
1030 0 : vty_out(vty, "%% Module translator \"%s\" not found\n",
1031 : translator_family);
1032 0 : return CMD_WARNING;
1033 : }
1034 : }
1035 :
1036 0 : if (changes)
1037 0 : return nb_cli_show_config_compare(
1038 : vty, vty->candidate_config_base, vty->candidate_config,
1039 : format, translator);
1040 :
1041 0 : nb_cli_show_config(vty, vty->candidate_config, format, translator,
1042 : !!with_defaults);
1043 :
1044 0 : return CMD_SUCCESS;
1045 : }
1046 :
1047 0 : DEFPY (show_config_candidate_section,
1048 : show_config_candidate_section_cmd,
1049 : "show",
1050 : SHOW_STR)
1051 : {
1052 0 : struct lyd_node *dnode;
1053 :
1054 : /* Top-level configuration node, display everything. */
1055 0 : if (vty->xpath_index == 0)
1056 0 : 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 0 : dnode = yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH);
1061 0 : if (!dnode)
1062 : /* Shouldn't happen. */
1063 : return CMD_WARNING;
1064 :
1065 0 : nb_cli_show_dnode_cmds(vty, dnode, 0);
1066 0 : vty_out(vty, "!\n");
1067 :
1068 0 : return CMD_SUCCESS;
1069 : }
1070 :
1071 0 : DEFPY (show_config_compare,
1072 : show_config_compare_cmd,
1073 : "show configuration compare\
1074 : <\
1075 : candidate$c1_candidate\
1076 : |running$c1_running\
1077 : |transaction (1-4294967295)$c1_tid\
1078 : >\
1079 : <\
1080 : candidate$c2_candidate\
1081 : |running$c2_running\
1082 : |transaction (1-4294967295)$c2_tid\
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 0 : enum nb_cfg_format format;
1102 0 : struct yang_translator *translator = NULL;
1103 0 : struct nb_config *config1, *config_transaction1 = NULL;
1104 0 : struct nb_config *config2, *config_transaction2 = NULL;
1105 0 : int ret = CMD_WARNING;
1106 :
1107 0 : if (c1_candidate)
1108 0 : config1 = vty->candidate_config;
1109 0 : else if (c1_running)
1110 0 : config1 = running_config;
1111 : else {
1112 0 : config_transaction1 = nb_db_transaction_load(c1_tid);
1113 0 : if (!config_transaction1) {
1114 0 : vty_out(vty, "%% Transaction %u does not exist\n\n",
1115 : (unsigned int)c1_tid);
1116 0 : goto exit;
1117 : }
1118 : config1 = config_transaction1;
1119 : }
1120 :
1121 0 : if (c2_candidate)
1122 0 : config2 = vty->candidate_config;
1123 0 : else if (c2_running)
1124 0 : config2 = running_config;
1125 : else {
1126 0 : config_transaction2 = nb_db_transaction_load(c2_tid);
1127 0 : if (!config_transaction2) {
1128 0 : vty_out(vty, "%% Transaction %u does not exist\n\n",
1129 : (unsigned int)c2_tid);
1130 0 : goto exit;
1131 : }
1132 : config2 = config_transaction2;
1133 : }
1134 :
1135 0 : if (json)
1136 : format = NB_CFG_FMT_JSON;
1137 0 : else if (xml)
1138 : format = NB_CFG_FMT_XML;
1139 : else
1140 0 : format = NB_CFG_FMT_CMDS;
1141 :
1142 0 : if (translator_family) {
1143 0 : translator = yang_translator_find(translator_family);
1144 0 : if (!translator) {
1145 0 : vty_out(vty, "%% Module translator \"%s\" not found\n",
1146 : translator_family);
1147 0 : goto exit;
1148 : }
1149 : }
1150 :
1151 0 : ret = nb_cli_show_config_compare(vty, config1, config2, format,
1152 : translator);
1153 0 : exit:
1154 0 : if (config_transaction1)
1155 0 : nb_config_free(config_transaction1);
1156 0 : if (config_transaction2)
1157 0 : nb_config_free(config_transaction2);
1158 :
1159 0 : 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 : */
1167 : ALIAS (show_config_compare,
1168 : show_config_compare_without_candidate_cmd,
1169 : "show configuration compare\
1170 : <\
1171 : running$c1_running\
1172 : |transaction (1-4294967295)$c1_tid\
1173 : >\
1174 : <\
1175 : running$c2_running\
1176 : |transaction (1-4294967295)$c2_tid\
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 :
1193 0 : DEFPY (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 0 : vty_out(vty,
1209 : "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1210 : #endif /* HAVE_CONFIG_ROLLBACKS */
1211 :
1212 0 : return CMD_SUCCESS;
1213 : }
1214 :
1215 0 : DEFPY (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 0 : vty_out(vty,
1233 : "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1234 : #endif /* HAVE_CONFIG_ROLLBACKS */
1235 :
1236 0 : return CMD_SUCCESS;
1237 : }
1238 :
1239 0 : DEFPY (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 0 : struct yang_translator *translator;
1248 :
1249 0 : translator = yang_translator_load(filename);
1250 0 : if (!translator) {
1251 0 : vty_out(vty, "%% Failed to load \"%s\"\n\n", filename);
1252 0 : vty_out(vty, "Please check the logs for more details.\n");
1253 0 : return CMD_WARNING;
1254 : }
1255 :
1256 0 : vty_out(vty, "%% Module translator \"%s\" loaded successfully.\n\n",
1257 0 : translator->family);
1258 :
1259 0 : return CMD_SUCCESS;
1260 : }
1261 :
1262 0 : DEFPY (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 0 : struct yang_translator *translator;
1271 :
1272 0 : translator = yang_translator_find(translator_family);
1273 0 : if (!translator) {
1274 0 : vty_out(vty, "%% Module translator \"%s\" not found\n",
1275 : translator_family);
1276 0 : return CMD_WARNING;
1277 : }
1278 :
1279 0 : yang_translator_unload(translator);
1280 :
1281 0 : return CMD_SUCCESS;
1282 : }
1283 :
1284 : #ifdef HAVE_CONFIG_ROLLBACKS
1285 : static 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 :
1295 : static 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 :
1331 0 : DEFPY (show_config_transaction,
1332 : show_config_transaction_cmd,
1333 : "show configuration transaction\
1334 : [\
1335 : (1-4294967295)$transaction_id\
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 0 : vty_out(vty,
1410 : "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1411 0 : return CMD_WARNING;
1412 : #endif /* HAVE_CONFIG_ROLLBACKS */
1413 : }
1414 :
1415 0 : static int nb_cli_oper_data_cb(const struct lysc_node *snode,
1416 : struct yang_translator *translator,
1417 : struct yang_data *data, void *arg)
1418 : {
1419 0 : struct lyd_node *dnode = arg;
1420 0 : struct ly_ctx *ly_ctx;
1421 :
1422 0 : if (translator) {
1423 0 : int ret;
1424 :
1425 0 : ret = yang_translate_xpath(translator,
1426 : YANG_TRANSLATE_FROM_NATIVE,
1427 0 : data->xpath, sizeof(data->xpath));
1428 0 : switch (ret) {
1429 : case YANG_TRANSLATE_SUCCESS:
1430 : break;
1431 0 : case YANG_TRANSLATE_NOTFOUND:
1432 0 : goto exit;
1433 0 : case YANG_TRANSLATE_FAILURE:
1434 0 : goto error;
1435 : }
1436 :
1437 0 : ly_ctx = translator->ly_ctx;
1438 : } else
1439 0 : ly_ctx = ly_native_ctx;
1440 :
1441 0 : LY_ERR err =
1442 0 : lyd_new_path(dnode, ly_ctx, data->xpath, (void *)data->value,
1443 : LYD_NEW_PATH_UPDATE, &dnode);
1444 0 : if (err) {
1445 0 : flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path(%s) failed: %s",
1446 : __func__, data->xpath, ly_errmsg(ly_native_ctx));
1447 0 : goto error;
1448 : }
1449 :
1450 0 : exit:
1451 0 : yang_data_free(data);
1452 0 : return NB_OK;
1453 :
1454 0 : error:
1455 0 : yang_data_free(data);
1456 0 : return NB_ERR;
1457 : }
1458 :
1459 0 : DEFPY (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\
1465 : |with-config$with_config\
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"
1475 : "YANG module translator\n"
1476 : "Merge configuration data\n")
1477 : {
1478 0 : LYD_FORMAT format;
1479 0 : struct yang_translator *translator = NULL;
1480 0 : struct ly_ctx *ly_ctx;
1481 0 : struct lyd_node *dnode;
1482 0 : char *strp;
1483 0 : uint32_t print_options = LYD_PRINT_WITHSIBLINGS;
1484 :
1485 0 : if (xml)
1486 : format = LYD_XML;
1487 : else
1488 0 : format = LYD_JSON;
1489 :
1490 0 : if (translator_family) {
1491 0 : translator = yang_translator_find(translator_family);
1492 0 : if (!translator) {
1493 0 : vty_out(vty, "%% Module translator \"%s\" not found\n",
1494 : translator_family);
1495 0 : return CMD_WARNING;
1496 : }
1497 :
1498 0 : ly_ctx = translator->ly_ctx;
1499 : } else
1500 0 : ly_ctx = ly_native_ctx;
1501 :
1502 : /* Obtain data. */
1503 0 : dnode = yang_dnode_new(ly_ctx, false);
1504 0 : if (nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb,
1505 : dnode)
1506 : != NB_OK) {
1507 0 : vty_out(vty, "%% Failed to fetch operational data.\n");
1508 0 : yang_dnode_free(dnode);
1509 0 : return CMD_WARNING;
1510 : }
1511 :
1512 0 : if (with_config && yang_dnode_exists(running_config->dnode, xpath)) {
1513 0 : struct lyd_node *config_dnode =
1514 0 : yang_dnode_get(running_config->dnode, xpath);
1515 0 : if (config_dnode != NULL) {
1516 0 : lyd_merge_tree(&dnode, yang_dnode_dup(config_dnode),
1517 : LYD_MERGE_DESTRUCT);
1518 0 : print_options |= LYD_PRINT_WD_ALL;
1519 : }
1520 : }
1521 :
1522 0 : (void)lyd_validate_all(&dnode, ly_ctx, 0, NULL);
1523 :
1524 : /* Display the data. */
1525 0 : if (lyd_print_mem(&strp, dnode, format, print_options) != 0 || !strp) {
1526 0 : vty_out(vty, "%% Failed to display operational data.\n");
1527 0 : yang_dnode_free(dnode);
1528 0 : return CMD_WARNING;
1529 : }
1530 0 : vty_out(vty, "%s", strp);
1531 0 : free(strp);
1532 0 : yang_dnode_free(dnode);
1533 :
1534 0 : return CMD_SUCCESS;
1535 : }
1536 :
1537 0 : DEFPY (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 0 : struct ly_ctx *ly_ctx;
1547 0 : struct yang_translator *translator = NULL;
1548 0 : const struct lys_module *module;
1549 0 : struct ttable *tt;
1550 0 : uint32_t idx = 0;
1551 :
1552 0 : if (translator_family) {
1553 0 : translator = yang_translator_find(translator_family);
1554 0 : if (!translator) {
1555 0 : vty_out(vty, "%% Module translator \"%s\" not found\n",
1556 : translator_family);
1557 0 : return CMD_WARNING;
1558 : }
1559 0 : ly_ctx = translator->ly_ctx;
1560 : } else
1561 0 : ly_ctx = ly_native_ctx;
1562 :
1563 : /* Prepare table. */
1564 0 : tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
1565 0 : ttable_add_row(tt, "Module|Version|Revision|Flags|Namespace");
1566 0 : tt->style.cell.rpad = 2;
1567 0 : tt->style.corner = '+';
1568 0 : ttable_restyle(tt);
1569 0 : ttable_rowseps(tt, 0, BOTTOM, true, '-');
1570 :
1571 0 : while ((module = ly_ctx_get_module_iter(ly_ctx, &idx))) {
1572 0 : char flags[8];
1573 :
1574 0 : snprintf(flags, sizeof(flags), "%c%c",
1575 0 : module->implemented ? 'I' : ' ',
1576 0 : LY_ARRAY_COUNT(module->deviated_by) ? 'D' : ' ');
1577 :
1578 0 : ttable_add_row(tt, "%s|%s|%s|%s|%s", module->name,
1579 0 : (module->parsed->version == 2) ? "1.1" : "1.0",
1580 0 : module->revision ? module->revision : "-", flags,
1581 0 : module->ns);
1582 : }
1583 :
1584 : /* Dump the generated table. */
1585 0 : if (tt->nrows > 1) {
1586 0 : char *table;
1587 :
1588 0 : vty_out(vty, " Flags: I - Implemented, D - Deviated\n\n");
1589 :
1590 0 : table = ttable_dump(tt, "\n");
1591 0 : vty_out(vty, "%s\n", table);
1592 0 : XFREE(MTYPE_TMP, table);
1593 : } else
1594 0 : vty_out(vty, "No YANG modules to display.\n\n");
1595 :
1596 0 : ttable_del(tt);
1597 :
1598 0 : return CMD_SUCCESS;
1599 : }
1600 :
1601 0 : DEFPY(show_yang_module_detail, show_yang_module_detail_cmd,
1602 : "show yang module\
1603 : [module-translator WORD$translator_family]\
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")
1616 : {
1617 0 : struct ly_ctx *ly_ctx;
1618 0 : struct yang_translator *translator = NULL;
1619 0 : const struct lys_module *module;
1620 0 : LYS_OUTFORMAT format;
1621 0 : char *strp;
1622 :
1623 0 : if (translator_family) {
1624 0 : translator = yang_translator_find(translator_family);
1625 0 : if (!translator) {
1626 0 : vty_out(vty, "%% Module translator \"%s\" not found\n",
1627 : translator_family);
1628 0 : return CMD_WARNING;
1629 : }
1630 0 : ly_ctx = translator->ly_ctx;
1631 : } else
1632 0 : ly_ctx = ly_native_ctx;
1633 :
1634 0 : module = ly_ctx_get_module_latest(ly_ctx, module_name);
1635 0 : if (!module) {
1636 0 : vty_out(vty, "%% Module \"%s\" not found\n", module_name);
1637 0 : return CMD_WARNING;
1638 : }
1639 :
1640 0 : if (yang)
1641 : format = LYS_OUT_YANG;
1642 0 : else if (yin)
1643 : format = LYS_OUT_YIN;
1644 0 : else if (compiled)
1645 : format = LYS_OUT_YANG_COMPILED;
1646 0 : else if (tree)
1647 : format = LYS_OUT_TREE;
1648 : else {
1649 0 : vty_out(vty,
1650 : "%% libyang v2 does not currently support summary\n");
1651 0 : return CMD_WARNING;
1652 : }
1653 :
1654 0 : if (lys_print_mem(&strp, module, format, 0) == 0) {
1655 0 : vty_out(vty, "%s\n", strp);
1656 0 : free(strp);
1657 : } else {
1658 : /* Unexpected. */
1659 0 : vty_out(vty, "%% Error generating module information\n");
1660 0 : return CMD_WARNING;
1661 : }
1662 :
1663 0 : return CMD_SUCCESS;
1664 : }
1665 :
1666 0 : DEFPY (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 0 : struct yang_translator *translator;
1674 0 : struct ttable *tt;
1675 :
1676 : /* Prepare table. */
1677 0 : tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
1678 0 : ttable_add_row(tt, "Family|Module|Deviations|Coverage (%%)");
1679 0 : tt->style.cell.rpad = 2;
1680 0 : tt->style.corner = '+';
1681 0 : ttable_restyle(tt);
1682 0 : ttable_rowseps(tt, 0, BOTTOM, true, '-');
1683 :
1684 0 : RB_FOREACH (translator, yang_translators, &yang_translators) {
1685 0 : struct yang_tmodule *tmodule;
1686 0 : struct listnode *ln;
1687 :
1688 0 : for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
1689 0 : ttable_add_row(tt, "%s|%s|%s|%.2f", translator->family,
1690 0 : tmodule->module->name,
1691 0 : tmodule->deviations->name,
1692 : tmodule->coverage);
1693 : }
1694 : }
1695 :
1696 : /* Dump the generated table. */
1697 0 : if (tt->nrows > 1) {
1698 0 : char *table;
1699 :
1700 0 : table = ttable_dump(tt, "\n");
1701 0 : vty_out(vty, "%s\n", table);
1702 0 : XFREE(MTYPE_TMP, table);
1703 : } else
1704 0 : vty_out(vty, "No YANG module translators to display.\n\n");
1705 :
1706 0 : ttable_del(tt);
1707 :
1708 0 : return CMD_SUCCESS;
1709 : }
1710 :
1711 : #ifdef HAVE_CONFIG_ROLLBACKS
1712 : static int nb_cli_rollback_configuration(struct vty *vty,
1713 : uint32_t transaction_id)
1714 : {
1715 : struct nb_context context = {};
1716 : struct nb_config *candidate;
1717 : char comment[80];
1718 : char errmsg[BUFSIZ] = {0};
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 :
1731 : context.client = NB_CLIENT_CLI;
1732 : context.user = vty;
1733 : ret = nb_candidate_commit(&context, candidate, true, comment, NULL,
1734 : errmsg, sizeof(errmsg));
1735 : nb_config_free(candidate);
1736 : switch (ret) {
1737 : case NB_OK:
1738 : vty_out(vty,
1739 : "%% Configuration was successfully rolled back.\n\n");
1740 : /* Print warnings (if any). */
1741 : if (strlen(errmsg) > 0)
1742 : vty_out(vty, "%s\n", errmsg);
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");
1750 : vty_show_nb_errors(vty, ret, errmsg);
1751 : return CMD_WARNING;
1752 : }
1753 : }
1754 : #endif /* HAVE_CONFIG_ROLLBACKS */
1755 :
1756 0 : DEFPY (rollback_config,
1757 : rollback_config_cmd,
1758 : "rollback configuration (1-4294967295)$transaction_id",
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 0 : vty_out(vty,
1767 : "%% FRR was compiled without --enable-config-rollbacks.\n\n");
1768 0 : return CMD_SUCCESS;
1769 : #endif /* HAVE_CONFIG_ROLLBACKS */
1770 : }
1771 :
1772 : /* Debug CLI commands. */
1773 : static struct debug *nb_debugs[] = {
1774 : &nb_dbg_cbs_config, &nb_dbg_cbs_state, &nb_dbg_cbs_rpc,
1775 : &nb_dbg_notif, &nb_dbg_events, &nb_dbg_libyang,
1776 : };
1777 :
1778 : static 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",
1784 : "debug northbound libyang",
1785 : };
1786 :
1787 0 : DEFINE_HOOK(nb_client_debug_set_all, (uint32_t flags, bool set), (flags, set));
1788 :
1789 0 : static void nb_debug_set_all(uint32_t flags, bool set)
1790 : {
1791 0 : for (unsigned int i = 0; i < array_size(nb_debugs); i++) {
1792 0 : DEBUG_FLAGS_SET(nb_debugs[i], flags, set);
1793 :
1794 : /* If all modes have been turned off, don't preserve options. */
1795 0 : if (!DEBUG_MODE_CHECK(nb_debugs[i], DEBUG_MODE_ALL))
1796 0 : DEBUG_CLEAR(nb_debugs[i]);
1797 : }
1798 :
1799 0 : hook_call(nb_client_debug_set_all, flags, set);
1800 0 : }
1801 :
1802 0 : DEFPY (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\
1809 : |libyang$libyang\
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"
1819 : "Events\n"
1820 : "libyang debugging\n")
1821 : {
1822 0 : uint32_t mode = DEBUG_NODE2MODE(vty->node);
1823 :
1824 0 : if (cbs) {
1825 0 : bool none = (!cbs_cfg && !cbs_state && !cbs_rpc);
1826 :
1827 0 : if (none || cbs_cfg)
1828 0 : DEBUG_MODE_SET(&nb_dbg_cbs_config, mode, !no);
1829 0 : if (none || cbs_state)
1830 0 : DEBUG_MODE_SET(&nb_dbg_cbs_state, mode, !no);
1831 0 : if (none || cbs_rpc)
1832 0 : DEBUG_MODE_SET(&nb_dbg_cbs_rpc, mode, !no);
1833 : }
1834 0 : if (notifications)
1835 0 : DEBUG_MODE_SET(&nb_dbg_notif, mode, !no);
1836 0 : if (events)
1837 0 : DEBUG_MODE_SET(&nb_dbg_events, mode, !no);
1838 0 : if (libyang) {
1839 0 : DEBUG_MODE_SET(&nb_dbg_libyang, mode, !no);
1840 0 : yang_debugging_set(!no);
1841 : }
1842 :
1843 : /* no specific debug --> act on all of them */
1844 0 : if (strmatch(argv[argc - 1]->text, "northbound")) {
1845 0 : nb_debug_set_all(mode, !no);
1846 0 : yang_debugging_set(!no);
1847 : }
1848 :
1849 0 : return CMD_SUCCESS;
1850 : }
1851 :
1852 0 : DEFINE_HOOK(nb_client_debug_config_write, (struct vty *vty), (vty));
1853 :
1854 0 : static int nb_debug_config_write(struct vty *vty)
1855 : {
1856 0 : for (unsigned int i = 0; i < array_size(nb_debugs); i++)
1857 0 : if (DEBUG_MODE_CHECK(nb_debugs[i], DEBUG_MODE_CONF))
1858 0 : vty_out(vty, "%s\n", nb_debugs_conflines[i]);
1859 :
1860 0 : hook_call(nb_client_debug_config_write, vty);
1861 :
1862 0 : return 1;
1863 : }
1864 :
1865 : static struct debug_callbacks nb_dbg_cbs = {.debug_set_all = nb_debug_set_all};
1866 : static struct cmd_node nb_debug_node = {
1867 : .name = "northbound debug",
1868 : .node = NORTHBOUND_DEBUG_NODE,
1869 : .prompt = "",
1870 : .config_write = nb_debug_config_write,
1871 : };
1872 :
1873 168 : void nb_cli_install_default(int node)
1874 : {
1875 168 : _install_element(node, &show_config_candidate_section_cmd);
1876 :
1877 168 : if (frr_get_cli_mode() != FRR_CLI_TRANSACTIONAL)
1878 : return;
1879 :
1880 0 : _install_element(node, &config_commit_cmd);
1881 0 : _install_element(node, &config_commit_comment_cmd);
1882 0 : _install_element(node, &config_commit_check_cmd);
1883 0 : _install_element(node, &config_update_cmd);
1884 0 : _install_element(node, &config_discard_cmd);
1885 0 : _install_element(node, &show_config_running_cmd);
1886 0 : _install_element(node, &show_config_candidate_cmd);
1887 0 : _install_element(node, &show_config_compare_cmd);
1888 0 : _install_element(node, &show_config_transaction_cmd);
1889 : }
1890 :
1891 : /* YANG module autocomplete. */
1892 0 : static void yang_module_autocomplete(vector comps, struct cmd_token *token)
1893 : {
1894 0 : const struct lys_module *module;
1895 0 : struct yang_translator *module_tr;
1896 0 : uint32_t idx;
1897 :
1898 0 : idx = 0;
1899 0 : while ((module = ly_ctx_get_module_iter(ly_native_ctx, &idx)))
1900 0 : vector_set(comps, XSTRDUP(MTYPE_COMPLETION, module->name));
1901 :
1902 0 : RB_FOREACH (module_tr, yang_translators, &yang_translators) {
1903 0 : idx = 0;
1904 0 : while ((module = ly_ctx_get_module_iter(module_tr->ly_ctx,
1905 : &idx)))
1906 0 : vector_set(comps,
1907 0 : XSTRDUP(MTYPE_COMPLETION, module->name));
1908 : }
1909 0 : }
1910 :
1911 : /* YANG module translator autocomplete. */
1912 0 : static void yang_translator_autocomplete(vector comps, struct cmd_token *token)
1913 : {
1914 0 : struct yang_translator *module_tr;
1915 :
1916 0 : RB_FOREACH (module_tr, yang_translators, &yang_translators)
1917 0 : vector_set(comps, XSTRDUP(MTYPE_COMPLETION, module_tr->family));
1918 0 : }
1919 :
1920 : static 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 :
1926 16 : void nb_cli_init(struct thread_master *tm)
1927 : {
1928 16 : master = tm;
1929 :
1930 : /* Initialize the shared candidate configuration. */
1931 16 : vty_shared_candidate_config = nb_config_new(NULL);
1932 :
1933 16 : debug_init(&nb_dbg_cbs);
1934 :
1935 16 : install_node(&nb_debug_node);
1936 16 : install_element(ENABLE_NODE, &debug_nb_cmd);
1937 16 : install_element(CONFIG_NODE, &debug_nb_cmd);
1938 :
1939 : /* Install commands specific to the transaction-base mode. */
1940 16 : if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) {
1941 0 : install_element(ENABLE_NODE, &config_exclusive_cmd);
1942 0 : install_element(ENABLE_NODE, &config_private_cmd);
1943 0 : install_element(ENABLE_NODE,
1944 : &show_config_compare_without_candidate_cmd);
1945 0 : install_element(ENABLE_NODE, &show_config_transaction_cmd);
1946 0 : install_element(ENABLE_NODE, &rollback_config_cmd);
1947 0 : install_element(ENABLE_NODE, &clear_config_transactions_cmd);
1948 :
1949 0 : install_element(CONFIG_NODE, &config_load_cmd);
1950 0 : install_element(CONFIG_NODE,
1951 : &config_database_max_transactions_cmd);
1952 : }
1953 :
1954 : /* Other commands. */
1955 16 : install_element(ENABLE_NODE, &show_config_running_cmd);
1956 16 : install_element(CONFIG_NODE, &yang_module_translator_load_cmd);
1957 16 : install_element(CONFIG_NODE, &yang_module_translator_unload_cmd);
1958 16 : install_element(ENABLE_NODE, &show_yang_operational_data_cmd);
1959 16 : install_element(ENABLE_NODE, &show_yang_module_cmd);
1960 16 : install_element(ENABLE_NODE, &show_yang_module_detail_cmd);
1961 16 : install_element(ENABLE_NODE, &show_yang_module_translator_cmd);
1962 16 : cmd_variable_handler_register(yang_var_handlers);
1963 16 : }
1964 :
1965 32 : void nb_cli_terminate(void)
1966 : {
1967 32 : nb_config_free(vty_shared_candidate_config);
1968 32 : }
|