back to topotato report
topotato coverage report
Current view: top level - lib - northbound_db.c (source / functions) Hit Total Coverage
Test: test_ospf6_vlink.py::VirtualLinkBasic Lines: 4 13 30.8 %
Date: 2023-02-16 02:06:43 Functions: 2 6 33.3 %

          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 "log.h"
      24             : #include "lib_errors.h"
      25             : #include "command.h"
      26             : #include "db.h"
      27             : #include "northbound.h"
      28             : #include "northbound_db.h"
      29             : 
      30          16 : int nb_db_init(void)
      31             : {
      32             : #ifdef HAVE_CONFIG_ROLLBACKS
      33             :         /*
      34             :          * NOTE: the delete_tail SQL trigger is used to implement a ring buffer
      35             :          * where only the last N transactions are recorded in the configuration
      36             :          * log.
      37             :          */
      38             :         if (db_execute(
      39             :                     "BEGIN TRANSACTION;\n"
      40             :                     "  CREATE TABLE IF NOT EXISTS transactions(\n"
      41             :                     "    client         CHAR(32)             NOT NULL,\n"
      42             :                     "    date           DATETIME             DEFAULT CURRENT_TIMESTAMP,\n"
      43             :                     "    comment        CHAR(80)             ,\n"
      44             :                     "    configuration  TEXT                 NOT NULL\n"
      45             :                     "  );\n"
      46             :                     "  CREATE TRIGGER IF NOT EXISTS delete_tail\n"
      47             :                     "    AFTER INSERT ON transactions\n"
      48             :                     "    FOR EACH ROW\n"
      49             :                     "    BEGIN\n"
      50             :                     "    DELETE\n"
      51             :                     "    FROM\n"
      52             :                     "      transactions\n"
      53             :                     "    WHERE\n"
      54             :                     "      rowid%%%u=NEW.rowid%%%u AND rowid!=NEW.rowid;\n"
      55             :                     "    END;\n"
      56             :                     "COMMIT;",
      57             :                     NB_DLFT_MAX_CONFIG_ROLLBACKS, NB_DLFT_MAX_CONFIG_ROLLBACKS)
      58             :             != 0)
      59             :                 return NB_ERR;
      60             : #endif /* HAVE_CONFIG_ROLLBACKS */
      61             : 
      62          16 :         return NB_OK;
      63             : }
      64             : 
      65          60 : int nb_db_transaction_save(const struct nb_transaction *transaction,
      66             :                            uint32_t *transaction_id)
      67             : {
      68             : #ifdef HAVE_CONFIG_ROLLBACKS
      69             :         struct sqlite3_stmt *ss;
      70             :         const char *client_name;
      71             :         char *config_str = NULL;
      72             :         int ret = NB_ERR;
      73             : 
      74             :         /*
      75             :          * Use a transaction to ensure consistency between the INSERT and SELECT
      76             :          * queries.
      77             :          */
      78             :         if (db_execute("BEGIN TRANSACTION;") != 0)
      79             :                 return NB_ERR;
      80             : 
      81             :         ss = db_prepare(
      82             :                 "INSERT INTO transactions\n"
      83             :                 "  (client, comment, configuration)\n"
      84             :                 "VALUES\n"
      85             :                 "  (?, ?, ?);");
      86             :         if (!ss)
      87             :                 goto exit;
      88             : 
      89             :         client_name = nb_client_name(transaction->context->client);
      90             :         /*
      91             :          * Always record configurations in the XML format, save the default
      92             :          * values too, as this covers the case where defaults may change.
      93             :          */
      94             :         if (lyd_print_mem(&config_str, transaction->config->dnode, LYD_XML,
      95             :                           LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL)
      96             :             != 0)
      97             :                 goto exit;
      98             : 
      99             :         if (db_bindf(ss, "%s%s%s", client_name, strlen(client_name),
     100             :                      transaction->comment, strlen(transaction->comment),
     101             :                      config_str ? config_str : "",
     102             :                      config_str ? strlen(config_str) : 0)
     103             :             != 0)
     104             :                 goto exit;
     105             : 
     106             :         if (db_run(ss) != SQLITE_OK)
     107             :                 goto exit;
     108             : 
     109             :         db_finalize(&ss);
     110             : 
     111             :         /*
     112             :          * transaction_id is an optional output parameter that provides the ID
     113             :          * of the recorded transaction.
     114             :          */
     115             :         if (transaction_id) {
     116             :                 ss = db_prepare("SELECT last_insert_rowid();");
     117             :                 if (!ss)
     118             :                         goto exit;
     119             : 
     120             :                 if (db_run(ss) != SQLITE_ROW)
     121             :                         goto exit;
     122             : 
     123             :                 if (db_loadf(ss, "%i", transaction_id) != 0)
     124             :                         goto exit;
     125             : 
     126             :                 db_finalize(&ss);
     127             :         }
     128             : 
     129             :         if (db_execute("COMMIT;") != 0)
     130             :                 goto exit;
     131             : 
     132             :         ret = NB_OK;
     133             : 
     134             : exit:
     135             :         if (config_str)
     136             :                 free(config_str);
     137             :         if (ss)
     138             :                 db_finalize(&ss);
     139             :         if (ret != NB_OK)
     140             :                 (void)db_execute("ROLLBACK TRANSACTION;");
     141             : 
     142             :         return ret;
     143             : #else  /* HAVE_CONFIG_ROLLBACKS */
     144          60 :         return NB_OK;
     145             : #endif /* HAVE_CONFIG_ROLLBACKS */
     146             : }
     147             : 
     148           0 : struct nb_config *nb_db_transaction_load(uint32_t transaction_id)
     149             : {
     150           0 :         struct nb_config *config = NULL;
     151             : #ifdef HAVE_CONFIG_ROLLBACKS
     152             :         struct lyd_node *dnode;
     153             :         const char *config_str;
     154             :         struct sqlite3_stmt *ss;
     155             :         LY_ERR err;
     156             : 
     157             :         ss = db_prepare(
     158             :                 "SELECT\n"
     159             :                 "  configuration\n"
     160             :                 "FROM\n"
     161             :                 "  transactions\n"
     162             :                 "WHERE\n"
     163             :                 "  rowid=?;");
     164             :         if (!ss)
     165             :                 return NULL;
     166             : 
     167             :         if (db_bindf(ss, "%d", transaction_id) != 0)
     168             :                 goto exit;
     169             : 
     170             :         if (db_run(ss) != SQLITE_ROW)
     171             :                 goto exit;
     172             : 
     173             :         if (db_loadf(ss, "%s", &config_str) != 0)
     174             :                 goto exit;
     175             : 
     176             :         err = lyd_parse_data_mem(ly_native_ctx, config_str, LYD_XML,
     177             :                                  LYD_PARSE_STRICT | LYD_PARSE_NO_STATE,
     178             :                                  LYD_VALIDATE_NO_STATE, &dnode);
     179             :         if (err || !dnode)
     180             :                 flog_warn(EC_LIB_LIBYANG, "%s: lyd_parse_data_mem() failed",
     181             :                           __func__);
     182             :         else
     183             :                 config = nb_config_new(dnode);
     184             : 
     185             : exit:
     186             :         db_finalize(&ss);
     187             : #endif /* HAVE_CONFIG_ROLLBACKS */
     188             : 
     189           0 :         return config;
     190             : }
     191             : 
     192           0 : int nb_db_clear_transactions(unsigned int n_oldest)
     193             : {
     194             : #ifdef HAVE_CONFIG_ROLLBACKS
     195             :         /* Delete oldest N entries. */
     196             :         if (db_execute("DELETE\n"
     197             :                        "FROM\n"
     198             :                        "  transactions\n"
     199             :                        "WHERE\n"
     200             :                        "  ROWID IN (\n"
     201             :                        "    SELECT\n"
     202             :                        "      ROWID\n"
     203             :                        "    FROM\n"
     204             :                        "      transactions\n"
     205             :                        "    ORDER BY ROWID ASC LIMIT %u\n"
     206             :                        "  );",
     207             :                        n_oldest)
     208             :             != 0)
     209             :                 return NB_ERR;
     210             : #endif /* HAVE_CONFIG_ROLLBACKS */
     211             : 
     212           0 :         return NB_OK;
     213             : }
     214             : 
     215           0 : int nb_db_set_max_transactions(unsigned int max)
     216             : {
     217             : #ifdef HAVE_CONFIG_ROLLBACKS
     218             :         /*
     219             :          * Delete old entries if necessary and update the SQL trigger that
     220             :          * auto-deletes old entries.
     221             :          */
     222             :         if (db_execute("BEGIN TRANSACTION;\n"
     223             :                        "  DELETE\n"
     224             :                        "  FROM\n"
     225             :                        "    transactions\n"
     226             :                        "  WHERE\n"
     227             :                        "    ROWID IN (\n"
     228             :                        "      SELECT\n"
     229             :                        "        ROWID\n"
     230             :                        "      FROM\n"
     231             :                        "        transactions\n"
     232             :                        "      ORDER BY ROWID DESC LIMIT -1 OFFSET %u\n"
     233             :                        "    );\n"
     234             :                        "  DROP TRIGGER delete_tail;\n"
     235             :                        "  CREATE TRIGGER delete_tail\n"
     236             :                        "  AFTER INSERT ON transactions\n"
     237             :                        "    FOR EACH ROW\n"
     238             :                        "    BEGIN\n"
     239             :                        "    DELETE\n"
     240             :                        "    FROM\n"
     241             :                        "      transactions\n"
     242             :                        "    WHERE\n"
     243             :                        "      rowid%%%u=NEW.rowid%%%u AND rowid!=NEW.rowid;\n"
     244             :                        "    END;\n"
     245             :                        "COMMIT;",
     246             :                        max, max, max)
     247             :             != 0)
     248             :                 return NB_ERR;
     249             : #endif /* HAVE_CONFIG_ROLLBACKS */
     250             : 
     251           0 :         return NB_OK;
     252             : }
     253             : 
     254           0 : int nb_db_transactions_iterate(void (*func)(void *arg, int transaction_id,
     255             :                                             const char *client_name,
     256             :                                             const char *date,
     257             :                                             const char *comment),
     258             :                                void *arg)
     259             : {
     260             : #ifdef HAVE_CONFIG_ROLLBACKS
     261             :         struct sqlite3_stmt *ss;
     262             : 
     263             :         /* Send SQL query and parse the result. */
     264             :         ss = db_prepare(
     265             :                 "SELECT\n"
     266             :                 "  rowid, client, date, comment\n"
     267             :                 "FROM\n"
     268             :                 "  transactions\n"
     269             :                 "ORDER BY\n"
     270             :                 "  rowid DESC;");
     271             :         if (!ss)
     272             :                 return NB_ERR;
     273             : 
     274             :         while (db_run(ss) == SQLITE_ROW) {
     275             :                 int transaction_id;
     276             :                 const char *client_name;
     277             :                 const char *date;
     278             :                 const char *comment;
     279             :                 int ret;
     280             : 
     281             :                 ret = db_loadf(ss, "%i%s%s%s", &transaction_id, &client_name,
     282             :                                &date, &comment);
     283             :                 if (ret != 0)
     284             :                         continue;
     285             : 
     286             :                 (*func)(arg, transaction_id, client_name, date, comment);
     287             :         }
     288             : 
     289             :         db_finalize(&ss);
     290             : #endif /* HAVE_CONFIG_ROLLBACKS */
     291             : 
     292           0 :         return NB_OK;
     293             : }

Generated by: LCOV version v1.16-topotato