[asterisk-commits] kmoore: testsuite/asterisk/trunk r5609 - in /asterisk/trunk: lib/python/aster...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Sep 19 07:50:03 CDT 2014


Author: kmoore
Date: Fri Sep 19 07:49:59 2014
New Revision: 5609

URL: http://svnview.digium.com/svn/testsuite?view=rev&rev=5609
Log:
Testsuite: Add modular event action framework

This adds a modular event/action system that can handle routing from
arbitrary events to arbitrary actions allowing creation of more
flexible tests. This includes event modules for AMI and ARI actions as
well as action modules for AMI, ARI, callbacks, and more.

This also converts three tests as a proof of concept.

Review: https://reviewboard.asterisk.org/r/3924/

Added:
    asterisk/trunk/lib/python/asterisk/pluggable_registry.py   (with props)
Removed:
    asterisk/trunk/tests/asyncagi/asyncagi_break/asyncagi_break.py
Modified:
    asterisk/trunk/lib/python/asterisk/ami.py
    asterisk/trunk/lib/python/asterisk/ari.py
    asterisk/trunk/lib/python/asterisk/pluggable_modules.py
    asterisk/trunk/tests/asyncagi/asyncagi_break/test-config.yaml
    asterisk/trunk/tests/rest_api/bridges/attended_transfer/attended_transfer.py
    asterisk/trunk/tests/rest_api/bridges/attended_transfer/test-config.yaml
    asterisk/trunk/tests/rest_api/bridges/bridge_by_id/test-config.yaml

Modified: asterisk/trunk/lib/python/asterisk/ami.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/ami.py?view=diff&rev=5609&r1=5608&r2=5609
==============================================================================
--- asterisk/trunk/lib/python/asterisk/ami.py (original)
+++ asterisk/trunk/lib/python/asterisk/ami.py Fri Sep 19 07:49:59 2014
@@ -13,6 +13,8 @@
 import logging
 import re
 import json
+from pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\
+                               PLUGGABLE_ACTION_REGISTRY, var_replace
 
 LOGGER = logging.getLogger(__name__)
 
@@ -647,3 +649,76 @@
         reactor.callLater(delay, self.login)
         return reason
 
+class AMIStartEventModule(object):
+    """An event module that triggers when the test starts."""
+
+    def __init__(self, test_object, triggered_callback, config):
+        """Setup the test start observer"""
+        self.test_object = test_object
+        self.triggered_callback = triggered_callback
+        self.config = config
+        test_object.register_ami_observer(self.start_observer)
+
+    def start_observer(self, ami):
+        """Notify the event-action mapper that ami has started."""
+        self.triggered_callback(self, ami)
+PLUGGABLE_EVENT_REGISTRY.register("ami-start", AMIStartEventModule)
+
+class AMIPluggableEventInstance(AMIHeaderMatchInstance):
+    """Subclass of AMIEventInstance that works with the pluggable event action
+    module.
+    """
+
+    def __init__(self, test_object, triggered_callback, config, data):
+        """Setup the AMI event observer"""
+        self.triggered_callback = triggered_callback
+	self.data = data
+        super(AMIPluggableEventInstance, self).__init__(config, test_object)
+
+    def event_callback(self, ami, event):
+        """Callback called when an event is received from AMI"""
+	super(AMIPluggableEventInstance, self).event_callback(ami, event)
+        if self.passed:
+            self.triggered_callback(self.data, ami, event)
+
+class AMIPluggableEventModule(object):
+    """Generates AMIEventInstance instances that match events for the pluggable
+    event-action framework.
+    """
+    def __init__(self, test_object, triggered_callback, config):
+        """Setup the AMI event observers"""
+        self.instances = []
+        if not isinstance(config, list):
+            config = [config]
+        for instance in config:
+            self.instances.append(AMIPluggableEventInstance(test_object,
+                                                            triggered_callback,
+                                                            instance,
+                                                            self))
+PLUGGABLE_EVENT_REGISTRY.register("ami-events", AMIPluggableEventModule)
+
+def replace_ami_vars(mydict, values):
+    outdict = {}
+    for key, value in mydict.iteritems():
+        outdict[key] = var_replace(value, values)
+
+    return outdict
+
+class AMIPluggableActionModule(object):
+    """Pluggable AMI action module.
+    """
+
+    def __init__(self, test_object, config):
+        """Setup the AMI event observer"""
+        self.test_object = test_object
+        if not isinstance(config, list):
+            config = [config]
+        self.config = config
+
+    def run(self, triggered_by, source, extra):
+        """Callback called when this action is triggered."""
+        for instance in self.config:
+            action = replace_ami_vars(instance["action"], extra)
+            ami_id = instance.get("id", 0)
+            self.test_object.ami[ami_id].sendMessage(action)
+PLUGGABLE_ACTION_REGISTRY.register("ami-actions", AMIPluggableActionModule)

