[asterisk-commits] mjordan: testsuite/asterisk/trunk r3423 - in /asterisk/trunk: lib/python/aste...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Aug 10 09:48:06 CDT 2012


Author: mjordan
Date: Fri Aug 10 09:48:02 2012
New Revision: 3423

URL: http://svnview.digium.com/svn/testsuite?view=rev&rev=3423
Log:
Prevent reactor timeouts from leaving orphaned SIPp processes

This patch does two things:
1. It migrates the SIPp process from using a provided twisted process
   protocol (from the utils module) to using a full blown ProcessProtocol.
   This lets us kill the process that the ProcessProtocol object manages
   in a much easier fashion.
2. When a reactor time out occurs and stop_asterisk is called, the provided
   concrete implementation of TestClass in the sipp module will now attempt
   to kill any SIPp processes that have failed to exit.

The presence state tests, which manage their own SIPp instances, have also
been modified to kill the SIPp instances if they fail to exit.

Modified:
    asterisk/trunk/lib/python/asterisk/sipp.py
    asterisk/trunk/tests/channels/SIP/sip_custom_presence/multiple_state_change/run-test
    asterisk/trunk/tests/channels/SIP/sip_custom_presence/nominal_state_change/run-test
    asterisk/trunk/tests/channels/SIP/sip_custom_presence/non_digium_state_change/run-test
    asterisk/trunk/tests/channels/SIP/sip_custom_presence/resubscribe/run-test

Modified: asterisk/trunk/lib/python/asterisk/sipp.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/sipp.py?view=diff&rev=3423&r1=3422&r2=3423
==============================================================================
--- asterisk/trunk/lib/python/asterisk/sipp.py (original)
+++ asterisk/trunk/lib/python/asterisk/sipp.py Fri Aug 10 09:48:02 2012
@@ -59,6 +59,7 @@
         self._final_observers = []
         self._test_config = test_config
         self._current_test = 0
+        self.scenarios = []
         if 'fail-on-any' in self._test_config:
             self._fail_on_any = self._test_config['fail-on-any']
         else:
@@ -75,6 +76,13 @@
         self.create_ami_factory()
 
         self.ast[0].cli_exec('sip set debug on')
+
+
+    def stop_asterisk(self):
+        ''' Kill any remaining SIPp scenarios '''
+        for scenario in self.scenarios:
+            if not scenario.exited:
+                scenario.kill()
 
 
     def ami_connect(self, ami):
@@ -156,7 +164,6 @@
             self.stop_reactor()
             return result
 
-        scenarios = []
         for defined_scenario_set in self._test_config['test-iterations']:
 
             scenario_set = defined_scenario_set['scenarios']
@@ -315,6 +322,68 @@
         dl.addCallback(__execute_next)
 
 
+class SIPpProtocol(protocol.ProcessProtocol):
+    ''' Class that manages a SIPp instance '''
+
+    def __init__(self, name, stop_deferred):
+        ''' Create a SIPp process
+
+        Keyword Arguments:
+        name - the name of the scenario
+        stop_deferred - a twisted Deferred object that will be called when the
+        process has exited
+        '''
+        self.__name = name
+        self.output = ""
+        self.exitcode = 0
+        self.exited = False
+        self.stderr = []
+        self.__stop_deferred = stop_deferred
+
+    def kill(self):
+        ''' Kill the SIPp scenario '''
+        if not self.exited:
+            LOGGER.warn("Killing SIPp Scenario %s" % self.__name)
+            self.transport.signalProcess('KILL')
+
+    def outReceived(self, data):
+        ''' Override of ProcessProtocol.outReceived '''
+        LOGGER.debug("Received from SIPp scenario %s: %s" % (self.__name, data))
+        self.output += data
+
+    def connectionMade(self):
+        ''' Override of ProcessProtocol.connectionMade '''
+        LOGGER.debug("Connection made to SIPp scenario %s" % (self.__name))
+
+    def errReceived(self, data):
+        ''' Override of ProcessProtocol.errReceived '''
+        # SIPp will send some 'normal' messages to stderr.  Buffer them so we
+        # can output them later if we want
+        self.stderr.append(data)
+
+    def processEnded(self, reason):
+        ''' Override of ProcessProtocol.processEnded '''
+        if self.exited:
+            return reason
+
+        self.exited = True
+        message = ""
+        if reason.value and reason.value.exitCode:
+            message = "SIPp scenario %s ended with code %d" % (self.__name, reason.value.exitCode,)
+            self.exitcode = reason.value.exitCode
+            for msg in self.stderr:
+                LOGGER.warn(msg)
+        else:
+            message = "SIPp scenario %s ended " % self.__name
+        try:
+            if not self.__stop_deferred.called:
+                self.__stop_deferred.callback(self)
+        except defer.AlreadyCalledError:
+            pass
+        LOGGER.info(message)
+        return reason
+
+
 class SIPpScenario:
     """
     A SIPp based scenario for the Asterisk testsuite.
@@ -359,6 +428,14 @@
         self.default_port = 5061
         self.sipp = TestSuiteUtils.which("sipp")
         self.passed = False
+        self.exited = False
+        self._process = None
+
+    def kill(self):
+        """ Kill the executing SIPp scenario """
+        if self._process:
+            self._process.kill()
+        return
 
     def run(self, test_case = None):
         """ Execute a SIPp scenario
