[asterisk-commits] sgriepentrog: testsuite/asterisk/trunk r4723 - in /asterisk/trunk: lib/python...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Feb 17 15:52:41 CST 2014
Author: sgriepentrog
Date: Mon Feb 17 15:52:36 2014
New Revision: 4723
URL: http://svnview.digium.com/svn/testsuite?view=rev&rev=4723
Log:
testsuite: LinkedID Propagation test
This monitors bridge enter/exit and CEL events, and builds
a picture of which channels are bridged together, and when
their LinkedID changes, and then verifies that each change
is correct.
Note: The BridgeEnter and CEL 'BRIDGE_ENTER' events can be
in either order - so wait for both to arrive before making
a judgement on the LinkedID.
This test can be added to any other bridging test to add a
layer of LinkedId Propagation checking to it.
The bridge_action test has been changed to use an event to
trigger channel creation sequence which resolves a problem
with creation order determination.
(issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3182/
Added:
asterisk/trunk/lib/python/asterisk/linkedid_check.py (with props)
Modified:
asterisk/trunk/tests/bridge/bridge_action/bridge_action.py
asterisk/trunk/tests/bridge/bridge_action/test-config.yaml
Added: asterisk/trunk/lib/python/asterisk/linkedid_check.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/linkedid_check.py?view=auto&rev=4723
==============================================================================
--- asterisk/trunk/lib/python/asterisk/linkedid_check.py (added)
+++ asterisk/trunk/lib/python/asterisk/linkedid_check.py Mon Feb 17 15:52:36 2014
@@ -1,0 +1,278 @@
+#!/usr/bin/env python
+""" Asterisk TestSuite LinkedId Propagation Test
+
+Copyright (C) 2014, Digium, Inc.
+Scott Griepentrog <sgriepentrog at digium.com>
+
+This program is free software, distributed under the terms
+of the GNU General Public License Version 2.
+
+
+This monitors bridge enter/exit and CEL events, and builds
+a picture of which channels are bridged together, and when
+their LinkedID changes, and then verifies that each change
+is correct.
+
+Note: The BridgeEnter and CEL 'BRIDGE_ENTER' events can be
+in either order - so wait for both to arrive before making
+a judgement on the LinkedID.
+
+This test can be added to any other bridging test to add a
+layer of LinkedId Propagation checking to it. However the
+creation of channels has to be done one at a time by using
+a Newchannel event to trigger the next one - so that order
+of creation can be determined. Also required is that this
+test module is listed before others to that it can process
+bridge events first before the test makes changes.
+
+"""
+
+import logging
+
+LOGGER = logging.getLogger(__name__)
+
+
+class LinkedIdChannel(object):
+ def __init__(self, uniqueid, order, test):
+ """ This initializes the LinkedID Channel object
+
+ Keyword Arguments:
+ uniqueid - channel unique id
+ order - creation order
+ test - the test_object used to set failure indication
+ """
+ self.order = order # creation order for age test
+ self.uniqueid = uniqueid # uniqueid for channel
+ self.linkedid = False # what linkedid should be
+ self.cel_linkedid = False # linkedid from last CEL event
+ self.failed = False # squelch repeated errors
+ self.test = test # copy of test object (to set fail)
+
+ LOGGER.info('New channel %s created order #%d' %
+ (uniqueid, self.order))
+
+ def event(self, event):
+ """ This handles a channel event
+
+ Keyword Arguments:
+ event - key value pairs in dict from AMI CEL event
+ """
+ uniqueid = event['uniqueid']
+ linked = event['linkedid']
+
+ if self.cel_linkedid != linked:
+ previous = self.cel_linkedid
+ self.cel_linkedid = linked
+ LOGGER.info('%s changed LinkedID %s to %s after CEL %s' %
+ (uniqueid, previous, linked, event['eventname']))
+
+ # this could be a CEL BRIDGE_ENTER showing up before the
+ # BridgeEnter, so don't evaluate what it should be here
+ # as it will caught on the next CEL event if wrong
+ return
+
+ # check that this CEL event has the correct LinkedId
+ if self.cel_linkedid != self.linkedid and not self.failed:
+ LOGGER.info('%s has LinkedID %s after CEL %s ' %
+ (uniqueid, self.cel_linkedid, event['eventname']))
+
+ """ This is not currently considered an error but should be!
+
+ If the first channel that entered the bridge has it's linkedid
+ changed when the second channel enters, there is not another CEL
+ event divulging the new linkedid, and it can be an old linkedid
+ pulled from the stasis cache when the next event occurs on this
+ channel.
+
+ TODO: change this to error and set failure when Asterisk fixed
+ """
+ LOGGER.warning('LinkedID Propagation error: ' +
+ 'Last CEL on %s has %s should be %s' %
+ (uniqueid, self.cel_linkedid, self.linkedid))
+
+ self.failed = True
+ #self.test.set_passed(False)
+
+ def getvar_callback(self, result):
+ LOGGER.info('AMI query indicates channel %s has linkedid %s' %
+ (self.uniqueid, result))
+
+ if self.linkedid != result:
+ LOGGER.error('LinkedID Propagation error: ' +
+ 'Channel %s has %s should be %s' %
+ (self.uniqueid, result, self.linkedid))
+ self.test.set_passed(False)
+
+ def getvar_failback(self, reason):
+ LOGGER.error('Failed to getvar linkedid for channel %s: %s' %
+ (self.uniqueid, reason))
+
+ def query_linkedid(self, ami):
+ """ query the channel to get the current linkedid
+
+ Keyword Arguments:
+ ami - The ami manager object
+ """
+ ami.getVar(self.uniqueid, 'CHANNEL(linkedid)'
+ ).addCallbacks(self.getvar_callback, self.getvar_failback)
+
+
+class LinkedIdCheck(object):
+ def __init__(self, module_config, test_object):
+ """ This initializes the LinkedId Check test object.
+
+ Keyword Arguments:
+ module_config - configuration section (not used)
+ test_object - the TestCase object running the test
+ """
+ # ask AMI to let us hook in for events
+ test_object.register_ami_observer(self.ami_connect)
+ # keep a ref to test_object so we can set failure
+ self.test_object = test_object
+
+ # initialize variables used in this class
+ self.channels = {} # channel objects
+ self.bridges = {} # bridge num for readable logs
+ self.chan_bridge = {} # what bridgeid is chan in or False
+
+ def ami_connect(self, ami):
+ """ register with StarPy AMI manager to get events
+
+ This registers with the StarPY AMI manager to get
+ the events we are interested in.
+
+ Keyword Arguments:
+ ami - The AMI manager object
+ """
+ ami.registerEvent('Newchannel', self.new_channel)
+ ami.registerEvent('CEL', self.channel_event)
+ ami.registerEvent('BridgeEnter', self.bridge_enter)
+ ami.registerEvent('BridgeLeave', self.bridge_leave)
+
+ def new_channel(self, ami, event):
+ """ handle new channel events to get correct order
+
+ It is possible for CEL CHAN_START events to arrive in
+ the wrong order, which throws off age calculations.
+ It is also possible for the New Channel event to be
+ later than the CEL CHAN_START, so handle creation on
+ either.
+
+ Keyword Arguments:
+ ami - The AMI manager object
+ event - The event key/value pairs in a dict
+ """
+ uniqueid = event['uniqueid']
+ if uniqueid not in self.channels.keys():
+ new_chan = LinkedIdChannel(uniqueid,
+ len(self.channels) + 1,
+ self.test_object)
+ self.channels[uniqueid] = new_chan
+ self.chan_bridge[uniqueid] = False
+
+ def channel_event(self, ami, event):
+ """ handle channel events to get channel's LinkedID
+
+ This handles Channel Event Logger events, which
+ are monitored because they contain the LinkedID
+ associated with the channel
+
+ Keyword Arguments
+ ami - The AMI manager object
+ event - The event key/value pairs in a dict
+ """
+
+ # insure the channel object is created for a new uniqueid
+ uniqueid = event['uniqueid']
+ linkedid = event['linkedid']
+ if not uniqueid in self.channels.keys():
+ new_chan = LinkedIdChannel(uniqueid,
+ len(self.channels) + 1,
+ self.test_object)
+ self.channels[uniqueid] = new_chan
+ self.chan_bridge[uniqueid] = False
+
+ # insure starting linkedid is updated
+ if not self.channels[uniqueid].linkedid:
+ self.channels[uniqueid].linkedid = linkedid
+ self.channels[uniqueid].cel_linkedid = linkedid
+ LOGGER.info('%s has LinkedID %s at %s ' %
+ (uniqueid, event['linkedid'], event['eventname']))
+
+ # pass event to the channel
+ self.channels[uniqueid].event(event)
+
+ def bridge_enter(self, ami, event):
+ """ handle bridge enter events, track bridged channels
+
+ This handles Bridge Enter events, used to track
+ which channels are in which bridges together.
+
+ Keyword Arguments
+ ami - The AMI manager object
+ event - The event key/value pairs in a dict
+ """
+ uniqueid = event['uniqueid']
+ bridgeid = event['bridgeuniqueid']
+
+ # uniqueid bridges numerically in logs for better readability
+ if bridgeid not in self.bridges.keys():
+ self.bridges[bridgeid] = len(self.bridges) + 1
+
+ # list of channels in this bridge
+ bridged_with = [uid for uid in self.chan_bridge.keys()
+ if self.chan_bridge[uid] == bridgeid]
+
+ LOGGER.info('%s entered bridge #%d with %s' %
+ (uniqueid, self.bridges[bridgeid], bridged_with))
+
+ # mark this channel in the bridge
+ self.chan_bridge[uniqueid] = bridgeid
+
+ # if alone in the bridge, skip the checks
+ if not bridged_with:
+ return
+
+ # determine the correct linkedid for chans in this bridge
+ bridged_with.append(uniqueid)
+ oldest = False
+ for uid in bridged_with:
+ linkedid = self.channels[uid].linkedid
+ LOGGER.info('EVAL => %s has LinkedID %s (%d)' %
+ (uid, linkedid, self.channels[linkedid].order))
+ if not oldest:
+ oldest = self.channels[uid].linkedid
+ elif self.channels[linkedid].order < self.channels[oldest].order:
+ oldest = self.channels[uid].linkedid
+
+ LOGGER.info('EVAL RESULT: oldest LinkedID in bridge #%d is %s (%d)' %
+ (self.bridges[bridgeid], oldest,
+ self.channels[oldest].order))
+
+ # set the value that it should be for comparison later
+ # when the next CEL event arrives
+ for uid in bridged_with:
+ self.channels[uid].linkedid = oldest
+
+ # have each channel query the linkedid because CEL may be old
+ self.channels[uid].query_linkedid(ami)
+
+ def bridge_leave(self, ami, event):
+ """ handle bridge leave events
+
+ This handles Bridge Leave events, used to track
+ which channels are in which bridges together.
+
+ Keyword Arguments
+ ami - The AMI manager object
+ event - The event key/value pairs in a dict
+ """
+ uniqueid = event['uniqueid']
+ bridgeid = event['bridgeuniqueid']
+ self.chan_bridge[uniqueid] = False
+ bridged_with = [uid for uid in self.chan_bridge.keys()
+ if self.chan_bridge[uid] == bridgeid]
+ LOGGER.info('%s left bridge #%d leaving %s' %
+ (uniqueid, self.bridges[bridgeid], bridged_with))
+
+# vim:sw=4:ts=4:expandtab:textwidth=79
Propchange: asterisk/trunk/lib/python/asterisk/linkedid_check.py
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: asterisk/trunk/lib/python/asterisk/linkedid_check.py
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: asterisk/trunk/lib/python/asterisk/linkedid_check.py
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: asterisk/trunk/tests/bridge/bridge_action/bridge_action.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/bridge/bridge_action/bridge_action.py?view=diff&rev=4723&r1=4722&r2=4723
==============================================================================
--- asterisk/trunk/tests/bridge/bridge_action/bridge_action.py (original)
+++ asterisk/trunk/tests/bridge/bridge_action/bridge_action.py Mon Feb 17 15:52:36 2014
@@ -14,8 +14,8 @@
class BridgeAction(object):
''' Pluggable Module object that manipulates channels
- using the Bridge AMI action
- '''
+ using the Bridge AMI action
+ '''
def __init__(self, module_config, test_object):
''' Constructor
@@ -27,6 +27,7 @@
self.test_object.register_ami_observer(self._ami_connected_handler)
self.test_object.register_stop_observer(self._stop_handler)
self.channels = []
+ self.originate = 1
self.round = 1
self.ami = None
self.channel_state = {}
@@ -45,15 +46,34 @@
ami.registerEvent('Newexten', self._new_exten_handler)
ami.registerEvent('BridgeEnter', self._bridge_enter_handler)
ami.registerEvent('BridgeLeave', self._bridge_leave_handler)
+ ami.registerEvent('Newchannel', self._new_channel_handler)
# Originate some channels
LOGGER.debug('Originating channels')
- for i in range(0, 5):
+ ami.originate(channel='Local/waiting_area at default',
+ context='default',
+ exten='waiting_area',
+ priority=1,
+ async=True).addErrback(self.test_object.
+ handle_originate_failure)
+
+ def _new_channel_handler(self, ami, event):
+ ''' AMI Newchannel event handler
+
+ :param ami The AMI instance that the event was received from
+ :param event The AMI Newchannel event
+
+ Wait until the new channel event has been received before
+ creating the next channel.
+ '''
+ if (';2' in event['channel'] and self.originate < 5):
+ self.originate += 1
ami.originate(channel='Local/waiting_area at default',
- context='default',
- exten='waiting_area',
- priority=1,
- async=True).addErrback(self.test_object.handle_originate_failure)
+ context='default',
+ exten='waiting_area',
+ priority=1,
+ async=True).addErrback(self.test_object.
+ handle_originate_failure)
def _stop_handler(self, result):
''' A deferred callback called as a result of the test stopping
Modified: asterisk/trunk/tests/bridge/bridge_action/test-config.yaml
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/bridge/bridge_action/test-config.yaml?view=diff&rev=4723&r1=4722&r2=4723
==============================================================================
--- asterisk/trunk/tests/bridge/bridge_action/test-config.yaml (original)
+++ asterisk/trunk/tests/bridge/bridge_action/test-config.yaml Mon Feb 17 15:52:36 2014
@@ -13,7 +13,8 @@
the channels from the second round's Bridge is chosen. These are Bridged
as well. This moves the channels from existing bridges into a new
Bridge.
- * If all BridgeEnter events are received, the test passes.'
+ * If all BridgeEnter events are received, the test passes.
+ * Now also tests LinkedID Propgataion.'
test-modules:
add-test-to-search-path: 'True'
@@ -21,6 +22,8 @@
config-section: test-object-config
typename: 'test_case.TestCaseModule'
modules:
+ -
+ typename: 'linkedid_check.LinkedIdCheck'
-
config-section: bridge-action
typename: 'bridge_action.BridgeAction'
More information about the asterisk-commits
mailing list