Modified: asterisk/trunk/lib/python/asterisk/ari.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/ari.py?view=diff&rev=5609&r1=5608&r2=5609
==============================================================================
--- asterisk/trunk/lib/python/asterisk/ari.py (original)
+++ asterisk/trunk/lib/python/asterisk/ari.py Fri Sep 19 07:49:59 2014
@@ -15,6 +15,8 @@
 import urllib
 
 from test_case import TestCase
+from pluggable_registry import PLUGGABLE_EVENT_REGISTRY,\
+                               PLUGGABLE_ACTION_REGISTRY, var_replace
 from test_suite_utils import all_match
 from twisted.internet import reactor
 try:
@@ -509,30 +511,9 @@
             self.body = json.dumps(self.body)
             self.headers = {'Content-type': 'application/json'}
 
-    def var_replace(self, text, values):
-        """ perform variable replacement on text
-
-        This allows a string such as uri to be written in the form:
-        playbacks/{playback.id}/control
-
-        :param text: text with optional {var} entries
-        :param values: nested dict of values to get replacement values from
-        """
-        for match in re.findall(r'{[^}]*}', text):
-            value = values
-            for var in match[1:-1].split('.'):
-                if not var in value:
-                    LOGGER.error('Unable to replace variables in %s from %s',
-                                 text, values)
-                    return None
-                value = value[var]
-            text = text.replace(match, value)
-
-        return text
-
     def send(self, values):
         """Send this ARI request substituting the given values"""
-        uri = self.var_replace(self.uri, values)
+        uri = var_replace(self.uri, values)
         url = self.ari.build_url(uri)
         requests_method = getattr(requests, self.method)
 
@@ -693,4 +674,82 @@
         # Need exactly this many events
         return Range(int(yaml), int(yaml))
 
+class ARIPluggableEventModule(object):
+    """Subclass of ARIEventInstance that works with the pluggable event action
+    module.
+    """
+
+    def __init__(self, test_object, triggered_callback, config):
+        """Setup the ARI event observer"""
+        self.test_object = test_object
+        self.triggered_callback = triggered_callback
+        if isinstance(config, list):
+            self.config = config
+        else:
+            self.config = [config]
+        for event_description in self.config:
+            event_description["expected_count_range"] =\
+                decode_range(event_description.get('count', 1))
+            event_description["event_count"] = 0
+        test_object.register_ws_event_handler(self.event_callback)
+        test_object.register_stop_observer(self.on_stop)
+
+    def event_callback(self, event):
+        """Callback called when an event is received from ARI"""
+        for event_description in self.config:
+            match = event_description["match"]
+            nomatch = event_description.get("nomatch", None)
+            if not all_match(match, event):
+                continue
+
+            # Now validate the nomatch, if it's there
+            if nomatch and all_match(nomatch, event):
+                continue
+            event_description["event_count"] += 1
+            self.triggered_callback(self, self.test_object.ari, event)
+
+    def on_stop(self, *args):
+        """Callback for the end of the test.
+
+        :param args: Ignored arguments.
+        """
+        for event_desc in self.config:
+            if not event_desc["expected_count_range"]\
+                .contains(event_desc["event_count"]):
+                # max could be int or float('inf'); format with %r
+                LOGGER.error("Expected %d <= count <= %r; was %d (%r, !%r)",
+                             event_desc["expected_count_range"].min_value,
+                             event_desc["expected_count_range"].max_value,
+                             event_desc["event_count"],
+                             event_desc["match"],
+                             event_desc.get("nomatch", None))
+                self.test_object.set_passed(False)
+            self.test_object.set_passed(True)
+PLUGGABLE_EVENT_REGISTRY.register("ari-events", ARIPluggableEventModule)
+
+class ARIPluggableRequestModule(object):
+    """Pluggable ARI action module.
+    """
+
+    def __init__(self, test_object, config):
+        """Setup the ARI event observer"""
+        self.test_object = test_object
+        if not isinstance(config, list):
+            config = [config]
+        self.requests = [ARIRequest(test_object.ari, request_config)
+                         for request_config in config]
+        self.count = 0
+
+    def run(self, triggered_by, source, extra):
+        """Callback called when this action is triggered."""
+        self.count += 1
+        for request in self.requests:
+            if request.instance and request.instance != self.count:
+                continue
+            if request.delay:
+                reactor.callLater(request.delay, request.send, extra)
+            else:
+                request.send(extra)
+PLUGGABLE_ACTION_REGISTRY.register("ari-requests", ARIPluggableRequestModule)
+
 # vim:sw=4:ts=4:expandtab:textwidth=79