@@ -375,31 +452,24 @@
         has exited.
         """
 
-        def __output_callback(result):
-            """ Callback from getProcessOutputAndValue """
-            out, err, code = result
-            LOGGER.debug("Launching SIPp Scenario %s exited %d"
-                % (self.scenario['scenario'], code))
-            if (code == 0):
+        def __scenario_callback(result):
+            self.exited = True
+            if (result.exitcode == 0):
                 self.passed = True
                 LOGGER.info("SIPp Scenario %s Exited" % (self.scenario['scenario']))
             else:
-                LOGGER.warning("SIPp Scenario %s Failed [%d, %s]" % (self.scenario['scenario'], code, err))
-            self.__exit_deferred.callback(self)
-
-        def __error_callback(result):
-            """ Errback from getProcessOutputAndValue """
-            out, err, code = result
-            LOGGER.warning("SIPp Scenario %s Failed [%d, %s]" % (self.scenario['scenario'], code, err))
-            self.__exit_deferred.callback(self)
-
-        def __evalute_scenario_results(result):
+                LOGGER.warning("SIPp Scenario %s Failed [%d]" % (self.scenario['scenario'], result.exitcode))
+            self._our_exit_deferred.callback(self)
+            return result
+
+        def __evaluate_scenario_results(result):
             """ Convenience function.  If the test case is injected into this method,
             then auto-fail the test if the scenario fails. """
             if not self.passed:
                 LOGGER.warning("SIPp Scenario %s Failed" % self.scenario['scenario'])
                 self.__test_case.passed = False
                 self.__test_case.stop_reactor()
+            return result
 
         sipp_args = [
                 self.sipp, '127.0.0.1',
@@ -425,15 +495,18 @@
         LOGGER.info("Executing SIPp scenario: %s" % self.scenario['scenario'])
         LOGGER.debug(sipp_args)
 
-        self.__exit_deferred = defer.Deferred()
-
-        df = utils.getProcessOutputAndValue(sipp_args[0], sipp_args, {"TERM" : "vt100",}, None, None)
-        df.addCallbacks(__output_callback, __error_callback)
+        self._our_exit_deferred = defer.Deferred()
+
+        exit_deferred = defer.Deferred()
+        exit_deferred.addCallback(__scenario_callback)
         if test_case:
             self.__test_case = test_case
-            df.addCallback(__evalute_scenario_results)
-
-        return self.__exit_deferred
+            exit_deferred.addCallback(__evaluate_scenario_results)
+
+        self._process = SIPpProtocol(self.scenario['scenario'], exit_deferred)
+        reactor.spawnProcess(self._process, sipp_args[0], sipp_args, {"TERM" : "vt100",}, None, None)
+
+        return self._our_exit_deferred
 
 class SIPpTest(TestCase):
     """
@@ -482,6 +555,16 @@
         self.scenarios = scenarios
         self.result = []
         self.create_asterisk()
