back to topotato report
topotato coverage report
Current view: top level - lib - northbound_cli.c (source / functions) Hit Total Coverage
Test: aggregated run ( view descriptions ) Lines: 118 785 15.0 %
Date: 2023-02-24 19:38:44 Functions: 11 52 21.2 %

          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         499 : static int nb_cli_classic_commit(struct vty *vty)
      55             : {
      56         499 :         struct nb_context context = {};
      57         499 :         char errmsg[BUFSIZ] = {0};
      58         499 :         int ret;
      59             : 
      60         499 :         context.client = NB_CLIENT_CLI;
      61         499 :         context.user = vty;
      62         499 :         ret = nb_candidate_commit(&context, vty->candidate_config, true, NULL,
      63             :                                   NULL, errmsg, sizeof(errmsg));
      64         499 :         switch (ret) {
      65         497 :         case NB_OK:
      66             :                 /* Successful commit. Print warnings (if any). */
      67         497 :                 if (strlen(errmsg) > 0)
      68           3 :                         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          62 : static void nb_cli_pending_commit_clear(struct vty *vty)
      89             : {
      90          62 :         vty->pending_commit = 0;
      91          62 :         XFREE(MTYPE_TMP, vty->pending_cmds_buf);
      92          62 :         vty->pending_cmds_buflen = 0;
      93          62 :         vty->pending_cmds_bufpos = 0;
      94          62 : }
      95             : 
      96        2907 : int nb_cli_pending_commit_check(struct vty *vty)
      97             : {
      98        2907 :         int ret = CMD_SUCCESS;
      99             : 
     100        2907 :         if (vty->pending_commit) {
     101          62 :                 ret = nb_cli_classic_commit(vty);
     102          62 :                 nb_cli_pending_commit_clear(vty);
     103             :         }
     104             : 
     105        2907 :         return ret;
     106             : }
     107             : 
     108          62 : static int nb_cli_schedule_command(struct vty *vty)
     109             : {
     110             :         /* Append command to dynamically sized buffer of scheduled commands. */
     111          62 :         if (!vty->pending_cmds_buf) {
     112          62 :                 vty->pending_cmds_buflen = 4096;
     113          62 :                 vty->pending_cmds_buf =
     114          62 :                         XCALLOC(MTYPE_TMP, vty->pending_cmds_buflen);
     115             :         }
     116          62 :         if ((strlen(vty->buf) + 3)
     117          62 :             > (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          62 :         strlcat(vty->pending_cmds_buf, "- ", vty->pending_cmds_buflen);
     124          62 :         vty->pending_cmds_bufpos = strlcat(vty->pending_cmds_buf, vty->buf,
     125             :                                            vty->pending_cmds_buflen);
     126             : 
     127             :         /* Schedule the commit operation. */
     128          62 :         vty->pending_commit = 1;
     129             : 
     130          62 :         return CMD_SUCCESS;
     131             : }
     132             : 
     133         819 : void nb_cli_enqueue_change(struct vty *vty, const char *xpath,
     134             :                            enum nb_operation operation, const char *value)
     135             : {
     136         819 :         struct vty_cfg_change *change;
     137             : 
     138         819 :         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         819 :         change = &vty->cfg_changes[vty->num_cfg_changes++];
     147         819 :         strlcpy(change->xpath, xpath, sizeof(change->xpath));
     148         819 :         change->operation = operation;
     149         819 :         change->value = value;
     150             : }
     151             : 
     152         509 : static int nb_cli_apply_changes_internal(struct vty *vty,
     153             :                                          const char *xpath_base,
     154             :                                          bool clear_pending)
     155             : {
     156         509 :         bool error = false;
     157             : 
     158         509 :         if (xpath_base == NULL)
     159           0 :                 xpath_base = "";
     160             : 
     161         509 :         VTY_CHECK_XPATH;
     162             : 
     163             :         /* Edit candidate configuration. */
     164        1328 :         for (size_t i = 0; i < vty->num_cfg_changes; i++) {
     165         819 :                 struct vty_cfg_change *change = &vty->cfg_changes[i];
     166         819 :                 struct nb_node *nb_node;
     167         819 :                 char xpath[XPATH_MAXLEN];
     168         819 :                 struct yang_data *data;
     169         819 :                 int ret;
     170             : 
     171             :                 /* Handle relative XPaths. */
     172         819 :                 memset(xpath, 0, sizeof(xpath));
     173         819 :                 if (vty->xpath_index > 0
     174         375 :                     && (xpath_base[0] == '.' || change->xpath[0] == '.'))
     175         373 :                         strlcpy(xpath, VTY_CURR_XPATH, sizeof(xpath));
     176         819 :                 if (xpath_base[0]) {
     177         585 :                         if (xpath_base[0] == '.')
     178         227 :                                 strlcat(xpath, xpath_base + 1, sizeof(xpath));
     179             :                         else
     180         358 :                                 strlcat(xpath, xpath_base, sizeof(xpath));
     181             :                 }
     182         819 :                 if (change->xpath[0] == '.')
     183         600 :                         strlcat(xpath, change->xpath + 1, sizeof(xpath));
     184             :                 else
     185         219 :                         strlcpy(xpath, change->xpath, sizeof(xpath));
     186             : 
     187             :                 /* Find the northbound node associated to the data path. */
     188         819 :                 nb_node = nb_node_find(xpath);
     189         819 :                 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         819 :                 if (change->value == NULL)
     198         411 :                         change->value = yang_snode_get_default(nb_node->snode);
     199         819 :                 data = yang_data_new(xpath, change->value);
     200             : 
     201             :                 /*
     202             :                  * Ignore "not found" errors when editing the candidate
     203             :                  * configuration.
     204             :                  */
     205         819 :                 ret = nb_candidate_edit(vty->candidate_config, nb_node,
     206             :                                         change->operation, xpath, NULL, data);
     207         819 :                 yang_data_free(data);
     208         819 :                 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         509 :         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         509 :         if (frr_get_cli_mode() == FRR_CLI_CLASSIC) {
     242         509 :                 if (clear_pending) {
     243         186 :                         if (vty->pending_commit)
     244          10 :                                 return nb_cli_pending_commit_check(vty);
     245         323 :                 } else if (vty->pending_allowed)
     246          62 :                         return nb_cli_schedule_command(vty);
     247         437 :                 assert(!vty->pending_commit);
     248         437 :                 return nb_cli_classic_commit(vty);
     249             :         }
     250             : 
     251             :         return CMD_SUCCESS;
     252             : }
     253             : 
     254         323 : int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
     255             : {
     256         323 :         char xpath_base[XPATH_MAXLEN] = {};
     257             : 
     258             :         /* Parse the base XPath format string. */
     259         323 :         if (xpath_base_fmt) {
     260         178 :                 va_list ap;
     261             : 
     262         178 :                 va_start(ap, xpath_base_fmt);
     263         178 :                 vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap);
     264         178 :                 va_end(ap);
     265             :         }
     266         323 :         return nb_cli_apply_changes_internal(vty, xpath_base, false);
     267             : }
     268             : 
     269         186 : int nb_cli_apply_changes_clear_pending(struct vty *vty,
     270             :                                        const char *xpath_base_fmt, ...)
     271             : {
     272         186 :         char xpath_base[XPATH_MAXLEN] = {};
     273             : 
     274             :         /* Parse the base XPath format string. */
     275         186 :         if (xpath_base_fmt) {
     276         186 :                 va_list ap;
     277             : 
     278         186 :                 va_start(ap, xpath_base_fmt);
     279         186 :                 vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap);
     280         186 :                 va_end(ap);
     281             :         }
     282         186 :         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        2708 : void nb_cli_install_default(int node)
    1874             : {
    1875        2708 :         _install_element(node, &show_config_candidate_section_cmd);
    1876             : 
    1877        2708 :         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         218 : void nb_cli_init(struct thread_master *tm)
    1927             : {
    1928         218 :         master = tm;
    1929             : 
    1930             :         /* Initialize the shared candidate configuration. */
    1931         218 :         vty_shared_candidate_config = nb_config_new(NULL);
    1932             : 
    1933         218 :         debug_init(&nb_dbg_cbs);
    1934             : 
    1935         218 :         install_node(&nb_debug_node);
    1936         218 :         install_element(ENABLE_NODE, &debug_nb_cmd);
    1937         218 :         install_element(CONFIG_NODE, &debug_nb_cmd);
    1938             : 
    1939             :         /* Install commands specific to the transaction-base mode. */
    1940         218 :         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         218 :         install_element(ENABLE_NODE, &show_config_running_cmd);
    1956         218 :         install_element(CONFIG_NODE, &yang_module_translator_load_cmd);
    1957         218 :         install_element(CONFIG_NODE, &yang_module_translator_unload_cmd);
    1958         218 :         install_element(ENABLE_NODE, &show_yang_operational_data_cmd);
    1959         218 :         install_element(ENABLE_NODE, &show_yang_module_cmd);
    1960         218 :         install_element(ENABLE_NODE, &show_yang_module_detail_cmd);
    1961         218 :         install_element(ENABLE_NODE, &show_yang_module_translator_cmd);
    1962         218 :         cmd_variable_handler_register(yang_var_handlers);
    1963         218 : }
    1964             : 
    1965         436 : void nb_cli_terminate(void)
    1966             : {
    1967         436 :         nb_config_free(vty_shared_candidate_config);
    1968         436 : }

Generated by: LCOV version v1.16-topotato