mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-06 12:36:58 +00:00
app_queue: Add RealTime support for queue rules
This patch gives the optional ability to keep queue rules in RealTime. It is important to note that with this patch: (a) Queue rules in RealTime are only examined on module load/reload (b) Queue rules are loaded both from the queuerules.conf file as well as the RealTime backend To inform app_queue to examine RealTime for queue rules, a new setting has been added to queuerules.conf's general section "realtime_rules". RealTime queue rules will only be used when this setting is set to "yes". The schema for the database table supports a rule_name, time, min_penalty, and max_penalty columns. min_penalty and max_penalty can be relative, if a '-' or '+' literal is provided. Otherwise, the penalties are treated as constants. For example: rule_name, time, min_penalty, max_penalty 'default', '10', '20', '30' 'test2', '20', '30', '55' 'test2', '25', '-11', '+1111' 'test2', '400', '112', '333' 'test3', '0', '4564', '46546' 'test_rule', '40', '15', '50' which would result in : Rule: default - After 10 seconds, adjust QUEUE_MAX_PENALTY to 30 and adjust QUEUE_MIN_PENALTY to 20 Rule: test2 - After 20 seconds, adjust QUEUE_MAX_PENALTY to 55 and adjust QUEUE_MIN_PENALTY to 30 - After 25 seconds, adjust QUEUE_MAX_PENALTY by 1111 and adjust QUEUE_MIN_PENALTY by -11 - After 400 seconds, adjust QUEUE_MAX_PENALTY to 333 and adjust QUEUE_MIN_PENALTY to 112 Rule: test3 - After 0 seconds, adjust QUEUE_MAX_PENALTY to 46546 and adjust QUEUE_MIN_PENALTY to 4564 Rule: test_rule - After 40 seconds, adjust QUEUE_MAX_PENALTY to 50 and adjust QUEUE_MIN_PENALTY to 15 If you use RealTime, the queue rules will be always reloaded on a module reload, even if the underlying file did not change. With the option disabled, the rules will only be reloaded if the file was modified. Review: https://reviewboard.asterisk.org/r/3607/ ASTERISK-23823 #close Reported by: Michael K patches: app_queue.c_realtime_trunk.patch uploaded by Michael K (License 6621) ........ Merged revisions 420624 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@420625 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
13
CHANGES
13
CHANGES
@@ -94,6 +94,19 @@ MixMonitor
|
|||||||
the recording. The option "P" will play a beep to the channel that stops the
|
the recording. The option "P" will play a beep to the channel that stops the
|
||||||
recording.
|
recording.
|
||||||
|
|
||||||
|
Queue
|
||||||
|
------------------
|
||||||
|
* Queue rules can now be stored in a database table, queue_rules. Unlike other
|
||||||
|
RealTime tables, the queue_rules table is only examined on module load or
|
||||||
|
module reload. A new general setting has been added to queuerules.conf,
|
||||||
|
'realtime_rules', which, when set to 'yes', will cause app_queue to look in
|
||||||
|
RealTime for additional queue rules to parse. Note that both the file and
|
||||||
|
the database can be used as a provide of queue rules when 'realtime_rules'
|
||||||
|
is set to 'yes'.
|
||||||
|
|
||||||
|
When app_queue is reloaded, all rules are re-parsed and loaded into memory.
|
||||||
|
There is no caching of RealTime queue rules.
|
||||||
|
|
||||||
ReadFile
|
ReadFile
|
||||||
------------------
|
------------------
|
||||||
* This module was deprecated and has been removed. Users of app_readfile
|
* This module was deprecated and has been removed. Users of app_readfile
|
||||||
|
@@ -66,6 +66,9 @@ ConfBridge:
|
|||||||
new method to achieve this functionality is by using sound_begin to play
|
new method to achieve this functionality is by using sound_begin to play
|
||||||
a sound to the conference when waitmarked users are moved into the conference.
|
a sound to the conference when waitmarked users are moved into the conference.
|
||||||
|
|
||||||
|
Queue:
|
||||||
|
- Queue rules provided in queuerules.conf can no longer be named "general".
|
||||||
|
|
||||||
SetMusicOnHold:
|
SetMusicOnHold:
|
||||||
- The SetMusicOnHold dialplan application was deprecated and has been removed.
|
- The SetMusicOnHold dialplan application was deprecated and has been removed.
|
||||||
Users of the application should use the CHANNEL function's musicclass
|
Users of the application should use the CHANNEL function's musicclass
|
||||||
@@ -342,6 +345,11 @@ Realtime Configuration:
|
|||||||
- A new set of Alembic scripts has been added for CDR tables. This will create
|
- A new set of Alembic scripts has been added for CDR tables. This will create
|
||||||
a 'cdr' table with the default schema that Asterisk expects.
|
a 'cdr' table with the default schema that Asterisk expects.
|
||||||
|
|
||||||
|
- A new upgrade script has been added that adds a 'queue_rules' table for
|
||||||
|
app_queue. Users of app_queue can store queue rules in a database. It is
|
||||||
|
important to note that app_queue only looks for this table on module load or
|
||||||
|
module reload; for more information, see the CHANGES file.
|
||||||
|
|
||||||
Resources:
|
Resources:
|
||||||
|
|
||||||
res_odbc:
|
res_odbc:
|
||||||
|
119
apps/app_queue.c
119
apps/app_queue.c
@@ -1362,6 +1362,9 @@ static int montype_default = 0;
|
|||||||
/*! \brief queues.conf [general] option */
|
/*! \brief queues.conf [general] option */
|
||||||
static int shared_lastcall = 1;
|
static int shared_lastcall = 1;
|
||||||
|
|
||||||
|
/*! \brief queuesrules.conf [general] option */
|
||||||
|
static int realtime_rules = 0;
|
||||||
|
|
||||||
/*! \brief Subscription to device state change messages */
|
/*! \brief Subscription to device state change messages */
|
||||||
static struct stasis_subscription *device_state_sub;
|
static struct stasis_subscription *device_state_sub;
|
||||||
|
|
||||||
@@ -2733,6 +2736,99 @@ static int insert_penaltychange(const char *list_name, const char *content, cons
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Load queue rules from realtime.
|
||||||
|
*
|
||||||
|
* Check rule for errors with time or fomatting, see if rule is relative to rest
|
||||||
|
* of queue, iterate list of rules to find correct insertion point, insert and return.
|
||||||
|
* \retval -1 on failure
|
||||||
|
* \retval 0 on success
|
||||||
|
* \note Call this with the rule_lists locked
|
||||||
|
*/
|
||||||
|
static int load_realtime_rules(void)
|
||||||
|
{
|
||||||
|
struct ast_config *cfg;
|
||||||
|
struct rule_list *rl_iter, *new_rl;
|
||||||
|
struct penalty_rule *pr_iter;
|
||||||
|
char *rulecat = NULL;
|
||||||
|
|
||||||
|
if (!ast_check_realtime("queue_rules")) {
|
||||||
|
ast_log(LOG_WARNING, "Missing \"queue_rules\" in extconfig.conf\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!(cfg = ast_load_realtime_multientry("queue_rules", "rule_name LIKE", "%", SENTINEL))) {
|
||||||
|
ast_log(LOG_WARNING, "Failed to load queue rules from realtime\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
while ((rulecat = ast_category_browse(cfg, rulecat)) && !ast_strlen_zero(rulecat)) {
|
||||||
|
const char *timestr, *maxstr, *minstr;
|
||||||
|
int penaltychangetime, rule_exists = 0, inserted = 0;
|
||||||
|
int max_penalty = 0, min_penalty = 0, min_relative = 0, max_relative = 0;
|
||||||
|
struct penalty_rule *new_penalty_rule = NULL;
|
||||||
|
AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
|
||||||
|
if (!(strcasecmp(rl_iter->name, rulecat))) {
|
||||||
|
rule_exists = 1;
|
||||||
|
new_rl = rl_iter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!rule_exists) {
|
||||||
|
if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
|
||||||
|
ast_config_destroy(cfg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
|
||||||
|
AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
|
||||||
|
}
|
||||||
|
timestr = ast_variable_retrieve(cfg, rulecat, "time");
|
||||||
|
if (!(timestr) || sscanf(timestr, "%30d", &penaltychangetime) != 1) {
|
||||||
|
ast_log(LOG_NOTICE, "Failed to parse time (%s) for one of the %s rules, skipping it\n",
|
||||||
|
(ast_strlen_zero(timestr) ? "invalid value" : timestr), rulecat);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(new_penalty_rule = ast_calloc(1, sizeof(*new_penalty_rule)))) {
|
||||||
|
ast_config_destroy(cfg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!(maxstr = ast_variable_retrieve(cfg, rulecat, "max_penalty")) ||
|
||||||
|
ast_strlen_zero(maxstr) || sscanf(maxstr, "%30d", &max_penalty) != 1) {
|
||||||
|
max_penalty = 0;
|
||||||
|
max_relative = 1;
|
||||||
|
} else {
|
||||||
|
if (*maxstr == '+' || *maxstr == '-') {
|
||||||
|
max_relative = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(minstr = ast_variable_retrieve(cfg, rulecat, "min_penalty")) ||
|
||||||
|
ast_strlen_zero(minstr) || sscanf(minstr, "%30d", &min_penalty) != 1) {
|
||||||
|
min_penalty = 0;
|
||||||
|
min_relative = 1;
|
||||||
|
} else {
|
||||||
|
if (*minstr == '+' || *minstr == '-') {
|
||||||
|
min_relative = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new_penalty_rule->time = penaltychangetime;
|
||||||
|
new_penalty_rule->max_relative = max_relative;
|
||||||
|
new_penalty_rule->max_value = max_penalty;
|
||||||
|
new_penalty_rule->min_relative = min_relative;
|
||||||
|
new_penalty_rule->min_value = min_penalty;
|
||||||
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&new_rl->rules, pr_iter, list) {
|
||||||
|
if (new_penalty_rule->time < pr_iter->time) {
|
||||||
|
AST_LIST_INSERT_BEFORE_CURRENT(new_penalty_rule, list);
|
||||||
|
inserted = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AST_LIST_TRAVERSE_SAFE_END;
|
||||||
|
if (!inserted) {
|
||||||
|
AST_LIST_INSERT_TAIL(&new_rl->rules, new_penalty_rule, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_config_destroy(cfg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
|
static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
|
||||||
{
|
{
|
||||||
char *value_copy = ast_strdupa(value);
|
char *value_copy = ast_strdupa(value);
|
||||||
@@ -8384,6 +8480,16 @@ static struct ast_custom_function queuememberpenalty_function = {
|
|||||||
.write = queue_function_memberpenalty_write,
|
.write = queue_function_memberpenalty_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*! Set the global queue rules parameters as defined in the "general" section of queuerules.conf */
|
||||||
|
static void queue_rules_set_global_params(struct ast_config *cfg)
|
||||||
|
{
|
||||||
|
const char *general_val = NULL;
|
||||||
|
realtime_rules = 0;
|
||||||
|
if ((general_val = ast_variable_retrieve(cfg, "general", "realtime_rules"))) {
|
||||||
|
realtime_rules = ast_true(general_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*! \brief Reload the rules defined in queuerules.conf
|
/*! \brief Reload the rules defined in queuerules.conf
|
||||||
*
|
*
|
||||||
* \param reload If 1, then only process queuerules.conf if the file
|
* \param reload If 1, then only process queuerules.conf if the file
|
||||||
@@ -8397,7 +8503,7 @@ static int reload_queue_rules(int reload)
|
|||||||
struct penalty_rule *pr_iter;
|
struct penalty_rule *pr_iter;
|
||||||
char *rulecat = NULL;
|
char *rulecat = NULL;
|
||||||
struct ast_variable *rulevar = NULL;
|
struct ast_variable *rulevar = NULL;
|
||||||
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
|
struct ast_flags config_flags = { (reload && !realtime_rules) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
|
||||||
|
|
||||||
if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
|
if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
|
||||||
ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
|
ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
|
||||||
@@ -8417,6 +8523,10 @@ static int reload_queue_rules(int reload)
|
|||||||
ast_free(rl_iter);
|
ast_free(rl_iter);
|
||||||
}
|
}
|
||||||
while ((rulecat = ast_category_browse(cfg, rulecat))) {
|
while ((rulecat = ast_category_browse(cfg, rulecat))) {
|
||||||
|
if (!strcasecmp(rulecat, "general")) {
|
||||||
|
queue_rules_set_global_params(cfg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
|
if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
|
||||||
AST_LIST_UNLOCK(&rule_lists);
|
AST_LIST_UNLOCK(&rule_lists);
|
||||||
ast_config_destroy(cfg);
|
ast_config_destroy(cfg);
|
||||||
@@ -8431,10 +8541,15 @@ static int reload_queue_rules(int reload)
|
|||||||
ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
|
ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AST_LIST_UNLOCK(&rule_lists);
|
|
||||||
|
|
||||||
ast_config_destroy(cfg);
|
ast_config_destroy(cfg);
|
||||||
|
|
||||||
|
if (realtime_rules && load_realtime_rules()) {
|
||||||
|
AST_LIST_UNLOCK(&rule_lists);
|
||||||
|
return AST_MODULE_LOAD_DECLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
AST_LIST_UNLOCK(&rule_lists);
|
||||||
return AST_MODULE_LOAD_SUCCESS;
|
return AST_MODULE_LOAD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
; database driver, database and table (or uses the
|
; database driver, database and table (or uses the
|
||||||
; name of the file as the table if not specified)
|
; name of the file as the table if not specified)
|
||||||
;
|
;
|
||||||
;uncomment to load queues.conf via the odbc engine.
|
; Uncomment to load queues.conf via the odbc engine.
|
||||||
;
|
;
|
||||||
;queues.conf => odbc,asterisk,ast_config
|
;queues.conf => odbc,asterisk,ast_config
|
||||||
;extensions.conf => sqlite,asterisk,ast_config
|
;extensions.conf => sqlite,asterisk,ast_config
|
||||||
@@ -89,6 +89,7 @@
|
|||||||
;meetme => mysql,general
|
;meetme => mysql,general
|
||||||
;queues => odbc,asterisk
|
;queues => odbc,asterisk
|
||||||
;queue_members => odbc,asterisk
|
;queue_members => odbc,asterisk
|
||||||
|
;queue_rules => odbc,asterisk
|
||||||
;acls => odbc,asterisk
|
;acls => odbc,asterisk
|
||||||
;musiconhold => mysql,general
|
;musiconhold => mysql,general
|
||||||
;queue_log => mysql,general
|
;queue_log => mysql,general
|
||||||
|
@@ -1,3 +1,14 @@
|
|||||||
|
|
||||||
|
[general]
|
||||||
|
|
||||||
|
; Look for queue rules in the queue_rules database table through RealTime. Note
|
||||||
|
; that this option is not strictly "RealTime", in the sense that the queue
|
||||||
|
; rules are only loaded and parsed during module load/reload. Queue rules
|
||||||
|
; must have a unique rule name and support relative min/max penalties.
|
||||||
|
;
|
||||||
|
; realtime_rules = yes
|
||||||
|
;
|
||||||
|
|
||||||
; It is possible to change the value of the QUEUE_MAX_PENALTY and QUEUE_MIN_PENALTY
|
; It is possible to change the value of the QUEUE_MAX_PENALTY and QUEUE_MIN_PENALTY
|
||||||
; channel variables in mid-call by defining rules in the queue for when to do so. This can allow for
|
; channel variables in mid-call by defining rules in the queue for when to do so. This can allow for
|
||||||
; a call to be opened to more members or potentially a different set of members.
|
; a call to be opened to more members or potentially a different set of members.
|
||||||
|
@@ -0,0 +1,31 @@
|
|||||||
|
"""Create queue_rules
|
||||||
|
|
||||||
|
Revision ID: d39508cb8d8
|
||||||
|
Revises: 5139253c0423
|
||||||
|
Create Date: 2014-08-10 17:27:32.973571
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'd39508cb8d8'
|
||||||
|
down_revision = '5139253c0423'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.create_table(
|
||||||
|
'queue_rules',
|
||||||
|
sa.Column('rule_name', sa.String(80), nullable=False),
|
||||||
|
sa.Column('time', sa.String(32), nullable=False),
|
||||||
|
sa.Column('min_penalty', sa.String(32), nullable=False),
|
||||||
|
sa.Column('max_penalty', sa.String(32), nullable=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
########################## drop tables ###########################
|
||||||
|
|
||||||
|
op.drop_table('queue_rules')
|
||||||
|
|
Reference in New Issue
Block a user