Modified: asterisk/trunk/lib/python/asterisk/pluggable_modules.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/pluggable_modules.py?view=diff&rev=5609&r1=5608&r2=5609
==============================================================================
--- asterisk/trunk/lib/python/asterisk/pluggable_modules.py (original)
+++ asterisk/trunk/lib/python/asterisk/pluggable_modules.py Fri Sep 19 07:49:59 2014
@@ -17,6 +17,10 @@
 from ami import AMIEventInstance
 from twisted.internet import reactor
 from starpy import fastagi
+from test_runner import load_and_parse_module
+from pluggable_registry import PLUGGABLE_ACTION_REGISTRY,\
+                               PLUGGABLE_EVENT_REGISTRY,\
+                               PluggableRegistry
 
 LOGGER = logging.getLogger(__name__)
 
@@ -641,5 +645,172 @@
             return
 
         agi.sendCommand(self.commands[idx])\
-	.addCallback(self.on_command_success, agi, idx)\
-	.addErrback(self.on_command_failure, agi, idx)
+        .addCallback(self.on_command_success, agi, idx)\
+        .addErrback(self.on_command_failure, agi, idx)
+
+class EventActionModule(object):
+    """A class that links arbitrary events with one or more actions.
+
+    Configuration is as follows:
+    config-section:
+        actions:
+            custom-action-name: custom.action.location
+        events:
+            custom-event-name: custom.event.location
+        mapping:
+            -
+                custom-event-name:
+                    event-config-goes-here
+                custom-action-name:
+                    action-config-goes-here
+
+    Or if no locally-defined events or actions are desired:
+    config-section:
+        -
+            event-name:
+                event-config-goes-here
+            other-event-name:
+                event-config-goes-here
+            action-name:
+                action-config-goes-here
+
+    Or if no locally-defined events or actions are desired and only one set is
+    desired:
+    config-section:
+        event-name:
+            event-config-goes-here
+        action-name:
+            action-config-goes-here
+
+    Any event in a set will trigger all actions in a set.
+    """
+
+    def __init__(self, instance_config, test_object):
+        """Constructor for pluggable modules"""
+        super(EventActionModule, self).__init__()
+        self.test_object = test_object
+        config = instance_config
+        if isinstance(config, list):
+            config = {"mapping": config}
+        elif isinstance(config, dict) and "mapping" not in config:
+            config = {"mapping": [config]}
+
+        # Parse out local action and event definitions
+        self.local_action_registry = PluggableRegistry()
+        self.local_event_registry = PluggableRegistry()
+
+        def register_modules(config, registry):
+            """Register pluggable modules into the registry"""
+            for key, local_class_path in config.iteritems():
+                local_class = load_and_parse_module(local_class_path)
+                if not local_class:
+                    raise Exception("Unable to load %s for module key %s"
+                                    % (local_class_path, key))
+                registry.register(key, local_class)
+
+        if "actions" in config:
+            register_modules(config["actions"], self.local_action_registry)
+        if "events" in config:
+            register_modules(config["events"], self.local_event_registry)
+
+        self.event_action_sets = []
+        self.parse_mapping(config)
+
+    def parse_mapping(self, config):
+        """Parse out the mapping and instantiate objects."""
+        for e_a_set in config["mapping"]:
+            plug_set = {"events": [], "actions": []}
+
+            for plug_name, plug_config in e_a_set.iteritems():
+                self.parse_module_config(plug_set, plug_name, plug_config)
+
+            if 0 == len(plug_set["events"]):
+                raise Exception("Pluggable set requires at least one event: %s"
+                                % e_a_set)
+
+            self.event_action_sets.append(plug_set)
+
+    def parse_module_config(self, plug_set, plug_name, plug_config):
+        """Parse module config and update the pluggable module set"""
+        if self.local_event_registry.check(plug_name):
+            plug_class = self.local_event_registry.get_class(plug_name)
+            plug_set["events"].append(
+                plug_class(self.test_object, self.event_triggered, plug_config))
+        elif self.local_action_registry.check(plug_name):
+            plug_class = self.local_action_registry.get_class(plug_name)
+            plug_set["actions"].append(
+                plug_class(self.test_object, plug_config))
+        elif PLUGGABLE_EVENT_REGISTRY.check(plug_name):
+            plug_class = PLUGGABLE_EVENT_REGISTRY.get_class(plug_name)
+            plug_set["events"].append(
+                plug_class(self.test_object, self.event_triggered, plug_config))
+        elif PLUGGABLE_ACTION_REGISTRY.check(plug_name):
+            plug_class = PLUGGABLE_ACTION_REGISTRY.get_class(plug_name)
+            plug_set["actions"].append(
+                plug_class(self.test_object, plug_config))
+        else:
+            raise Exception("Pluggable component '%s' not recognized"
+                            % plug_name)
+
+    def find_triggered_set(self, triggered_by):
+        """Find the set that was triggered."""
+        for e_a_set in self.event_action_sets:
+            for event_mod in e_a_set["events"]:
+                if event_mod == triggered_by:
+                    return e_a_set
+        return None
+
+    def event_triggered(self, triggered_by, source=None, extra=None):
+        """Run actions for the triggered set."""
+        triggered_set = self.find_triggered_set(triggered_by)
+        if not triggered_set:
+            raise Exception("Unable to find event/action set for %s"
+                            % triggered_by)
+
+        for action_mod in triggered_set["actions"]:
+            action_mod.run(triggered_by, source, extra)
+
+class TestStartEventModule(object):
+    """An event module that triggers when the test starts."""
+
+    def __init__(self, test_object, triggered_callback, config):
+        """Setup the test start observer"""
+        self.test_object = test_object
+        self.triggered_callback = triggered_callback
+        self.config = config
+        test_object.register_start_observer(self.start_observer)
+
+    def start_observer(self, ast):
+        """Notify the event-action mapper that the test has started."""
+        self.triggered_callback(self, ast)
+PLUGGABLE_EVENT_REGISTRY.register("test-start", TestStartEventModule)
+
+class LogActionModule(object):
+    """An action module that logs a message when triggered."""
+
+    def __init__(self, test_object, config):
+        """Setup the test start observer"""
+        self.test_object = test_object
+        self.message = config["message"]
+
+    def run(self, triggered_by, source, extra):
+        """Log a message."""
+        LOGGER.info(self.message)
+PLUGGABLE_ACTION_REGISTRY.register("logger", LogActionModule)
+
+class CallbackActionModule(object):
+    """An action module that calls the specified callback."""
+
+    def __init__(self, test_object, config):
+        """Setup the test start observer"""
+        self.test_object = test_object
+        self.module = config["module"]
+        self.method = config["method"]
+
+    def run(self, triggered_by, source, extra):
+        """Call the callback."""
+        module = __import__(self.module)
+        method = getattr(module, self.method)
+        self.test_object.set_passed(method(self.test_object, triggered_by,
+                                           source, extra))
+PLUGGABLE_ACTION_REGISTRY.register("callback", CallbackActionModule)

