Browse code

modules/carrierroute Improvement for cr_route in failure route

Small improvement in cr_route() function - when it is called from
failure_route it will take care not choose a previously choosen gateway.

Added cr_reload sercmd.

Lucian Balaceanu authored on 20/03/2013 12:27:30 • Anca Vamanu committed on 20/03/2013 12:27:30
Showing 5 changed files
... ...
@@ -511,6 +511,11 @@ descavp)
511 511
    This is useful if you need some additional informations that belongs to
512 512
    each gw, like the destination or the number of channels.
513 513
 
514
+   The function pays special attention to the failurerouting cases, so
515
+   that any destination that has failed to provide a successful response
516
+   will not be reused in a subsequent call of cr_route. This situation can
517
+   appear when different route domains contain a set of common gateways.
518
+
514 519
    This function is only usable with rewrite_user and prefix_matching
515 520
    containing a valid string. This string needs to be numerical if the
516 521
    match_mode parameter is set to 10. It uses the standard CRC32 algorithm
... ...
@@ -1063,7 +1068,7 @@ Chapter 2. Module parameter for database access.
1063 1068
 
1064 1069
    URL to the database containing the data.
1065 1070
 
1066
-   Default value is “mysql://openserro:openserro@localhost/openser”.
1071
+   Default value is “mysql://kamailioro:kamailioro@localhost/kamailio”.
1067 1072
 
1068 1073
    Example 2.1. Set db_url parameter
1069 1074
 ...
... ...
@@ -41,6 +41,7 @@
41 41
 #include "../../str.h"
42 42
 #include "../../mem/mem.h"
43 43
 #include "../../ut.h" /* for user2uid() */
44
+#include "../../rpc_lookup.h" /* for sercmd */
44 45
 #include "carrierroute.h"
45 46
 #include "cr_fixup.h"
46 47
 #include "cr_map.h"
... ...
@@ -51,6 +52,9 @@
51 52
 #include "config.h"
52 53
 #include <sys/stat.h>
53 54
 
55
+#define AVP_CR_URIS "_cr_uris"
56
+int_str cr_uris_avp; // contains all PSTN destinations
57
+
54 58
 MODULE_VERSION
55 59
 
56 60
 str carrierroute_db_url = str_init(DEFAULT_RODB_URL);
... ...
@@ -129,6 +133,8 @@ static mi_export_t mi_cmds[] = {
129 133
 	{ 0, 0, 0, 0, 0}
130 134
 };
131 135
 
