Browse Source

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 12 years ago
parent
commit
6bb98ddd9e

+ 6 - 1
modules/carrierroute/README

@@ -511,6 +511,11 @@ descavp)
    This is useful if you need some additional informations that belongs to
    This is useful if you need some additional informations that belongs to
    each gw, like the destination or the number of channels.
    each gw, like the destination or the number of channels.
 
 
+   The function pays special attention to the failurerouting cases, so
+   that any destination that has failed to provide a successful response
+   will not be reused in a subsequent call of cr_route. This situation can
+   appear when different route domains contain a set of common gateways.
+
    This function is only usable with rewrite_user and prefix_matching
    This function is only usable with rewrite_user and prefix_matching
    containing a valid string. This string needs to be numerical if the
    containing a valid string. This string needs to be numerical if the
    match_mode parameter is set to 10. It uses the standard CRC32 algorithm
    match_mode parameter is set to 10. It uses the standard CRC32 algorithm
@@ -1063,7 +1068,7 @@ Chapter 2. Module parameter for database access.
 
 
    URL to the database containing the data.
    URL to the database containing the data.
 
 
-   Default value is “mysql://openserro:openserro@localhost/openser”.
+   Default value is “mysql://kamailioro:kamailioro@localhost/kamailio”.
 
 
    Example 2.1. Set db_url parameter
    Example 2.1. Set db_url parameter
 ...
 ...

+ 43 - 1
modules/carrierroute/carrierroute.c

@@ -41,6 +41,7 @@
 #include "../../str.h"
 #include "../../str.h"
 #include "../../mem/mem.h"
 #include "../../mem/mem.h"
 #include "../../ut.h" /* for user2uid() */
 #include "../../ut.h" /* for user2uid() */
+#include "../../rpc_lookup.h" /* for sercmd */
 #include "carrierroute.h"
 #include "carrierroute.h"
 #include "cr_fixup.h"
 #include "cr_fixup.h"
 #include "cr_map.h"
 #include "cr_map.h"
@@ -51,6 +52,9 @@
 #include "config.h"
 #include "config.h"
 #include <sys/stat.h>
 #include <sys/stat.h>
 
 
+#define AVP_CR_URIS "_cr_uris"
+int_str cr_uris_avp; // contains all PSTN destinations
+
 MODULE_VERSION
 MODULE_VERSION
 
 
 str carrierroute_db_url = str_init(DEFAULT_RODB_URL);
 str carrierroute_db_url = str_init(DEFAULT_RODB_URL);
@@ -129,6 +133,8 @@ static mi_export_t mi_cmds[] = {
 	{ 0, 0, 0, 0, 0}
 	{ 0, 0, 0, 0, 0}
 };
 };
 
 