Added: asterisk/trunk/lib/python/asterisk/pluggable_registry.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/pluggable_registry.py?view=auto&rev=5609
==============================================================================
--- asterisk/trunk/lib/python/asterisk/pluggable_registry.py (added)
+++ asterisk/trunk/lib/python/asterisk/pluggable_registry.py Fri Sep 19 07:49:59 2014
@@ -1,0 +1,64 @@
+#!/usr/bin/env python
+"""Pluggable module registries
+
+Copyright (C) 2014, Digium, Inc.
+Kinsey Moore <kmoore at digium.com>
+
+This program is free software, distributed under the terms of
+the GNU General Public License Version 2.
+"""
+import logging
+import re
+
+LOGGER = logging.getLogger(__name__)
+
+class PluggableRegistry(object):
+    """Registry for pluggable modules"""
+
+    def __init__(self):
+        self.registry = {}
+
+    def register(self, key, factory):
+        """Register a module"""
+        self.registry[key] = factory
+
+    def check(self, key):
+        """Check whether a module factory exists for the given key"""
+        if key in self.registry:
+            return True
+        return False
+
+    def get_class(self, key):
+        """Get the class for a module"""
+        return self.registry[key]
+
+PLUGGABLE_EVENT_REGISTRY = PluggableRegistry()
+PLUGGABLE_ACTION_REGISTRY = PluggableRegistry()
+
+def var_replace(text, values):
+    """ perform variable replacement on text
+
+    This allows a parameters to be written to include variables from the
+    arbitrarily structured object provided by an ARI or AMI event like so:
+    from ARI to ARI: Uri: 'playbacks/{playback.id}/control'
+    from AMI to AMI: Channel: '{channel}'
+    from AMI to ARI: Uri: 'channels/{uniqueid}/play'
+    from ARI to AMI: Channel: '{channel.name}'
+
+    :param text: text with optional {var} entries
+    :param values: nested dict of values to get replacement values from
+    """
+    if not isinstance(text, str):
+        return text
+
+    for match in re.findall(r'{[^}]*}', text):
+        value = values
+        for var in match[1:-1].split('.'):
+            if not var in value:
+                LOGGER.error('Unable to replace variables in %s from %s',
+                             text, values)
+                return None
+            value = value[var]
+        text = text.replace(match, value)
+
+    return text

