Sfoglia il codice sorgente

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 anni fa
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
    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
    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
@@ -1063,7 +1068,7 @@ Chapter 2. Module parameter for database access.
 
    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
 ...

+ 43 - 1
modules/carrierroute/carrierroute.c

@@ -41,6 +41,7 @@
 #include "../../str.h"
 #include "../../mem/mem.h"
 #include "../../ut.h" /* for user2uid() */
+#include "../../rpc_lookup.h" /* for sercmd */
 #include "carrierroute.h"
 #include "cr_fixup.h"
 #include "cr_map.h"
@@ -51,6 +52,9 @@
 #include "config.h"
 #include <sys/stat.h>
 
+#define AVP_CR_URIS "_cr_uris"
+int_str cr_uris_avp; // contains all PSTN destinations
+
 MODULE_VERSION
 
 str carrierroute_db_url = str_init(DEFAULT_RODB_URL);
@@ -129,6 +133,8 @@ static mi_export_t mi_cmds[] = {
 	{ 0, 0, 0, 0, 0}
 };
 
+static rpc_export_t rpc_methods[];
+
 struct module_exports exports = {
 	"carrierroute",
 	DEFAULT_DLFLAGS, /* dlopen flags */
@@ -164,6 +170,11 @@ static int mod_init(void) {
 		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_username_col.len = strlen(subscriber_username_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){
 		carrierroute_db_close();
 	}
+
+	cr_uris_avp.s.s = AVP_CR_URIS;
+	cr_uris_avp.s.len = sizeof(AVP_CR_URIS) -1;
+
 	return 0;
 }
 
@@ -263,10 +278,37 @@ static int mi_child_init(void) {
 	return 0;
 }
 
-
 static void mod_destroy(void) {
 	if(mode == CARRIERROUTE_MODE_DB){
 		carrierroute_db_close();
 	}
 	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
 
 #include "../../str.h"
+#include "../../usr_avp.h"
 
 #define DICE_MAX 1000
 
@@ -53,4 +54,6 @@ extern const str CR_EMPTY_PREFIX;
 extern int mode;
 extern int cr_match_mode;
 
+extern int_str cr_uris_avp;
+
 #endif

+ 118 - 3
modules/carrierroute/cr_func.c

@@ -49,6 +49,8 @@
 #include "carrierroute.h"
 #include "config.h"
 
+#define MAX_DESTINATIONS 64
+
 enum hash_algorithm {
 	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
@@ -269,6 +271,65 @@ static struct route_rule * get_rule_by_hash(const struct route_flags * rf,
 	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
@@ -288,7 +349,6 @@ static int actually_rewrite(const struct route_rule *rs, str *dest,
 	char *p;
 	int_str avp_val;
 	int strip = 0;
-
 	str l_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) {
 		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) {
 				LM_ERR("invalid dice_max value\n");
 				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");
 				return -1;
 			}
+
 			/* This auto-magically takes the last rule if anything is broken.
 			 * 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
 			 * zero and the message could not be routed at all if we use
 			 * '<' 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;
-			        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->backup) {
 					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;
 				}
 			}
+
+			//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;
+		}
 		case alg_crc32_nofallback:
 			if ((prob = (hash_func(msg, hash_source, rf->max_targets))) < 0) {
 				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
         informations that belongs to each gw, like the destination or the
         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>
         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