+static rpc_export_t rpc_methods[];
+
 struct module_exports exports = {
 struct module_exports exports = {
 	"carrierroute",
 	"carrierroute",
 	DEFAULT_DLFLAGS, /* dlopen flags */
 	DEFAULT_DLFLAGS, /* dlopen flags */
@@ -164,6 +170,11 @@ static int mod_init(void) {
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if(rpc_register_array(rpc_methods)!=0) {
+		LM_ERR("failed to register RPC commands\n");
+		return -1;
+	}
+
 	subscriber_table.len = strlen(subscriber_table.s);
 	subscriber_table.len = strlen(subscriber_table.s);
 	subscriber_username_col.len = strlen(subscriber_username_col.s);
 	subscriber_username_col.len = strlen(subscriber_username_col.s);
 	subscriber_domain_col.len = strlen(subscriber_domain_col.s);
 	subscriber_domain_col.len = strlen(subscriber_domain_col.s);
@@ -241,6 +252,10 @@ static int mod_init(void) {
 	if(mode == CARRIERROUTE_MODE_DB){
 	if(mode == CARRIERROUTE_MODE_DB){
 		carrierroute_db_close();
 		carrierroute_db_close();
 	}
 	}
+
+	cr_uris_avp.s.s = AVP_CR_URIS;
+	cr_uris_avp.s.len = sizeof(AVP_CR_URIS) -1;
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -263,10 +278,37 @@ static int mi_child_init(void) {
 	return 0;
 	return 0;
 }
 }
 
 
-
 static void mod_destroy(void) {
 static void mod_destroy(void) {
 	if(mode == CARRIERROUTE_MODE_DB){
 	if(mode == CARRIERROUTE_MODE_DB){
 		carrierroute_db_close();
 		carrierroute_db_close();
 	}
 	}
 	destroy_route_data();
 	destroy_route_data();
 }
 }
+
+static const char *rpc_cr_reload_routes_doc[2] = {
+	"Reload routes", 0
+};
+
+static void rpc_cr_reload_routes(rpc_t *rpc, void *c) {
+
+	if(mode == CARRIERROUTE_MODE_DB){
+		if (carrierroute_dbh==NULL) {
+			carrierroute_dbh = carrierroute_dbf.init(&carrierroute_db_url);
+			if(carrierroute_dbh==0 ) {
+				LM_ERR("cannot initialize database connection\n");
+				return;
+			}
+		}
+	}
+
+	if ( (reload_route_data())!=0 ) {
+		LM_ERR("failed to load routing data\n");
+		return;
+	}
+}
+
+static rpc_export_t rpc_methods[] = {
+	{ "cr.reload_routes",  rpc_cr_reload_routes, rpc_cr_reload_routes_doc, 0},
+	{0, 0, 0, 0}
+};
+

+ 3 - 0
modules/carrierroute/carrierroute.h

@@ -31,6 +31,7 @@
 #define CARRIERROUTE_H
 #define CARRIERROUTE_H
 
 
 #include "../../str.h"
 #include "../../str.h"
+#include "../../usr_avp.h"
 
 
 #define DICE_MAX 1000
 #define DICE_MAX 1000
 
 
@@ -53,4 +54,6 @@ extern const str CR_EMPTY_PREFIX;
 extern int mode;
 extern int mode;
 extern int cr_match_mode;
 extern int cr_match_mode;
 
 
+extern int_str cr_uris_avp;
+
 #endif
 #endif

+ 118 - 3
modules/carrierroute/cr_func.c

@@ -49,6 +49,8 @@
 #include "carrierroute.h"
 #include "carrierroute.h"
 #include "config.h"
 #include "config.h"
 
 
+#define MAX_DESTINATIONS 64
+
 enum hash_algorithm {
 enum hash_algorithm {
 	alg_crc32 = 1, /*!< hashing algorithm is CRC32 */
 	alg_crc32 = 1, /*!< hashing algorithm is CRC32 */
 	alg_crc32_nofallback, /*!< same algorithm as alg_crc32, with only a backup rule, but no fallback tree is chosen
 	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,
 	return act_hash;
 	return act_hash;
 }
 }
 
 
+// debug functions for cr_uri_avp
+/*
+static void print_cr_uri_avp(){
+	struct search_state st;
+	int_str val;
+	int elem = 0;
+
+	if (!search_first_avp( AVP_VAL_STR | AVP_NAME_STR, cr_uris_avp, &val, &st)) {
+		LM_DBG("no AVPs - we are done!\n");
+		return;
+	}
+
+	LM_DBG("	cr_uri_avp[%d]=%.*s\n", elem++, val.s.len, val.s.s);
+
+	while (  search_next_avp(&st, &val) ) {
+		LM_DBG("	cr_uri_avp[%d]=%.*s\n", elem++, val.s.len, val.s.s);
+	}
+}
+*/
+
+static void build_used_uris_list(avp_value_t* used_dests, int* no_dests){
+	struct search_state st;
+	int_str val;
+	*no_dests = 0;
+
+	if (!search_first_avp( AVP_VAL_STR | AVP_NAME_STR, cr_uris_avp, &val, &st)) {
+		//LM_DBG("no AVPs - we are done!\n");
+		return;
+	}
+
+	used_dests[(*no_dests)++] = val;
+	//LM_DBG("	used_dests[%d]=%.*s \n", (*no_dests)-1, used_dests[(*no_dests)-1].s.len, used_dests[(*no_dests)-1].s.s);
+
+	while ( search_next_avp(&st, &val) ) {
+		if ( MAX_DESTINATIONS == *no_dests ) {
+			LM_ERR("Too many  AVPs - we are done!\n");
+			return;
+		}
+		used_dests[(*no_dests)++] = val;
+		//LM_DBG("	used_dests[%d]=%.*s \n", (*no_dests)-1, used_dests[(*no_dests)-1].s.len, used_dests[(*no_dests)-1].s.s);
+	}
+
+	//LM_DBG("sucessfully built used_uris list!\n");
+}
+
+int cr_uri_already_used(str dest , avp_value_t* used_dests, int no_dests){
+	int i;
+	for (i=0; i<no_dests; i++){
+		if ( (dest.len == used_dests[i].s.len) &&
+				(memcmp(dest.s, used_dests[i].s.s, dest.len)==0)){
+			LM_NOTICE("Candidate destination <%.*s> was previously used.\n", dest.len, dest.s);
+			return 1;
+		}
+
+	}
+	//LM_DBG("cr_uri_already_used: Candidate destination <%.*s> was NEVER USED.\n", dest.len, dest.s);
+	return 0;
+}
+
 
 
 /**
 /**
  * does the work for rewrite_on_rule, writes the new URI into dest
  * 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,
 	char *p;
 	char *p;
 	int_str avp_val;
 	int_str avp_val;
 	int strip = 0;
 	int strip = 0;
-
 	str l_user;
 	str l_user;
 	
 	
 	if( !rs || !dest || !msg || !user) {
 	if( !rs || !dest || !msg || !user) {
@@ -406,6 +466,11 @@ static int rewrite_on_rule(struct route_flags *rf_head, flag_t flags, str * dest
 
 
 	switch (alg) {
 	switch (alg) {
 		case alg_crc32:
 		case alg_crc32:
+		{
+			static avp_value_t used_dests[MAX_DESTINATIONS];
+			static int no_dests = 0;
+			avp_value_t cr_new_uri;
+
 			if(rf->dice_max == 0) {
 			if(rf->dice_max == 0) {
 				LM_ERR("invalid dice_max value\n");
 				LM_ERR("invalid dice_max value\n");
 				return -1;
 				return -1;
@@ -414,15 +479,48 @@ static int rewrite_on_rule(struct route_flags *rf_head, flag_t flags, str * dest
 				LM_ERR("could not hash message with CRC32");
 				LM_ERR("could not hash message with CRC32");
 				return -1;
 				return -1;
 			}
 			}
+
 			/* This auto-magically takes the last rule if anything is broken.
 			/* This auto-magically takes the last rule if anything is broken.
 			 * Sometimes the hash result is zero. If the first rule is off
 			 * Sometimes the hash result is zero. If the first rule is off
 			 * (has a probablility of zero) then it has also a dice_to of
 			 * (has a probablility of zero) then it has also a dice_to of
 			 * zero and the message could not be routed at all if we use
 			 * zero and the message could not be routed at all if we use
 			 * '<' here. Thus the '<=' is necessary.
 			 * '<' here. Thus the '<=' is necessary.
+			 *
+			 * cr_uri_already_used is a function that checks that the selected
+			 * rule has not been previously used as a failed destinatin
 			 */
 			 */
+
 			for (rr = rf->rule_list;
 			for (rr = rf->rule_list;
-			        rr->next != NULL && rr->dice_to <= prob;
-		        rr = rr->next) {}
+				rr->next!= NULL && rr->dice_to <= prob ; rr = rr->next) {}
+
+			//LM_DBG("CR: candidate hashed destination is: <%.*s>\n", rr->host.len, rr->host.s);
+
+			if (is_route_type(FAILURE_ROUTE) && (mode == CARRIERROUTE_MODE_DB) ){
+				build_used_uris_list(used_dests, &no_dests);
+
+				if (cr_uri_already_used(rr->host, used_dests, no_dests) ) {
+					//LM_DBG("CR: selecting new destination !!! \n");
+					for (rr = rf->rule_list;
+								rr!= NULL && cr_uri_already_used(rr->host, used_dests, no_dests); rr = rr->next) {}
+					/* are there any destinations that were not already used? */
+					if (rr == NULL) {
+						LM_NOTICE("All gateways from this group were already used\n");
+						return -1;
+					}
+
+					/* this is a hack: we do not take probabilities into consideration if first destination
+					 * was previously tried */
+
+					do {
+						int rule_no = rand() % rf->rule_num;
+						//LM_DBG("CR: trying rule_no=%d \n", rule_no);
+						for (rr = rf->rule_list; (rule_no > 0) && (rr->next!=NULL) ; rule_no-- , rr = rr->next) {}
+					} while (cr_uri_already_used(rr->host, used_dests, no_dests));
+					LM_DBG("CR: candidate selected destination is: <%.*s>\n", rr->host.len, rr->host.s);
+				}
+			}
+			/*This should be regarded as an ELSE branch for the if above
+			 * ( status exists for mode == CARRIERROUTE_MODE_FILE */
 			if (!rr->status) {
 			if (!rr->status) {
 				if (!rr->backup) {
 				if (!rr->backup) {
 					LM_ERR("all routes are off\n");
 					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
 					rr = rr->backup->rr;
 					rr = rr->backup->rr;
 				}
 				}
 			}
 			}
+
+			//LM_DBG("CR: destination is: <%.*s>\n", rr->host.len, rr->host.s);
+			cr_new_uri.s = rr->host;
+			/* insert used destination into avp, in case corresponding request fails and
+			 * another destination has to be used; this new destination must not be one
+			 * that failed before
+			 */
+
+			if (mode == CARRIERROUTE_MODE_DB){
+				if ( add_avp( AVP_VAL_STR | AVP_NAME_STR, cr_uris_avp, cr_new_uri) < 0){
+					LM_ERR("set AVP failed\n");
+					return -1;
+				}
+				//print_cr_uri_avp();
+			}
+
 			break;
 			break;
+		}
 		case alg_crc32_nofallback:
 		case alg_crc32_nofallback:
 			if ((prob = (hash_func(msg, hash_source, rf->max_targets))) < 0) {
 			if ((prob = (hash_func(msg, hash_source, rf->max_targets))) < 0) {
 				LM_ERR("could not hash message with CRC32");
 				LM_ERR("could not hash message with CRC32");

+ 7 - 1
modules/carrierroute/doc/carrierroute_admin.xml

@@ -427,7 +427,13 @@ cr_tree_rewrite_uri(tree, domain)
         is stored in an AVP). This is useful if you need some additional
         is stored in an AVP). This is useful if you need some additional
         informations that belongs to each gw, like the destination or the
         informations that belongs to each gw, like the destination or the
         number of channels.
         number of channels.
-        </para>
+		</para>
+		<para>
+		The function pays special attention to the failurerouting cases, so that
+		any destination that has failed to provide a successful response will not
+		be reused in a subsequent call of cr_route. This situation can appear when
+		different route domains contain a set of common gateways.
+		</para>
         <para>
         <para>
         This function is only usable with rewrite_user and prefix_matching
         This function is only usable with rewrite_user and prefix_matching
         containing a valid string. This string needs to be numerical if the match_mode
         containing a valid string. This string needs to be numerical if the match_mode