Browse code

Merge pull request #2893 from CFrits/master

db_cluster: RPC commands to control database connections

Daniel-Constantin Mierla authored on 27/10/2021 12:52:20 • GitHub committed on 27/10/2021 12:52:20
Showing 5 changed files
... ...
@@ -22,6 +22,7 @@
22 22
 
23 23
 #include "../../core/sr_module.h"
24 24
 #include "../../core/dprint.h"
25
+#include "../../core/rpc_lookup.h"
25 26
 #include "../../lib/srdb1/db.h"
26 27
 #include "dbcl_data.h"
27 28
 #include "dbcl_api.h"
... ...
@@ -56,6 +57,8 @@ static param_export_t params[] = {
56 57
 	{0, 0, 0}
57 58
 };
58 59
 
60
+static rpc_export_t rpc_methods[];
61
+
59 62
 struct module_exports exports = {
60 63
 	"db_cluster",		/* module name */
61 64
 	DEFAULT_DLFLAGS,	/* dlopen flags */
... ...
@@ -73,6 +76,12 @@ struct module_exports exports = {
73 76
 int mod_init(void)
74 77
 {
75 78
 	LM_DBG("Setting up DB cluster\n");
79
+
80
+	if(rpc_register_array(rpc_methods) != 0) {
81
+		LM_ERR("failed to register RPC commands\n");
82
+		return -1;
83
+	}
84
+
76 85
 	return 0;
77 86
 }
78 87
 
... ...
@@ -112,3 +121,243 @@ int dbcl_cls_param(modparam_t type, void *val)
112 121
 {
113 122
 	return dbcl_parse_cls_param((char*)val);
114 123
 }
124
+
125
+str dbcl_connection_active_str = str_init("active");
126
+str dbcl_connection_disabled_str = str_init("disabled");
127
+
128
+str *dbcl_get_status_str(int status)
129
+{
130
+	if (status & DBCL_CON_INACTIVE)
131
+		return &dbcl_connection_disabled_str;
132
+	else
133
+		return &dbcl_connection_active_str;
134
+
135
+	return 0;
136
+}
137
+
138
+static int dbcl_active_count_connections(str cluster)
139
+{
140
+	dbcl_cls_t *cls=NULL;
141
+	int count = 0;
142
+	int i, j;
143
+
144
+	cls = dbcl_get_cluster(&cluster);
145
+
146
+	if(cls==NULL)
147
+	{
148
+		LM_ERR("cluster not found [%.*s]\n", cluster.len, cluster.s);
149
+		return 0;
150
+	}
151
+
152
+	for(i=1; i<DBCL_PRIO_SIZE; i++)
153
+	{
154
+		for(j=0; j<cls->rlist[i].clen; j++)
155
+		{
156
+			if(cls->rlist[i].clist[j] != NULL)
157
+			{
158
+				LM_INFO("read connection [%.*s]\n", cls->rlist[i].clist[j]->name.len, cls->rlist[i].clist[j]->name.s);
159
+
160
+				if(cls->rlist[i].clist[j]->sinfo==NULL)
161
+					return 0;
162
+
163
+				if(cls->rlist[i].clist[j]->sinfo->state == 0)
164
+				{
165
+					count++;
166
+				}
167
+			}
168
+		}
169
+	}
170
+
171
+	return count;
172
+}
173
+
174
+static void dbcl_rpc_list_connections(rpc_t *rpc, void *c)
175
+{
176
+	void *handle;
177
+	dbcl_cls_t *cls=NULL;
178
+	str cluster;
179
+	int i, j;
180
+	unsigned int ticks;
181
+
182
+	if (rpc->scan(c, "S", &cluster) != 1) {
183
+		rpc->fault(c, 500, "Not enough parameters (cluster)");
184
+		return;
185
+	}
186
+
187
+	cls = dbcl_get_cluster(&cluster);
188
+
189
+	if(cls==NULL)
190
+	{
191
+		LM_ERR("cluster not found [%.*s]\n", cluster.len, cluster.s);
192
+		return;
193
+	}
194
+
195
+	for(i=1; i<DBCL_PRIO_SIZE; i++)
196
+	{
197
+		for(j=0; j<cls->rlist[i].clen; j++)
198
+		{
199
+			if(cls->rlist[i].clist[j] != NULL)
200
+			{
201
+				LM_INFO("read connection [%.*s]\n", cls->rlist[i].clist[j]->name.len, cls->rlist[i].clist[j]->name.s);
202
+
203
+				if(rpc->add(c, "{", &handle) < 0)
204
+					goto error;
205
+
206
+				if(cls->rlist[i].clist[j]->sinfo==NULL)
207
+					goto error;
208
+
209
+				if (cls->rlist[i].clist[j]->sinfo->aticks != 0)
210
+					ticks = cls->rlist[i].clist[j]->sinfo->aticks - get_ticks();
211
+				else
212
+					ticks = 0;
213
+
214
+				if(rpc->struct_add(handle, "SSdSdd", "connection", &cls->rlist[i].clist[j]->name, 
215
+								"url", &cls->rlist[i].clist[j]->db_url,
216
+								"flags", cls->rlist[i].clist[j]->flags,
217
+								"state", dbcl_get_status_str(cls->rlist[i].clist[j]->sinfo->state),
218
+								"ticks", ticks,
219
+								"ref", cls->ref) 
220
+						< 0)
221
+					goto error;
222
+			}
223
+		}
224
+	}
225
+
226
+	return;
227
+
228
+error:
229
+	LM_ERR("Failed to add item to RPC response\n");
230
+	rpc->fault(c, 500, "Server failure");
231
+	return;
232
+}
233
+
234
+static void dbcl_rpc_disable_connection(rpc_t *rpc, void *c)
235
+{
236
+	dbcl_cls_t *cls=NULL;
237
+	dbcl_con_t *con=NULL;
238
+	str cluster;
239
+	str connection;
240
+	int seconds;
241
+
242
+	if (rpc->scan(c, "SSd", &cluster, &connection, &seconds) < 3) {
243
+		rpc->fault(c, 500, "Not enough parameters (cluster) (connection) (seconds)");
244
+		return;
245
+	}
246
+
247
+	cls = dbcl_get_cluster(&cluster);
248
+
249
+	if(cls==NULL)
250
+	{
251
+		LM_INFO("cluster not found [%.*s]\n", cluster.len, cluster.s);
252
+		return;
253
+	}
254
+
255
+	con = dbcl_get_connection(&connection);
256
+
257
+	if(con==NULL)
258
+	{
259
+		LM_INFO("connection not found [%.*s]\n", connection.len, connection.s);
260
+		return;
261
+	}
262
+
263
+	if(con->sinfo==NULL)
264
+		return;
265
+
266
+	/* Overwrite the number of seconds if the connection is already disabled. */
267
+	if (con->sinfo->state & DBCL_CON_INACTIVE)
268
+	{
269
+		dbcl_disable_con(con, seconds);
270
+		return;
271
+	}
272
+
273
+	if (dbcl_active_count_connections(cluster) <= 1)
274
+	{
275
+		rpc->fault(c, 500, "Cannot disable last active connection in a cluster");
276
+		return;
277
+	}
278
+
279
+	dbcl_disable_con(con, seconds);
280
+
281
+	return;
282
+}
283
+
284
+static void dbcl_rpc_enable_connection(rpc_t *rpc, void *c)
285
+{
286
+	dbcl_cls_t *cls=NULL;
287
+	dbcl_con_t *con=NULL;
288
+	str cluster;
289
+	str connection;
290
+
291
+	if (rpc->scan(c, "SS", &cluster, &connection) < 2) {
292
+		rpc->fault(c, 500, "Not enough parameters (cluster) (connection)");
293
+		return;
294
+	}
295
+
296
+	cls = dbcl_get_cluster(&cluster);
297
+
298
+	if(cls==NULL)
299
+	{   
300
+		LM_INFO("cluster not found [%.*s]\n", cluster.len, cluster.s);
301
+		return;
302
+	}
303
+
304
+	con = dbcl_get_connection(&connection);
305
+
306
+	if(con==NULL)
307
+	{   
308
+		LM_INFO("connection not found [%.*s]\n", connection.len, connection.s);
309
+		return;
310
+	}
311
+
312
+	dbcl_enable_con(con);
313
+
314
+	return;
315
+}
316
+
317
+static void dbcl_rpc_list_clusters(rpc_t *rpc, void *c)
318
+{
319
+	void *handle;
320
+	dbcl_cls_t *cls=NULL;
321
+
322
+	cls = dbcl_get_cluster_root();
323
+
324
+	if(cls==NULL)
325
+	{
326
+		LM_ERR("root not set\n");
327
+		return;
328
+	}
329
+
330
+	while(cls)
331
+	{
332
+		LM_INFO("cluster found ID [%u] NAME [%.*s]\n", cls->clsid, cls->name.len, cls->name.s);
333
+
334
+		if(rpc->add(c, "{", &handle) < 0)
335
+			goto error;
336
+
337
+		if(rpc->struct_add(handle, "S", "cluster", &cls->name) < 0)
338
+			goto error;
339
+
340
+		cls = cls->next;
341
+	}
342
+
343
+	return;
344
+
345
+error:
346
+	LM_ERR("Failed to add item to RPC response\n");
347
+	rpc->fault(c, 500, "Server failure");
348
+	return;
349
+}
350
+
351
+
352
+static const char *dbcl_rpc_list_clusters_doc[2] = {"Print all clusters", 0};
353
+static const char *dbcl_rpc_list_connections_doc[2] = {"Print all database connections of a cluster", 0};
354
+static const char *dbcl_rpc_disable_connection_doc[2] = {"Disable a connection of a cluster for a period", 0};
355
+static const char *dbcl_rpc_enable_connection_doc[2] = {"Enable a connection of a cluster", 0};
356
+
357
+static rpc_export_t rpc_methods[] = {
358
+	{"dbcl.list_clusters", dbcl_rpc_list_clusters, dbcl_rpc_list_clusters_doc, RET_ARRAY},
359
+	{"dbcl.list_connections", dbcl_rpc_list_connections, dbcl_rpc_list_connections_doc, RET_ARRAY},
360
+	{"dbcl.disable_connection", dbcl_rpc_disable_connection, dbcl_rpc_disable_connection_doc, 0},
361
+	{"dbcl.enable_connection", dbcl_rpc_enable_connection, dbcl_rpc_enable_connection_doc, 0},
362
+	{0, 0, 0, 0}
363
+};
... ...
@@ -80,6 +80,15 @@ dbcl_cls_t *dbcl_get_cluster(str *name)
80 80
 	return NULL;
81 81
 }
82 82
 
83
+dbcl_cls_t *dbcl_get_cluster_root()
84
+{
85
+	return _dbcl_cls_root;
86
+}
87
+
88
+dbcl_con_t *dbcl_get_connection_root()
89
+{
90
+	return _dbcl_con_root;
91
+}
83 92
 
84 93
 int dbcl_init_con(str *name, str *url)
85 94
 {
... ...
@@ -152,6 +161,30 @@ int dbcl_inactive_con(dbcl_con_t *sc)
152 161
 	return 0;
153 162
 }
154 163
 
164
+int dbcl_disable_con(dbcl_con_t *sc, int seconds)
165
+{
166
+	LM_INFO("disable connection [%.*s] for %d seconds\n", sc->name.len, sc->name.s, seconds);
167
+
168
+	if(sc==NULL || sc->sinfo==NULL)
169
+		return -1;
170
+	sc->sinfo->aticks = get_ticks() + seconds;
171
+	sc->sinfo->state |= DBCL_CON_INACTIVE;
172
+	return 0;
173
+}
174
+
175
+int dbcl_enable_con(dbcl_con_t *sc)
176
+{
177
+	LM_INFO("enable connection [%.*s]\n", sc->name.len, sc->name.s);
178
+
179
+	if(sc==NULL || sc->flags==0 || sc->dbh==NULL)
180
+		return -1;
181
+	if(sc->sinfo==NULL)
182
+		return 0;
183
+	sc->sinfo->aticks = 0;
184
+	sc->sinfo->state &= ~DBCL_CON_INACTIVE;
185
+	return 0;
186
+}
187
+
155 188
 int dbcl_parse_con_param(char *val)
156 189
 {
157 190
 	str name;
... ...
@@ -83,9 +83,14 @@ int dbcl_init_dbf(dbcl_cls_t *cls);
83 83
 int dbcl_init_connections(dbcl_cls_t *cls);
84 84
 int dbcl_close_connections(dbcl_cls_t *cls);
85 85
 dbcl_cls_t *dbcl_get_cluster(str *name);
86
+dbcl_con_t *dbcl_get_connection(str *name);
87
+dbcl_cls_t *dbcl_get_cluster_root();
88
+dbcl_con_t *dbcl_get_connection_root();
86 89
 
87 90
 int dbcl_valid_con(dbcl_con_t *sc);
88 91
 int dbcl_inactive_con(dbcl_con_t *sc);
92
+int dbcl_disable_con(dbcl_con_t *sc, int seconds);
93
+int dbcl_enable_con(dbcl_con_t *sc);
89 94
 
90 95
 int dbcl_parse_con_param(char *val);
91 96
 int dbcl_parse_cls_param(char *val);
... ...
@@ -23,6 +23,11 @@
23 23
 		<surname>Mierla</surname>
24 24
 		<email>miconda@gmail.com</email>
25 25
 	    </editor>
26
+	    <editor>
27
+		<firstname>Frits</firstname>
28
+		<surname>Wiersma</surname>
29
+		<email>frits.wiersma@isp.solcon.nl</email>
30
+	    </editor>
26 31
 	</authorgroup>
27 32
 	<copyright>
28 33
 	    <year>2012</year>
... ...
@@ -216,5 +216,66 @@ modparam("sqlops", "sqlcon", "ca=&gt;cluster://k1")
216 216
 </programlisting>
217 217
 		</example>
218 218
 	</section>
219
+    <section>
220
+    <title>RPC Commands</title>
221
+
222
+        <section id="dbcl.list_clusters">
223
+        <title>dbcl.list_clusters</title>
224
+        <para>Lists all database clusters.</para>
225
+        <para>Name: <emphasis>dbcl.list_clusters</emphasis></para>
226
+        <para>RPC Command Format:</para>
227
+        <programlisting  format="linespecific">
228
+...
229
+&kamcmd; dbcl.list_clusters
230
+...
231
+        </programlisting>
232
+        </section>
233
+
234
+        <section id="dbcl.list_connections">
235
+        <title>dbcl.list_connections</title>
236
+        <para>Lists all database connections of a specific cluster.</para>
237
+        <para>Name: <emphasis>dbcl.list_connections</emphasis></para>
238
+        <para>RPC Command Format:</para>
239
+        <programlisting  format="linespecific">
240
+...
241
+&kamcmd; dbcl.list_connections clustername
242
+...
243
+        </programlisting>
244
+        </section>
245
+
246
+        <section id="dbcl.disable_connection">
247
+        <title>dbcl.disable_connection</title>
248
+        <para>
249
+		Disable a database connection of a specific cluster for a period.
250
+		The database connection remains open but will not be used for the specified period (seconds).
251
+		The command does not allow to close all database connections of the specific cluster.
252
+		If the database connection is already disabled than the new period will overwrite the old period.
253
+		</para>
254
+        <para>Name: <emphasis>dbcl.disable_connection</emphasis></para>
255
+        <para>RPC Command Format:</para>
256
+        <programlisting  format="linespecific">
257
+...
258
+&kamcmd; dbcl.disable_connection clustername connectionname period
259
+...
260
+        </programlisting>
261
+        </section>
262
+
263
+        <section id="dbcl.enable_connection">
264
+        <title>dbcl.enable_connection</title>
265
+        <para>
266
+		Enable a database connection of a specific cluster.
267
+		</para>
268
+        <para>Name: <emphasis>dbcl.enable_connection</emphasis></para>
269
+        <para>RPC Command Format:</para>
270
+        <programlisting  format="linespecific">
271
+...
272
+&kamcmd; dbcl.enable_connection clustername connectionname
273
+...
274
+        </programlisting>
275
+        </section>
276
+
277
+
278
+	</section>
279
+
219 280
 </chapter>
220 281