Propchange: asterisk/trunk/lib/python/asterisk/pluggable_registry.py
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: asterisk/trunk/lib/python/asterisk/pluggable_registry.py
------------------------------------------------------------------------------
    svn:executable = *

Propchange: asterisk/trunk/lib/python/asterisk/pluggable_registry.py
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: asterisk/trunk/lib/python/asterisk/pluggable_registry.py
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: asterisk/trunk/tests/asyncagi/asyncagi_break/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/asyncagi/asyncagi_break/test-config.yaml?view=diff&rev=5609&r1=5608&r2=5609
==============================================================================
--- asterisk/trunk/tests/asyncagi/asyncagi_break/test-config.yaml (original)
+++ asterisk/trunk/tests/asyncagi/asyncagi_break/test-config.yaml Fri Sep 19 07:49:59 2014
@@ -11,13 +11,13 @@
         typename: 'test_case.SimpleTestCase'
     modules:
         -
-            config-section: ami-config-11
+            config-section: pluggable-config-11
             maxversion: '12.0.0'
-            typename: 'ami.AMIEventModule'
+            typename: 'pluggable_modules.EventActionModule'
         -
-            config-section: ami-config-12
+            config-section: pluggable-config-12
             minversion: '12.0.0'
-            typename: 'ami.AMIEventModule'
+            typename: 'pluggable_modules.EventActionModule'
 
 test-object-config:
     spawn-after-hangup: True
@@ -26,111 +26,123 @@
             channel: 'Local/s at default'
             application: 'Echo'
 
-ami-config-11:
+pluggable-config-11:
     -
-        type: 'headermatch'
-        conditions:
-            match:
-                Event: 'UserEvent'
-        requirements:
-            match:
-                Result: 'pass'
-        count: '1'
+        ami-events:
+            conditions:
+                match:
+                    Event: 'UserEvent'
+            requirements:
+                match:
+                    Result: 'pass'
+            count: '1'
     -
-        type: 'callback'
-        conditions:
-            match:
-                Event: 'AsyncAGI'
-                Channel: 'Local/s at default-.*'
-                SubEvent: 'Start'
-        callbackModule: 'asyncagi_break'
-        callbackMethod: 'async_start'
+        ami-events:
+            conditions:
+                match:
+                    Event: 'AsyncAGI'
+                    Channel: 'Local/s at default-.*'
+                    SubEvent: 'Start'
+        ami-actions:
+            action:
+                action: 'AGI'
+                channel: '{channel}'
+                command: 'STREAM FILE tt-monkeys ""'
     -
-        type: 'callback'
-        conditions:
-            match:
-                Event: 'AGIExec'
-                SubEvent: 'Start'
-                Channel: 'Local/s at default-.*'
-                Command: 'STREAM FILE tt-monkeys ""'
-        callbackModule: 'asyncagi_break'
-        callbackMethod: 'async_break'
+        ami-events:
+            conditions:
+                match:
+                    Event: 'AGIExec'
+                    SubEvent: 'Start'
+                    Channel: 'Local/s at default-.*'
+                    Command: 'STREAM FILE tt-monkeys ""'
+        ami-actions:
+            action:
+                action: 'AGI'
+                channel: '{channel}'
+                command: 'ASYNCAGI BREAK'
     -
-        type: 'headermatch'
-        conditions:
-            match:
-                Event: 'AGIExec'
-                SubEvent: 'End'
-                Channel: 'Local/s at default-.*'
-                Command: 'STREAM FILE tt-monkeys ""'
-        requirements:
-            match:
-                ResultCode: '200'
-                Result: 'Success'
-        count: '1'
+        ami-events:
+            conditions:
+                match:
+                    Event: 'AGIExec'
+                    SubEvent: 'End'
+                    Channel: 'Local/s at default-.*'
+                    Command: 'STREAM FILE tt-monkeys ""'
+            requirements:
+                match:
+                    ResultCode: '200'
+                    Result: 'Success'
+            count: '1'
     -
-        type: 'headermatch'
-        conditions:
-            match:
-                Event: 'AGIExec'
-                SubEvent: 'End'
-                Channel: 'Local/s at default-.*'
-                Command: 'ASYNCAGI BREAK'
-        requirements:
-            match:
-                ResultCode: '200'
-                Result: 'Success'
+        ami-events:
+            conditions:
+                match:
+                    Event: 'AGIExec'
+                    SubEvent: 'End'
+                    Channel: 'Local/s at default-.*'
+                    Command: 'ASYNCAGI BREAK'
+            requirements:
+                match:
+                    ResultCode: '200'
+                    Result: 'Success'
 
-ami-config-12:
+pluggable-config-12:
     -
-        type: 'headermatch'
-        conditions:
-            match:
-                Event: 'UserEvent'
-        requirements:
-            match:
-                Result: 'pass'
-        count: '1'
+        ami-events:
+            conditions:
+                match:
+                    Event: 'UserEvent'
+            requirements:
+                match:
+                    Result: 'pass'
+            count: '1'
     -
-        type: 'callback'
-        conditions:
-            match:
-                Event: 'AsyncAGIStart'
-                Channel: 'Local/s at default-.*'
-        callbackModule: 'asyncagi_break'
-        callbackMethod: 'async_start'
+        ami-events:
+            conditions:
+                match:
+                    Event: 'AsyncAGIStart'
+                    Channel: 'Local/s at default-.*'
+        ami-actions:
+            action:
+                action: 'AGI'
+                channel: '{channel}'
+                command: 'STREAM FILE tt-monkeys ""'
     -
-        type: 'callback'
-        conditions:
-            match:
-                Event: 'AGIExecStart'
-                Channel: 'Local/s at default-.*'
-                Command: 'STREAM FILE tt-monkeys ""'
-        callbackModule: 'asyncagi_break'
-        callbackMethod: 'async_break'
+        ami-events:
+            conditions:
+                match:
+                    Event: 'AGIExecStart'
+                    Channel: 'Local/s at default-.*'
+                    Command: 'STREAM FILE tt-monkeys ""'
+        ami-actions:
+            action:
+                action: 'AGI'
+                channel: '{channel}'
+                command: 'ASYNCAGI BREAK'
     -
-        type: 'headermatch'
-        conditions:
-            match:
-                Event: 'AGIExecEnd'
-                Channel: 'Local/s at default-.*'
-                Command: 'STREAM FILE tt-monkeys ""'
-        requirements:
-            match:
-                ResultCode: '200'
-                Result: 'Success'
-        count: '1'
+        ami-events:
+            conditions:
+                match:
+                    Event: 'AGIExecEnd'
+                    Channel: 'Local/s at default-.*'
+                    Command: 'STREAM FILE tt-monkeys ""'
+            requirements:
+                match:
+                    ResultCode: '200'
+                    Result: 'Success'
+            count: '1'
     -
-        type: 'headermatch'
-        conditions:
-            match:
-                Event: 'AGIExecEnd'
-                Channel: 'Local/s at default-.*'
-                Command: 'ASYNCAGI BREAK'
-        requirements:
-            match:
-                ResultCode: '200'
-                Result: 'Success'
+        ami-events:
+            conditions:
+                match:
+                    Event: 'AGIExecEnd'
+                    Channel: 'Local/s at default-.*'
+                    Command: 'ASYNCAGI BREAK'
+            requirements:
+                match:
+                    ResultCode: '200'
+                    Result: 'Success'
 
 properties:
     minversion: '1.8.0.0'

Modified: asterisk/trunk/tests/rest_api/bridges/attended_transfer/attended_transfer.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/rest_api/bridges/attended_transfer/attended_transfer.py?view=diff&rev=5609&r1=5608&r2=5609
==============================================================================
--- asterisk/trunk/tests/rest_api/bridges/attended_transfer/attended_transfer.py (original)
+++ asterisk/trunk/tests/rest_api/bridges/attended_transfer/attended_transfer.py Fri Sep 19 07:49:59 2014
@@ -22,7 +22,7 @@
 
 TEST = TestLogic()
 