+        self._scenario_objects = []
+
+
+    def stop_asterisk(self):
+        ''' Kill any scenarios still in existance '''
+        for scenario in self._scenario_objects:
+            if not scenario.exited:
+                LOGGER.warn("SIPp Scenario %s has not exited; killing" % scenario.name)
+                scenario.kill()
+
 
     def run(self):
         """
@@ -510,6 +593,7 @@
             if not '-p' in s:
                 s['-p'] = str(default_port)
             scenario = SIPpScenario(self.test_dir, s)
+            self._scenario_objects.append(scenario)
             df = scenario.run(self)
             df.addCallback(__check_result)
             deferds.append(df)

Modified: asterisk/trunk/tests/channels/SIP/sip_custom_presence/multiple_state_change/run-test
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/SIP/sip_custom_presence/multiple_state_change/run-test?view=diff&rev=3423&r1=3422&r2=3423
==============================================================================
--- asterisk/trunk/tests/channels/SIP/sip_custom_presence/multiple_state_change/run-test (original)
+++ asterisk/trunk/tests/channels/SIP/sip_custom_presence/multiple_state_change/run-test Fri Aug 10 09:48:02 2012
@@ -102,6 +102,11 @@
         ami.registerEvent("TestEvent", self.inspectPresence)
         self.runSippTest()
 
+    def stop_asterisk(self):
+        ''' Kill the SIPp test if it didn't exit '''
+        if not self.sipTest.exited:
+            self.sipTest.kill()
+
     def run(self):
         TestCase.run(self)
         self.create_ami_factory()

Modified: asterisk/trunk/tests/channels/SIP/sip_custom_presence/nominal_state_change/run-test
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/SIP/sip_custom_presence/nominal_state_change/run-test?view=diff&rev=3423&r1=3422&r2=3423
==============================================================================
--- asterisk/trunk/tests/channels/SIP/sip_custom_presence/nominal_state_change/run-test (original)
+++ asterisk/trunk/tests/channels/SIP/sip_custom_presence/nominal_state_change/run-test Fri Aug 10 09:48:02 2012
@@ -77,6 +77,11 @@
         if self.num_notifies == 2:
             self.notifyPassed = True
 
+    def stop_asterisk(self):
+        ''' Kill the SIPp test if it didn't exit '''
+        if not self.sipTest.exited:
+            self.sipTest.kill()
+
     def ami_connect(self, ami):
         self.ast[ami.id].cli_exec("sip set debug on")
         ami.registerEvent("TestEvent", self.inspectPresence)

Modified: asterisk/trunk/tests/channels/SIP/sip_custom_presence/non_digium_state_change/run-test
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/SIP/sip_custom_presence/non_digium_state_change/run-test?view=diff&rev=3423&r1=3422&r2=3423
==============================================================================
--- asterisk/trunk/tests/channels/SIP/sip_custom_presence/non_digium_state_change/run-test (original)
+++ asterisk/trunk/tests/channels/SIP/sip_custom_presence/non_digium_state_change/run-test Fri Aug 10 09:48:02 2012
@@ -68,6 +68,11 @@
         ami.registerEvent("UserEvent", self.originateComplete)
         self.runSippTest()
 
+    def stop_asterisk(self):
+        ''' Kill the SIPp test if it didn't exit '''
+        if not self.sipTest.exited:
+            self.sipTest.kill()
+
     def run(self):
         TestCase.run(self)
         self.create_ami_factory()

Modified: asterisk/trunk/tests/channels/SIP/sip_custom_presence/resubscribe/run-test
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/tests/channels/SIP/sip_custom_presence/resubscribe/run-test?view=diff&rev=3423&r1=3422&r2=3423
==============================================================================
--- asterisk/trunk/tests/channels/SIP/sip_custom_presence/resubscribe/run-test (original)
+++ asterisk/trunk/tests/channels/SIP/sip_custom_presence/resubscribe/run-test Fri Aug 10 09:48:02 2012
@@ -120,6 +120,13 @@
         ami.registerEvent("TestEvent", self.inspectPresence)
         self.runSippTest()
 
+    def stop_asterisk(self):
+        ''' Kill the SIPp test if it didn't exit '''
+        if not self.sipTestInitial.exited:
+            self.sipTestInitial.kill()
+        if not self.sipTestResubscribe.exited:
+            self.sipTestResubscribe.kill()
+
     def run(self):
         TestCase.run(self)
         self.create_ami_factory()




More information about the asterisk-commits mailing list