Add persistent dynamic queue member support (bug #2929)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4390 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Mark Spencer 2004-12-06 05:54:16 +00:00
parent 0ceb14b9a4
commit 0455688ea1
2 changed files with 165 additions and 2 deletions

View File

@ -7,6 +7,16 @@
* *
* Mark Spencer <markster@digium.com> * Mark Spencer <markster@digium.com>
* *
* 2004-11-25: Persistent Dynamic Members added by:
* NetNation Communications (www.netnation.com)
* Kevin Lindsay <kevinl@netnation.com>
*
* Each dynamic agent in each queue is now stored in the astdb.
* When asterisk is restarted, each agent will be automatically
* readded into their recorded queues. This feature can be
* configured with the 'peristent_members=<1|0>' KVP under the
* '[general]' group in queues.conf. The default is on.
*
* 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr). * 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
* *
* These features added by David C. Troy <dave@toad.net>: * These features added by David C. Troy <dave@toad.net>:
@ -22,7 +32,7 @@
* Added servicelevel statistic by Michiel Betel <michiel@betel.nl> * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
* Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com> * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
* *
* Fixed ot work with CVS as of 2004-02-25 and released as 1.07a * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
* by Matthew Enger <m.enger@xi.com.au> * by Matthew Enger <m.enger@xi.com.au>
* *
* This program is free software, distributed under the terms of * This program is free software, distributed under the terms of
@ -46,6 +56,7 @@
#include <asterisk/monitor.h> #include <asterisk/monitor.h>
#include <asterisk/utils.h> #include <asterisk/utils.h>
#include <asterisk/causes.h> #include <asterisk/causes.h>
#include <asterisk/astdb.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
@ -136,6 +147,13 @@ static char *app_rqm_descrip =
"Example: RemoveQueueMember(techsupport|SIP/3000)\n" "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
""; "";
/* Persistent Members astdb family */
static const char *pm_family = "/Queue/PersistentMembers";
/* The maximum lengh of each persistent member queue database entry */
#define PM_MAX_LEN 2048
/* queues.conf [general] option */
static int queue_persistent_members = 0;
/* We define a customer "local user" structure because we /* We define a customer "local user" structure because we
use it not only for keeping track of what is in use but use it not only for keeping track of what is in use but
also for keeping track of who we're dialing. */ also for keeping track of who we're dialing. */
@ -1542,6 +1560,46 @@ static struct member * create_queue_node( char * interface, int penalty )
return( cur ) ; return( cur ) ;
} }
/* Dump all members in a specific queue to the databse
*
* <pm_family>/<queuename> = <interface>;<penalty>;...
*
*/
static void dump_queue_members(struct ast_call_queue *pm_queue)
{
struct member *cur_member = NULL;
char value[PM_MAX_LEN];
int value_len = 0;
int res;
memset(value, 0, sizeof(value));
if (pm_queue) {
cur_member = pm_queue->members;
while (cur_member) {
if (cur_member->dynamic) {
value_len = strlen(value);
res = snprintf(value+value_len, sizeof(value)-value_len, "%s/%s;%d;", cur_member->tech, cur_member->loc, cur_member->penalty);
if (res != strlen(value + value_len)) {
ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
break;
}
}
cur_member = cur_member->next;
}
if (!ast_strlen_zero(value) && !cur_member) {
if (ast_db_put(pm_family, pm_queue->name, value))
ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
} else {
/* Delete the entry if the queue is empty or there is an error */
ast_db_del(pm_family, pm_queue->name);
}
}
}
static int remove_from_queue(char *queuename, char *interface) static int remove_from_queue(char *queuename, char *interface)
{ {
struct ast_call_queue *q; struct ast_call_queue *q;
@ -1570,6 +1628,10 @@ static int remove_from_queue(char *queuename, char *interface)
"Location: %s/%s\r\n", "Location: %s/%s\r\n",
q->name, last_member->tech, last_member->loc); q->name, last_member->tech, last_member->loc);
free(last_member); free(last_member);
if (queue_persistent_members)
dump_queue_members(q);
res = RES_OKAY; res = RES_OKAY;
} else { } else {
res = RES_EXISTS; res = RES_EXISTS;
@ -1610,6 +1672,10 @@ static int add_to_queue(char *queuename, char *interface, int penalty)
"Status: %d\r\n", "Status: %d\r\n",
q->name, new_member->tech, new_member->loc, new_member->dynamic ? "dynamic" : "static", q->name, new_member->tech, new_member->loc, new_member->dynamic ? "dynamic" : "static",
new_member->penalty, new_member->calls, new_member->lastcall, new_member->status); new_member->penalty, new_member->calls, new_member->lastcall, new_member->status);
if (queue_persistent_members)
dump_queue_members(q);
res = RES_OKAY; res = RES_OKAY;
} else { } else {
res = RES_OUTOFMEMORY; res = RES_OUTOFMEMORY;
@ -1626,6 +1692,86 @@ static int add_to_queue(char *queuename, char *interface, int penalty)
return res; return res;
} }
/* Add members saved in the queue members DB file saves
* created by dump_queue_members(), back into the queues */
static void reload_queue_members(void)
{
char *cur_pm_ptr;
char *pm_queue_name;
char *pm_interface;
char *pm_penalty_tok;
int pm_penalty = 0;
struct ast_db_entry *pm_db_tree = NULL;
int pm_family_len = 0;
struct ast_call_queue *cur_queue = NULL;
char queue_data[PM_MAX_LEN];
pm_db_tree = ast_db_gettree(pm_family, NULL);
pm_family_len = strlen(pm_family);
ast_mutex_lock(&qlock);
/* Each key in 'pm_family' is the name of a specific queue in which
* we will reload members into. */
while (pm_db_tree) {
pm_queue_name = pm_db_tree->key+pm_family_len+2;
cur_queue = queues;
while (cur_queue) {
ast_mutex_lock(&cur_queue->lock);
if (strcmp(pm_queue_name, cur_queue->name) == 0)
break;
ast_mutex_unlock(&cur_queue->lock);
cur_queue = cur_queue->next;
}
if (!cur_queue) {
/* If the queue no longer exists, remove it from the
* database */
ast_db_del(pm_family, pm_queue_name);
pm_db_tree = pm_db_tree->next;
continue;
} else
ast_mutex_unlock(&cur_queue->lock);
if (!ast_db_get(pm_family, pm_queue_name, queue_data, PM_MAX_LEN)) {
/* Parse each <interface>;<penalty>; from the value of the
* queuename key and add it to the respective queue */
cur_pm_ptr = queue_data;
while ((pm_interface = strsep(&cur_pm_ptr, ";"))) {
if (!(pm_penalty_tok = strsep(&cur_pm_ptr, ";"))) {
ast_log(LOG_WARNING, "Error parsing corrupted Queue DB string for '%s'\n", pm_queue_name);
break;
}
pm_penalty = strtol(pm_penalty_tok, NULL, 10);
if (errno == ERANGE) {
ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", pm_penalty_tok);
break;
}
if (option_debug)
ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d\n", pm_queue_name, pm_interface, pm_penalty);
if (add_to_queue(pm_queue_name, pm_interface, pm_penalty) == RES_OUTOFMEMORY) {
ast_log(LOG_ERROR, "Out of Memory\n");
break;
}
}
}
pm_db_tree = pm_db_tree->next;
}
ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n");
ast_mutex_unlock(&qlock);
if (pm_db_tree) {
ast_db_freetree(pm_db_tree);
pm_db_tree = NULL;
}
}
static int rqm_exec(struct ast_channel *chan, void *data) static int rqm_exec(struct ast_channel *chan, void *data)
{ {
int res=-1; int res=-1;
@ -1994,6 +2140,8 @@ static void reload_queues(void)
struct ast_variable *var; struct ast_variable *var;
struct member *prev, *cur; struct member *prev, *cur;
int new; int new;
char *general_val = NULL;
cfg = ast_load("queues.conf"); cfg = ast_load("queues.conf");
if (!cfg) { if (!cfg) {
ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n"); ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
@ -2178,6 +2326,11 @@ static void reload_queues(void)
queues = q; queues = q;
} }
} }
} else {
/* Initialize global settings */
queue_persistent_members = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
queue_persistent_members = ast_true(general_val);
} }
cat = ast_category_browse(cfg, cat); cat = ast_category_browse(cfg, cat);
} }
@ -2728,6 +2881,10 @@ int load_module(void)
ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ; ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
} }
reload_queues(); reload_queues();
if (queue_persistent_members)
reload_queue_members();
return res; return res;
} }

View File

@ -1,7 +1,13 @@
[general] [general]
; ;
; Global settings for call queues ; Global settings for call queues
; (none exist currently) ;
; Persistent Members
; Store each dynamic agent in each queue in the astdb so that
; when asterisk is restarted, each agent will be automatically
; readded into their recorded queues. Default is 'yes'.
;
persistentmembers = yes
; ;
; Note that a timeout to fail out of a queue may be passed as part of application call ; Note that a timeout to fail out of a queue may be passed as part of application call
; from extensions.conf: ; from extensions.conf: