]> git.proxmox.com Git - mirror_frr.git/blame - lib/northbound_db.c
zebra, lib: fix the ZEBRA_INTERFACE_VRF_UPDATE zapi message
[mirror_frr.git] / lib / northbound_db.c
CommitLineData
1c2facd1
RW
1/*
2 * Copyright (C) 2018 NetDEF, Inc.
3 * Renato Westphal
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <zebra.h>
21
22#include "libfrr.h"
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
30int 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 return NB_OK;
63}
64
65int 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->client);
90 /* Always record configurations in the XML format. */
91 if (lyd_print_mem(&config_str, transaction->config->dnode, LYD_XML,
92 LYP_FORMAT | LYP_WITHSIBLINGS)
93 != 0)
94 goto exit;
95
96 if (db_bindf(ss, "%s%s%s", client_name, strlen(client_name),
97 transaction->comment, strlen(transaction->comment),
98 config_str ? config_str : "",
99 config_str ? strlen(config_str) : 0)
100 != 0)
101 goto exit;
102
103 if (db_run(ss) != SQLITE_OK)
104 goto exit;
105
106 db_finalize(&ss);
107
108 /*
109 * transaction_id is an optional output parameter that provides the ID
110 * of the recorded transaction.
111 */
112 if (transaction_id) {
113 ss = db_prepare("SELECT last_insert_rowid();");
114 if (!ss)
115 goto exit;
116
117 if (db_run(ss) != SQLITE_ROW)
118 goto exit;
119
120 if (db_loadf(ss, "%i", transaction_id) != 0)
121 goto exit;
122
123 db_finalize(&ss);
124 }
125
126 if (db_execute("COMMIT;") != 0)
127 goto exit;
128
129 ret = NB_OK;
130
131exit:
132 if (config_str)
133 free(config_str);
134 if (ss)
135 db_finalize(&ss);
136 if (ret != NB_OK)
137 (void)db_execute("ROLLBACK TRANSACTION;");
138
139 return ret;
140#else /* HAVE_CONFIG_ROLLBACKS */
141 return NB_OK;
142#endif /* HAVE_CONFIG_ROLLBACKS */
143}
144
145struct nb_config *nb_db_transaction_load(uint32_t transaction_id)
146{
147 struct nb_config *config = NULL;
148#ifdef HAVE_CONFIG_ROLLBACKS
149 struct lyd_node *dnode;
150 const char *config_str;
151 struct sqlite3_stmt *ss;
152
153 ss = db_prepare(
154 "SELECT\n"
155 " configuration\n"
156 "FROM\n"
157 " transactions\n"
158 "WHERE\n"
159 " rowid=?;");
160 if (!ss)
161 return NULL;
162
163 if (db_bindf(ss, "%d", transaction_id) != 0)
164 goto exit;
165
166 if (db_run(ss) != SQLITE_ROW)
167 goto exit;
168
169 if (db_loadf(ss, "%s", &config_str) != 0)
170 goto exit;
171
172 dnode = lyd_parse_mem(ly_native_ctx, config_str, LYD_XML,
173 LYD_OPT_CONFIG);
174 if (!dnode)
175 flog_warn(EC_LIB_LIBYANG, "%s: lyd_parse_mem() failed",
176 __func__);
177 else
178 config = nb_config_new(dnode);
179
180exit:
181 db_finalize(&ss);
182#endif /* HAVE_CONFIG_ROLLBACKS */
183
184 return config;
185}
186
187int nb_db_clear_transactions(unsigned int n_oldest)
188{
189#ifdef HAVE_CONFIG_ROLLBACKS
190 /* Delete oldest N entries. */
191 if (db_execute("DELETE\n"
192 "FROM\n"
193 " transactions\n"
194 "WHERE\n"
195 " ROWID IN (\n"
196 " SELECT\n"
197 " ROWID\n"
198 " FROM\n"
199 " transactions\n"
200 " ORDER BY ROWID ASC LIMIT %u\n"
201 " );",
202 n_oldest)
203 != 0)
204 return NB_ERR;
205#endif /* HAVE_CONFIG_ROLLBACKS */
206
207 return NB_OK;
208}
209
210int nb_db_set_max_transactions(unsigned int max)
211{
212#ifdef HAVE_CONFIG_ROLLBACKS
213 /*
214 * Delete old entries if necessary and update the SQL trigger that
215 * auto-deletes old entries.
216 */
217 if (db_execute("BEGIN TRANSACTION;\n"
218 " DELETE\n"
219 " FROM\n"
220 " transactions\n"
221 " WHERE\n"
222 " ROWID IN (\n"
223 " SELECT\n"
224 " ROWID\n"
225 " FROM\n"
226 " transactions\n"
227 " ORDER BY ROWID DESC LIMIT -1 OFFSET %u\n"
228 " );\n"
229 " DROP TRIGGER delete_tail;\n"
230 " CREATE TRIGGER delete_tail\n"
231 " AFTER INSERT ON transactions\n"
232 " FOR EACH ROW\n"
233 " BEGIN\n"
234 " DELETE\n"
235 " FROM\n"
236 " transactions\n"
237 " WHERE\n"
238 " rowid%%%u=NEW.rowid%%%u AND rowid!=NEW.rowid;\n"
239 " END;\n"
240 "COMMIT;",
241 max, max, max)
242 != 0)
243 return NB_ERR;
244#endif /* HAVE_CONFIG_ROLLBACKS */
245
246 return NB_OK;
247}
248
249int nb_db_transactions_iterate(void (*func)(void *arg, int transaction_id,
250 const char *client_name,
251 const char *date,
252 const char *comment),
253 void *arg)
254{
255#ifdef HAVE_CONFIG_ROLLBACKS
256 struct sqlite3_stmt *ss;
257
258 /* Send SQL query and parse the result. */
259 ss = db_prepare(
260 "SELECT\n"
261 " rowid, client, date, comment\n"
262 "FROM\n"
263 " transactions\n"
264 "ORDER BY\n"
265 " rowid DESC;");
266 if (!ss)
267 return NB_ERR;
268
269 while (db_run(ss) == SQLITE_ROW) {
270 int transaction_id;
271 const char *client_name;
272 const char *date;
273 const char *comment;
274 int ret;
275
276 ret = db_loadf(ss, "%i%s%s%s", &transaction_id, &client_name,
277 &date, &comment);
278 if (ret != 0)
279 continue;
280
281 (*func)(arg, transaction_id, client_name, date, comment);
282 }
283
284 db_finalize(&ss);
285#endif /* HAVE_CONFIG_ROLLBACKS */
286
287 return NB_OK;
288}