136
+static rpc_export_t rpc_methods[];
137
+
132 138
 struct module_exports exports = {
133 139
 	"carrierroute",
134 140
 	DEFAULT_DLFLAGS, /* dlopen flags */
... ...
@@ -164,6 +170,11 @@ static int mod_init(void) {
164 170
 		return -1;
165 171
 	}
166 172
 
173
+	if(rpc_register_array(rpc_methods)!=0) {
174
+		LM_ERR("failed to register RPC commands\n");
175
+		return -1;
176
+	}
177
+
167 178
 	subscriber_table.len = strlen(subscriber_table.s);
168 179
 	subscriber_username_col.len = strlen(subscriber_username_col.s);
169 180
 	subscriber_domain_col.len = strlen(subscriber_domain_col.s);
... ...
@@ -241,6 +252,10 @@ static int mod_init(void) {
241 252
 	if(mode == CARRIERROUTE_MODE_DB){
242 253
 		carrierroute_db_close();
243 254
 	}
255
+
256
+	cr_uris_avp.s.s = AVP_CR_URIS;
257
+	cr_uris_avp.s.len = sizeof(AVP_CR_URIS) -1;
258
+
244 259
 	return 0;
245 260
 }
246 261
 
... ...
@@ -263,10 +278,37 @@ static int mi_child_init(void) {
263 278
 	return 0;
264 279
 }
265 280
 
266
-
267 281
 static void mod_destroy(void) {
268 282
 	if(mode == CARRIERROUTE_MODE_DB){
269 283
 		carrierroute_db_close();
270 284
 	}
271 285
 	destroy_route_data();
272 286
 }
287
+
288
+static const char *rpc_cr_reload_routes_doc[2] = {
289
+	"Reload routes", 0
290
+};
291
+
292
+static void rpc_cr_reload_routes(rpc_t *rpc, void *c) {
293
+
294
+	if(mode == CARRIERROUTE_MODE_DB){
295
+		if (carrierroute_dbh==NULL) {
296
+			carrierroute_dbh = carrierroute_dbf.init(&carrierroute_db_url);
297
+			if(carrierroute_dbh==0 ) {
298
+				LM_ERR("cannot initialize database connection\n");
299
+				return;
300
+			}
301
+		}
302
+	}
303
+
304
+	if ( (reload_route_data())!=0 ) {
305
+		LM_ERR("failed to load routing data\n");
306
+		return;
307
+	}
308
+}
309
+
310
+static rpc_export_t rpc_methods[] = {
311
+	{ "cr.reload_routes",  rpc_cr_reload_routes, rpc_cr_reload_routes_doc, 0},
312
+	{0, 0, 0, 0}
313
+};
314
+
... ...
@@ -31,6 +31,7 @@
31 31
 #define CARRIERROUTE_H
32 32
 
33 33
 #include "../../str.h"
34
+#include "../../usr_avp.h"
34 35
 
35 36
 #define DICE_MAX 1000
36 37
 
... ...
@@ -53,4 +54,6 @@ extern const str CR_EMPTY_PREFIX;
53 54
 extern int mode;
54 55
 extern int cr_match_mode;
55 56
 
57
+extern int_str cr_uris_avp;
58
+
56 59
 #endif
... ...
@@ -49,6 +49,8 @@
49 49
 #include "carrierroute.h"
50 50
 #include "config.h"
51 51
 
52
+#define MAX_DESTINATIONS 64
53
+
52 54
 enum hash_algorithm {
53 55
 	alg_crc32 = 1, /*!< hashing algorithm is CRC32 */
54 56
 	alg_crc32_nofallback, /*!< same algorithm as alg_crc32, with only a backup rule, but no fallback tree is chosen
... ...
@@ -269,6 +271,65 @@ static struct route_rule * get_rule_by_hash(const struct route_flags * rf,
269 271
 	return act_hash;
270 272
 }
271 273
 
274
+// debug functions for cr_uri_avp
275
+/*
276
+static void print_cr_uri_avp(){
277
+	struct search_state st;
278
+	int_str val;
279
+	int elem = 0;
280
+
281
+	if (!search_first_avp( AVP_VAL_STR | AVP_NAME_STR, cr_uris_avp, &val, &st)) {
282
+		LM_DBG("no AVPs - we are done!\n");
283
+		return;
284
+	}
285
+
286
+	LM_DBG("	cr_uri_avp[%d]=%.*s\n", elem++, val.s.len, val.s.s);
287
+
288
+	while (  search_next_avp(&st, &val) ) {
289
+		LM_DBG("	cr_uri_avp[%d]=%.*s\n", elem++, val.s.len, val.s.s);
290
+	}
291
+}
292
+*/
293
+
294
+static void build_used_uris_list(avp_value_t* used_dests, int* no_dests){
295
+	struct search_state st;
296
+	int_str val;
297
+	*no_dests = 0;
298
+
299
+	if (!search_first_avp( AVP_VAL_STR | AVP_NAME_STR, cr_uris_avp, &val, &st)) {
300
+		//LM_DBG("no AVPs - we are done!\n");
301
+		return;
302
+	}
303
+
304
+	used_dests[(*no_dests)++] = val;
305
+	//LM_DBG("	used_dests[%d]=%.*s \n", (*no_dests)-1, used_dests[(*no_dests)-1].s.len, used_dests[(*no_dests)-1].s.s);
306
+
307
+	while ( search_next_avp(&st, &val) ) {
308
+		if ( MAX_DESTINATIONS == *no_dests ) {
309
+			LM_ERR("Too many  AVPs - we are done!\n");
310
+			return;
311
+		}
312
+		used_dests[(*no_dests)++] = val;
313
+		//LM_DBG("	used_dests[%d]=%.*s \n", (*no_dests)-1, used_dests[(*no_dests)-1].s.len, used_dests[(*no_dests)-1].s.s);
314
+	}
315
+
316
+	//LM_DBG("sucessfully built used_uris list!\n");
317
+}
318
+
319
+int cr_uri_already_used(str dest , avp_value_t* used_dests, int no_dests){
320
+	int i;
321
+	for (i=0; i<no_dests; i++){
322
+		if ( (dest.len == used_dests[i].s.len) &&
323
+				(memcmp(dest.s, used_dests[i].s.s, dest.len)==0)){
324
+			LM_NOTICE("Candidate destination <%.*s> was previously used.\n", dest.len, dest.s);
325
+			return 1;
326
+		}
327
+
328
+	}
329
+	//LM_DBG("cr_uri_already_used: Candidate destination <%.*s> was NEVER USED.\n", dest.len, dest.s);
330
+	return 0;
331
+}
332
+
272 333
 
273 334
 /**
274 335
  * does the work for rewrite_on_rule, writes the new URI into dest
... ...
@@ -288,7 +349,6 @@ static int actually_rewrite(const struct route_rule *rs, str *dest,
288 349
 	char *p;
289 350
 	int_str avp_val;
290 351
 	int strip = 0;
291
-
292 352
 	str l_user;
293 353
 	
294 354
 	if( !rs || !dest || !msg || !user) {
... ...
@@ -406,6 +466,11 @@ static int rewrite_on_rule(struct route_flags *rf_head, flag_t flags, str * dest
406 466
 
407 467
 	switch (alg) {
408 468
 		case alg_crc32:
469
+		{
470
+			static avp_value_t used_dests[MAX_DESTINATIONS];
471
+			static int no_dests = 0;
472
+			avp_value_t cr_new_uri;
473
+
409 474
 			if(rf->dice_max == 0) {
410 475
 				LM_ERR("invalid dice_max value\n");
411 476
 				return -1;
... ...
@@ -414,15 +479,48 @@ static int rewrite_on_rule(struct route_flags *rf_head, flag_t flags, str * dest
414 479
 				LM_ERR("could not hash message with CRC32");
415 480
 				return -1;
416 481
 			}
482
+
417 483
 			/* This auto-magically takes the last rule if anything is broken.
418 484
 			 * Sometimes the hash result is zero. If the first rule is off
419 485
 			 * (has a probablility of zero) then it has also a dice_to of
420 486
 			 * zero and the message could not be routed at all if we use
421 487
 			 * '<' here. Thus the '<=' is necessary.
488
+			 *
489
+			 * cr_uri_already_used is a function that checks that the selected
490
+			 * rule has not been previously used as a failed destinatin
422 491
 			 */
492
+
423 493
 			for (rr = rf->rule_list;
424
-			        rr->next != NULL && rr->dice_to <= prob;
425
-		        rr = rr->next) {}
494
+				rr->next!= NULL && rr->dice_to <= prob ; rr = rr->next) {}
495
+
496
+			//LM_DBG("CR: candidate hashed destination is: <%.*s>\n", rr->host.len, rr->host.s);
497
+
498
+			if (is_route_type(FAILURE_ROUTE) && (mode == CARRIERROUTE_MODE_DB) ){
499
+				build_used_uris_list(used_dests, &no_dests);
500
+
501
+				if (cr_uri_already_used(rr->host, used_dests, no_dests) ) {
502
+					//LM_DBG("CR: selecting new destination !!! \n");
503
+					for (rr = rf->rule_list;
504
+								rr!= NULL && cr_uri_already_used(rr->host, used_dests, no_dests); rr = rr->next) {}
505
+					/* are there any destinations that were not already used? */
506
+					if (rr == NULL) {
507
+						LM_NOTICE("All gateways from this group were already used\n");
508
+						return -1;
509
+					}
510
+
511
+					/* this is a hack: we do not take probabilities into consideration if first destination
512
+					 * was previously tried */
513
+
514
+					do {
515
+						int rule_no = rand() % rf->rule_num;
516
+						//LM_DBG("CR: trying rule_no=%d \n", rule_no);
517
+						for (rr = rf->rule_list; (rule_no > 0) && (rr->next!=NULL) ; rule_no-- , rr = rr->next) {}
518
+					} while (cr_uri_already_used(rr->host, used_dests, no_dests));
519
+					LM_DBG("CR: candidate selected destination is: <%.*s>\n", rr->host.len, rr->host.s);
520
+				}
521
+			}
522
+			/*This should be regarded as an ELSE branch for the if above
523
+			 * ( status exists for mode == CARRIERROUTE_MODE_FILE */
426 524
 			if (!rr->status) {
427 525
 				if (!rr->backup) {
428 526
 					LM_ERR("all routes are off\n");
... ...
@@ -435,7 +533,24 @@ static int rewrite_on_rule(struct route_flags *rf_head, flag_t flags, str * dest
435 533
 					rr = rr->backup->rr;
436 534
 				}
437 535
 			}
536
+
537
+			//LM_DBG("CR: destination is: <%.*s>\n", rr->host.len, rr->host.s);
538
+			cr_new_uri.s = rr->host;
539
+			/* insert used destination into avp, in case corresponding request fails and
540
+			 * another destination has to be used; this new destination must not be one
541
+			 * that failed before
542
+			 */
543
+
544
+			if (mode == CARRIERROUTE_MODE_DB){
545
+				if ( add_avp( AVP_VAL_STR | AVP_NAME_STR, cr_uris_avp, cr_new_uri) < 0){
546
+					LM_ERR("set AVP failed\n");
547
+					return -1;
548
+				}
549
+				//print_cr_uri_avp();
550
+			}
551
+
438 552
 			break;
553
+		}
439 554
 		case alg_crc32_nofallback:
440 555
 			if ((prob = (hash_func(msg, hash_source, rf->max_targets))) < 0) {
441 556
 				LM_ERR("could not hash message with CRC32");
... ...
@@ -427,7 +427,13 @@ cr_tree_rewrite_uri(tree, domain)
427 427
         is stored in an AVP). This is useful if you need some additional
428 428
         informations that belongs to each gw, like the destination or the
429 429
         number of channels.
430
-        </para>
430
+		</para>
431
+		<para>
432
+		The function pays special attention to the failurerouting cases, so that
433
+		any destination that has failed to provide a successful response will not
434
+		be reused in a subsequent call of cr_route. This situation can appear when
435
+		different route domains contain a set of common gateways.
436
+		</para>
431 437
         <para>
432 438
         This function is only usable with rewrite_user and prefix_matching
433 439
         containing a valid string. This string needs to be numerical if the match_mode