Author: mjordan Date: Sun Aug 10 19:07:22 2014 New Revision: 420624 URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=420624 Log: 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) Added: branches/13/contrib/ast-db-manage/config/versions/d39508cb8d8_create_queue_rules.py (with props) Modified: branches/13/CHANGES branches/13/UPGRADE.txt branches/13/apps/app_queue.c branches/13/configs/samples/extconfig.conf.sample branches/13/configs/samples/queuerules.conf.sample Modified: branches/13/CHANGES URL: http://svnview.digium.com/svn/asterisk/branches/13/CHANGES?view=diff&rev=420624&r1=420623&r2=420624 ============================================================================== --- branches/13/CHANGES (original) +++ branches/13/CHANGES Sun Aug 10 19:07:22 2014 @@ -87,6 +87,19 @@ have been added. The option "p" will play a beep to the channel that starts the recording. The option "P" will play a beep to the channel that stops the 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 ------------------ Modified: branches/13/UPGRADE.txt URL: http://svnview.digium.com/svn/asterisk/branches/13/UPGRADE.txt?view=diff&rev=420624&r1=420623&r2=420624 ============================================================================== --- branches/13/UPGRADE.txt (original) +++ branches/13/UPGRADE.txt Sun Aug 10 19:07:22 2014 @@ -66,6 +66,9 @@ 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. +Queue: + - Queue rules provided in queuerules.conf can no longer be named "general". + SetMusicOnHold: - The SetMusicOnHold dialplan application was deprecated and has been removed. Users of the application should use the CHANNEL function's musicclass @@ -342,6 +345,11 @@ - 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 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: res_odbc: Modified: branches/13/apps/app_queue.c URL: http://svnview.digium.com/svn/asterisk/branches/13/apps/app_queue.c?view=diff&rev=420624&r1=420623&r2=420624 ============================================================================== --- branches/13/apps/app_queue.c (original) +++ branches/13/apps/app_queue.c Sun Aug 10 19:07:22 2014 @@ -1362,6 +1362,9 @@ /*! \brief queues.conf [general] option */ static int shared_lastcall = 1; +/*! \brief queuesrules.conf [general] option */ +static int realtime_rules = 0; + /*! \brief Subscription to device state change messages */ static struct stasis_subscription *device_state_sub; @@ -2730,6 +2733,99 @@ ast_free(rule); return -1; } + 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; } @@ -8384,6 +8480,16 @@ .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 * * \param reload If 1, then only process queuerules.conf if the file @@ -8397,7 +8503,7 @@ struct penalty_rule *pr_iter; char *rulecat = 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))) { ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n"); @@ -8417,6 +8523,10 @@ ast_free(rl_iter); } 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)))) { AST_LIST_UNLOCK(&rule_lists); ast_config_destroy(cfg); @@ -8431,10 +8541,15 @@ ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno); } } + + 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); - - ast_config_destroy(cfg); - return AST_MODULE_LOAD_SUCCESS; } Modified: branches/13/configs/samples/extconfig.conf.sample URL: http://svnview.digium.com/svn/asterisk/branches/13/configs/samples/extconfig.conf.sample?view=diff&rev=420624&r1=420623&r2=420624 ============================================================================== --- branches/13/configs/samples/extconfig.conf.sample (original) +++ branches/13/configs/samples/extconfig.conf.sample Sun Aug 10 19:07:22 2014 @@ -15,7 +15,7 @@ ; database driver, database and table (or uses the ; 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 ;extensions.conf => sqlite,asterisk,ast_config @@ -89,6 +89,7 @@ ;meetme => mysql,general ;queues => odbc,asterisk ;queue_members => odbc,asterisk +;queue_rules => odbc,asterisk ;acls => odbc,asterisk ;musiconhold => mysql,general ;queue_log => mysql,general Modified: branches/13/configs/samples/queuerules.conf.sample URL: http://svnview.digium.com/svn/asterisk/branches/13/configs/samples/queuerules.conf.sample?view=diff&rev=420624&r1=420623&r2=420624 ============================================================================== --- branches/13/configs/samples/queuerules.conf.sample (original) +++ branches/13/configs/samples/queuerules.conf.sample Sun Aug 10 19:07:22 2014 @@ -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 ; 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. Added: branches/13/contrib/ast-db-manage/config/versions/d39508cb8d8_create_queue_rules.py URL: http://svnview.digium.com/svn/asterisk/branches/13/contrib/ast-db-manage/config/versions/d39508cb8d8_create_queue_rules.py?view=auto&rev=420624 ============================================================================== --- branches/13/contrib/ast-db-manage/config/versions/d39508cb8d8_create_queue_rules.py (added) +++ branches/13/contrib/ast-db-manage/config/versions/d39508cb8d8_create_queue_rules.py Sun Aug 10 19:07:22 2014 @@ -1,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') + Propchange: branches/13/contrib/ast-db-manage/config/versions/d39508cb8d8_create_queue_rules.py ------------------------------------------------------------------------------ svn:eol-style = native Propchange: branches/13/contrib/ast-db-manage/config/versions/d39508cb8d8_create_queue_rules.py ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Propchange: branches/13/contrib/ast-db-manage/config/versions/d39508cb8d8_create_queue_rules.py ------------------------------------------------------------------------------ svn:mime-type = text/plain -- _____________________________________________________________________ -- Bandwidth and Colocation Provided by http://www.api-digital.com -- svn-commits mailing list To UNSUBSCRIBE or update options visit: http://lists.digium.com/mailman/listinfo/svn-commits