diff --git a/modules/qrouting/qr_acc.c b/modules/qrouting/qr_acc.c new file mode 100644 index 00000000000..c0c0ab4f03f --- /dev/null +++ b/modules/qrouting/qr_acc.c @@ -0,0 +1,117 @@ +#include "qr_acc.h" + +extern qr_rule_t * qr_rules_start; +int myn = 0; + +int test_acc(struct sip_msg* msg) { + qr_gw_t *gw = qr_rules_start->dest->dst.gw; + struct timespec cur_time; + int_str invite_time; + /* register callback for the responses to this INVITE */ + if(tmb.register_tmcb(msg, 0,TMCB_RESPONSE_IN, qr_check_reply_tmcb, + (void*)gw, 0) <= 0) { + LM_ERR("cannot register TMCB_RESPONSE_IN\n"); + return -1; + } + + /* get the time of INVITE */ + if(clock_gettime(CLOCK_REALTIME, &cur_time) < 0) { + LM_ERR("failed to get system time\n"); + return -1; + } + + invite_time.s.s = (char*)&cur_time; + invite_time.s.len = sizeof(struct timespec); + + /* save the pointer to the time structure in an avp */ + if(add_avp(AVP_VAL_STR, avp_invite_time_pdd, invite_time) < 0) { + LM_ERR("failed to attach avp (time of invite) to transaction\n"); + return -1; + } + return 1; +} + +/* a call for this gateway returned 200OK */ +inline void qr_add_200OK(qr_gw_t * gw) { + lock_get(gw->acc_lock); + ++(gw->current_interval.stats.as); + ++(gw->current_interval.stats.cc); + lock_release(gw->acc_lock); +} + +/* a call for this gateway returned 4XX */ +inline void qr_add_4xx(qr_gw_t * gw) { + lock_get(gw->acc_lock); + ++(gw->current_interval.stats.cc); + lock_release(gw->acc_lock); +} + +/* + * returns the elapsed time in miliseconds from + * a given moment specified by time_t + */ +double get_elapsed_time(struct timespec * start) { + struct timespec now; + double seconds, elapsed = 0, milisec_start, milisec_now; + + if(clock_gettime(CLOCK_REALTIME, &now) < 0) { + LM_ERR("failed to get the current time[RESPONSE]\n"); + return -1; + } + + seconds = difftime(now.tv_sec, start->tv_sec); /* seconds elapsed betwen + now and the initial invite */ + if(seconds < 0) { + LM_ERR("negative time elapsed from INVITE\n"); + return -1; + } + /* compute the difference in 100miliseconds */ + elapsed += (seconds * 1000); + milisec_start = start->tv_nsec/1000000; + milisec_now = now.tv_nsec/1000000; + elapsed += (milisec_now - milisec_start); + + return elapsed; +} + +/* + * checks the response to an INVITE and does accounting accordingly + */ +void qr_check_reply_tmcb(struct cell *cell, int type, struct tmcb_params *ps) { + int_str time_of_invite; + double pdd_tm; + qr_gw_t *gw = (qr_gw_t*)ps->param; + + if(gw == NULL) + return; + + if(ps->code == 180 || ps->code == 183) { /* Ringing */ + if(search_first_avp(AVP_VAL_STR, avp_invite_time_pdd, &time_of_invite, + NULL) < 0) { + LM_ERR("failed to find the avp containing the time of invite "\ + "maybe it is not the first 18X response\n"); + } else if(time_of_invite.s.s != 0) { + if(( pdd_tm = + get_elapsed_time( + (struct timespec*)time_of_invite.s.s)) + < 0) { + return; /* TODO: smth smarter? */ + } + + } + } else if(ps->code >= 200 && ps->code<500) { /* completed calls */ + if(ps->code == 200) { + qr_add_200OK(qr_rules_start->dest[0].dst.gw); + } else if (ps->code != 408 || (ps->code == 408 && (cell->flags & + T_UAC_HAS_RECV_REPLY) )){ /* if it's 408 it must have + one provisional response */ + qr_add_4xx(gw); + } + } else if(ps->code >= 500) { /* 1XX should not be accounted - + provisional responses */ + lock_get(gw->acc_lock); + ++(qr_rules_start->dest[0].dst.gw->current_interval.n.ok); + lock_release(gw->acc_lock); + } +} + diff --git a/modules/qrouting/qr_acc.h b/modules/qrouting/qr_acc.h new file mode 100644 index 00000000000..178ee657d4e --- /dev/null +++ b/modules/qrouting/qr_acc.h @@ -0,0 +1,18 @@ +#ifndef __QR_ACC_H__ +#define __QR_ACC_H__ + +#include + +#include "../tm/tm_load.h" +#include "qr_stats.h" + +struct tm_binds tmb; +int avp_invite_time_pdd; /* AVP for storing the time of the INVITE */ + + +int test_acc(struct sip_msg*); +inline void qr_add_200OK(qr_gw_t * gw); +inline void qr_add_4xx(qr_gw_t*); +void qr_check_reply_tmcb(struct cell*, int ,struct tmcb_params*); + +#endif diff --git a/modules/qrouting/qr_stats.c b/modules/qrouting/qr_stats.c index fd00e605152..967c76339e5 100644 --- a/modules/qrouting/qr_stats.c +++ b/modules/qrouting/qr_stats.c @@ -1,6 +1,6 @@ /** * - * qrouting module: qrouting.c + * qrouting module: qr_stats.c * * Copyright (C) 2004-2005 FhG Fokus * Copyright (C) 2006-2010 Voice Sistem SRL @@ -37,6 +37,9 @@ #include "qr_stats.h" +qr_rule_t * qr_rules_end = NULL; /* used when adding rules */ +qr_rule_t * qr_rules_start = NULL; /* used when updating statistics */ + /* create the samples for a gateway's history */ qr_sample_t * create_history(void) { qr_sample_t * history, *tmp; @@ -103,15 +106,61 @@ void qr_free_gw(qr_gw_t * gw) { shm_free(gw); } -/* a call for this gateway returned 200OK */ -inline void qr_add_200OK(qr_gw_t * gw) { - lock_get(gw->acc_lock); - ++(gw->current_interval.call_stats.as); - ++(gw->current_interval.call_stats.cc); - lock_release(gw->acc_lock); +/* creates a rule n_dest destinations (by default marked as gws) */ +void * qr_create_rule(int n_dest) { + qr_rule_t *new; + int i; + + if((new = (qr_rule_t*)shm_malloc(sizeof(qr_rule_t))) == NULL) { + LM_ERR("no more shm memory\n"); + return NULL; + } + memset(new, 0, sizeof(qr_rule_t)); + + /* prepare an array for adding gateways */ + if((new->dest = (qr_dst_t*)shm_malloc(n_dest*sizeof(qr_dst_t))) == NULL) { + LM_ERR("no more shm memory\n"); + return NULL; + } + + for(i=0; idest[i].type |= QR_DST_GW; + } + return new; +} + +/* marks index_grp destination from the rule as group and creates the gw array */ +int qr_dst_is_grp(void *rule_v, int index_grp, int n_gw) { + qr_rule_t *rule = (qr_rule_t*)rule_v; + + if(rule == NULL) { + LM_ERR("bad rule\n"); + return -1; + } + rule->dest[index_grp].type = 0; + rule->dest[index_grp].type |= QR_DST_GRP; + + rule->dest[index_grp].dst.grp.gw = (qr_gw_t**)shm_malloc(n_gw * + sizeof(qr_gw_t*)); + if(rule->dest[index_grp].dst.grp.gw == NULL) { + LM_ERR("no more shm memory\n"); + return -1; + } + + return 0; + } -/* a call for this gateway returned 4XX */ -void qr_add_4xx(qr_gw_t * gw) { - ++(gw->current_interval.call_stats.cc); +/* add rule to internal rule list */ +void qr_add_rule(void *rule) { + /*TODO: lock per rule */ + qr_rule_t *new = (qr_rule_t*)rule; + + if(qr_rules_end == NULL) { + qr_rules_start = new; + } else { + qr_rules_end->next = new; + } + qr_rules_end = new; } + diff --git a/modules/qrouting/qr_stats.h b/modules/qrouting/qr_stats.h index 5ff6f6d463d..d9499866733 100644 --- a/modules/qrouting/qr_stats.h +++ b/modules/qrouting/qr_stats.h @@ -39,6 +39,8 @@ #define QR_STATUS_DIRTY (1<<0) #define QR_STATUS_DSBL (1<<1) #define QR_STATUS_SKIP (1<<2) +#define MIN_DEST 4 + int qr_n; /* number of intervals in history */ @@ -57,7 +59,7 @@ typedef struct qr_calls { typedef struct qr_stats { qr_n_calls_t n; - qr_calls_t call_stats; + qr_calls_t stats; } qr_stats_t; /* sample interval */ @@ -97,32 +99,24 @@ typedef struct qr_grp { typedef struct qr_dst { union { qr_gw_t * gw; - qr_grp_t * grp; + qr_grp_t grp; } dst; char type; } qr_dst_t; /* destinations associated with a rule */ typedef struct qr_rule { - qr_dst_t **dest; + qr_dst_t *dest; qr_thresholds_t threshold; struct qr_rule *next; } qr_rule_t; -#define qr_init_rule {\ - shm_malloc(sizeof(qr_rule));\ -} - -#define qr_create_destinations(n) {\ - shm_malloc(n * sizeof(qr_dst_t))\ -} - qr_gw_t * qr_create_gw(void); void qr_free_gw(qr_gw_t *); -inline void qr_add_200OK(qr_gw_t * gw); +int qr_dst_is_grp(void *, int, int); +void *qr_create_rule(int); +void qr_add_rule(void*); -qr_rule_t * qr_rules_end; /* used when adding rules */ -qr_rule_t * qr_rules_start; /* used when updating statistics */ #endif diff --git a/modules/qrouting/qrouting.c b/modules/qrouting/qrouting.c index 1cda785a8cd..8dfa1e93229 100644 --- a/modules/qrouting/qrouting.c +++ b/modules/qrouting/qrouting.c @@ -36,15 +36,27 @@ #include "../../timer.h" #include "qr_stats.h" +#include "qr_acc.h" #define T_PROC_LABEL "[qrouting]:sampling interval" static int history = 30; /* the history span in minutes */ static int sampling_interval = 5; /* the sampling interval in seconds */ +extern qr_rule_t * qr_rules_start; + +str avp_invite_time_name_pdd = str_init("$avp(qr_invite_time_pdd)"); +str avp_invite_time_name_ast = str_init("$avp(qr_invite_time_ast)"); + + /* timer use for creating the statistics */ struct sr_timer_process t_proc; +static cmd_export_t cmds[] = { + {"test_acc", (cmd_function)test_acc, 0, 0, 0, + REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE}, + {0, 0, 0, 0, 0, 0} +}; static param_export_t params[] = { {"history", INT_PARAM, &history}, {"sampling_interval", INT_PARAM, &sampling_interval}, @@ -57,13 +69,14 @@ static int qr_exit(void); static void timer_func(void); + struct module_exports exports = { "qrouting", MOD_TYPE_DEFAULT,/* class of this module */ MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ 0, /* OpenSIPS module dependencies */ - 0, /* Exported functions */ + cmds, /* Exported functions */ params, /* Exported parameters */ 0, /* exported statistics */ 0, /* exported MI functions */ @@ -76,12 +89,31 @@ struct module_exports exports = { }; static int qr_init(void){ + qr_rule_t *my_rule; /* FIXME: testing purpose */ + LM_INFO("QR module\n"); LM_DBG("history = %d, sampling_interval = %d\n", history, sampling_interval); register_timer_process(T_PROC_LABEL, (void*)timer_func, NULL, sampling_interval, 0); qr_n = (history * 60)/sampling_interval; /* the number of sampling intervals in history */ + + if(load_tm_api(&tmb) == -1) { + LM_ERR("failed to load tm functions\n"); + } + + /* FIXME:testing purpose */ + my_rule = qr_create_rule(1); + qr_add_rule(my_rule); + qr_rules_start->dest[0].dst.gw = qr_create_gw(); + + /* AVP for storing the time when the INVITE was recvd + * for computing PDD*/ + if(parse_avp_spec(&avp_invite_time_name_pdd, &avp_invite_time_pdd) < 0) { + LM_ERR("failed to get avp id\n"); + return -1; + } + return 0; }