-def on_kickoff_start(ari, event, test_object):
+def on_kickoff_start(test_object, triggered_by, ari, event):
     LOGGER.debug("on_kickoff_start(%r)" % event)
 
     def _start_referer_scenario(referer_scenario, test_object):
@@ -48,25 +48,25 @@
     ari.post('bridges', TEST.bridge_id, 'addChannel', channel=event['channel']['id'])
     return True
 
-def on_test_start(ari, event, test_object):
+def on_test_start(test_object, triggered_by, ari, event):
     LOGGER.debug("on_test_start(%r)" % event)
 
     ari.post('bridges', TEST.bridge_id, 'addChannel', channel=event['channel']['id'])
     return True
 
-def on_swap_start(ari, event, test_object):
+def on_swap_start(test_object, triggered_by, ari, event):
     LOGGER.debug("on_swap_start(%r)" % event)
 
     TEST.swap_id = event['channel']['id']
     return True
 
-def on_swap_enter(ari, event, test_object):
+def on_swap_enter(test_object, triggered_by, ari, event):
     LOGGER.debug("on_swap_enter(%r)" % event)
 
     ari.delete('channels', TEST.swap_id)
     return True
 
-def on_attended_transfer(ari, event, test_object):
+def on_attended_transfer(test_object, triggered_by, ari, event):
     LOGGER.debug("on_attended_transfer(%r)" % event)
 
     ari.delete('bridges', TEST.bridge_id)

Modified: asterisk/trunk/tests/rest_api/bridges/attended_transfer/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/rest_api/bridges/attended_transfer/test-config.yaml?view=diff&rev=5609&r1=5608&r2=5609
==============================================================================
--- asterisk/trunk/tests/rest_api/bridges/attended_transfer/test-config.yaml (original)
+++ asterisk/trunk/tests/rest_api/bridges/attended_transfer/test-config.yaml Fri Sep 19 07:49:59 2014
@@ -35,63 +35,66 @@
         typename: ari.AriTestObject
     modules:
         -
-            config-section: ari-config
-            typename: ari.WebSocketEventModule
+            config-section: pluggable-config
+            typename: 'pluggable_modules.EventActionModule'
 
-ari-config:
-    apps: testsuite
-    events:
-        -   conditions:
-                match:
-                    type: StasisStart
-                    application: testsuite
-                    args: []
-                    channel:
-                        name: 'Local/s at default-.*'
+pluggable-config:
+    -
+        ari-events:
+            match:
+                type: StasisStart
+                application: testsuite
+                args: []
+                channel:
+                    name: 'Local/s at default-.*'
             count: 1
-            callback:
-                module: attended_transfer
-                method: on_kickoff_start
-        -   conditions:
-                match:
-                    type: StasisStart
-                    application: testsuite
-                    args: ['test']
+        callback:
+            module: attended_transfer
+            method: on_kickoff_start
+    -
+        ari-events:
+            match:
+                type: StasisStart
+                application: testsuite
+                args: ['test']
             count: 1
-            callback:
-                module: attended_transfer
-                method: on_test_start
-        -   conditions:
-                match:
-                    type: StasisStart
-                    application: testsuite
-                    args: []
-                    channel:
-                        name: 'Local/_attended at transfer-.*'
-                    replace_channel:
-                        name: 'PJSIP/bob-.*'
+        callback:
+            module: attended_transfer
+            method: on_test_start
+    -
+        ari-events:
+            match:
+                type: StasisStart
+                application: testsuite
+                args: []
+                channel:
+                    name: 'Local/_attended at transfer-.*'
+                replace_channel:
+                    name: 'PJSIP/bob-.*'
             count: 1
-            callback:
-                module: attended_transfer
-                method: on_swap_start
-        -   conditions:
-                match:
-                    type: BridgeAttendedTransfer
-                    application: testsuite
+        callback:
+            module: attended_transfer
+            method: on_swap_start
+    -
+        ari-events:
+            match:
+                type: BridgeAttendedTransfer
+                application: testsuite
             count: 1
-            callback:
-                module: attended_transfer
-                method: on_attended_transfer
-        -   conditions:
-                match:
-                    type: ChannelEnteredBridge
-                    application: testsuite
-                    channel:
-                        name: 'Local/_attended at transfer-.*'
+        callback:
+            module: attended_transfer
+            method: on_attended_transfer
+    -
+        ari-events:
+            match:
+                type: ChannelEnteredBridge
+                application: testsuite
+                channel:
+                    name: 'Local/_attended at transfer-.*'
             count: 1
-            callback:
-                module: attended_transfer
-                method: on_swap_enter
+        callback:
+            module: attended_transfer
+            method: on_swap_enter
 
 properties:
     minversion: '12.1.0'

Modified: asterisk/trunk/tests/rest_api/bridges/bridge_by_id/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/rest_api/bridges/bridge_by_id/test-config.yaml?view=diff&rev=5609&r1=5608&r2=5609
==============================================================================
--- asterisk/trunk/tests/rest_api/bridges/bridge_by_id/test-config.yaml (original)
+++ asterisk/trunk/tests/rest_api/bridges/bridge_by_id/test-config.yaml Fri Sep 19 07:49:59 2014
@@ -25,8 +25,8 @@
         config-section: test-object-config
         typename: ari.AriOriginateTestObject
     modules:
-        -   config-section: ari-config
-            typename: ari.WebSocketEventModule
+        -   config-section: pluggable-config
+            typename: pluggable_modules.EventActionModule
 
 test-object-config:
     # using default origination:
@@ -34,141 +34,137 @@
     #   channelId: testsuite-default-id
     #   app: testsuite
 
-ari-config:
-    apps: testsuite
-    events:
-        -
-            conditions:
-                match:
-                    type: 'ChannelStateChange'
+pluggable-config:
+    -
+        ari-events:
+            match:
+                type: 'ChannelStateChange'
             count: '>1'
-        -
-            conditions:
-                match:
-                    type: StasisStart
-                    application: testsuite
-                    channel:
-                        id: 'testsuite-default-id$'
+    -
+        ari-events:
+            match:
+                type: StasisStart
+                application: testsuite
+                channel:
+                    id: 'testsuite-default-id$'
             count: 1
-            requests:
-                -
-                    method: 'post'
-                    uri: 'bridges'
-                    params:
-                        bridgeId: 'MyFirstBridge'
-                    # note: creating bridge does not result in another event
-                -
-                    # note: an explicit subscription to the bridge is required to catch BridgeDestroyed later
-                    method: 'post'
-                    uri: 'applications/testsuite/subscription'
-                    params:
-                        eventSource: 'bridge:MyFirstBridge'
-                -
-                    method: 'post'
-                    uri: 'channels'
-                    params:
-                        endpoint: 'Local/s at default'
-                        channelId: 'MyFirstChannel'
-                        app: 'testsuite'
-        -
-            conditions:
-                match:
-                    type: StasisStart
-                    application: testsuite
-                    channel:
-                        id: 'MyFirstChannel$'
+        ari-requests:
+            -
+                method: 'post'
+                uri: 'bridges'
+                params:
+                    bridgeId: 'MyFirstBridge'
+                # note: creating bridge does not result in another event
+            -
+                # note: an explicit subscription to the bridge is required to catch BridgeDestroyed later
+                method: 'post'
+                uri: 'applications/testsuite/subscription'
+                params:
+                    eventSource: 'bridge:MyFirstBridge'
+            -
+                method: 'post'
+                uri: 'channels'
+                params:
+                    endpoint: 'Local/s at default'
+                    channelId: 'MyFirstChannel'
+                    app: 'testsuite'
+    -
+        ari-events:
+            match:
+                type: StasisStart
+                application: testsuite
+                channel:
+                    id: 'MyFirstChannel$'
             count: 1
-            requests:
-                method: 'post'
-                uri: 'bridges/MyFirstBridge/addChannel'
-                params:
-                    channel: 'MyFirstChannel'
-        -
-            conditions:
-                match:
-                    type: ChannelEnteredBridge
-                    application: testsuite
-                    channel:
-                        id: 'MyFirstChannel$'
+        ari-requests:
+            method: 'post'
+            uri: 'bridges/MyFirstBridge/addChannel'
+            params:
+                channel: 'MyFirstChannel'
+    -
+        ari-events:
+            match:
+                type: ChannelEnteredBridge
+                application: testsuite
+                channel:
+                    id: 'MyFirstChannel$'
             count: 1
-            requests:
-                method: 'post'
-                uri: 'bridges/MyFirstBridge/removeChannel'
-                params:
-                    channel: 'MyFirstChannel'
-        -
-            conditions:
-                match:
-                    type: ChannelLeftBridge
-                    application: testsuite
-                    channel:
-                        id: 'MyFirstChannel$'
+        ari-requests:
+            method: 'post'
+            uri: 'bridges/MyFirstBridge/removeChannel'
+            params:

[... 137 lines stripped ...]



More information about the asterisk-commits mailing list