[asterisk-commits] mjordan: branch mjordan/twisted_process r3092 - in /asterisk/team/mjordan/twi...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Mar 14 10:54:51 CDT 2012


Author: mjordan
Date: Wed Mar 14 10:54:47 2012
New Revision: 3092

URL: http://svnview.digium.com/svn/testsuite?view=rev&rev=3092
Log:
Initial migration of python subprocess to twisted

And its going so well too.

Added:
    asterisk/team/mjordan/twisted_process/   (props changed)
      - copied from r3077, asterisk/trunk/
    asterisk/team/mjordan/twisted_process/lib/python/asterisk/TestSuiteUtils.py
      - copied unchanged from r3077, asterisk/trunk/lib/python/asterisk/utils.py
Removed:
    asterisk/team/mjordan/twisted_process/lib/python/asterisk/utils.py
Modified:
    asterisk/team/mjordan/twisted_process/lib/python/asterisk/TestCase.py
    asterisk/team/mjordan/twisted_process/lib/python/asterisk/TestConfig.py
    asterisk/team/mjordan/twisted_process/lib/python/asterisk/asterisk.py
    asterisk/team/mjordan/twisted_process/lib/python/asterisk/sippversion.py
    asterisk/team/mjordan/twisted_process/lib/python/asterisk/version.py
    asterisk/team/mjordan/twisted_process/runtests.py

Propchange: asterisk/team/mjordan/twisted_process/
------------------------------------------------------------------------------
    automerge = *

Propchange: asterisk/team/mjordan/twisted_process/
------------------------------------------------------------------------------
    reviewboard:url = https://reviewboard.asterisk.org

Propchange: asterisk/team/mjordan/twisted_process/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Wed Mar 14 10:54:47 2012
@@ -1,0 +1,2 @@
+asterisk-test-suite-report.xml
+*.pyc

Propchange: asterisk/team/mjordan/twisted_process/
------------------------------------------------------------------------------
--- svn:mergeinfo (added)
+++ svn:mergeinfo Wed Mar 14 10:54:47 2012
@@ -1,0 +1,2 @@
+/asterisk/team/group/cdr_test_log_congestion:1823-2951
+/asterisk/trunk:1112

Propchange: asterisk/team/mjordan/twisted_process/
------------------------------------------------------------------------------
    svnmerge-integrated = /asterisk/trunk:1-2945

Modified: asterisk/team/mjordan/twisted_process/lib/python/asterisk/TestCase.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mjordan/twisted_process/lib/python/asterisk/TestCase.py?view=diff&rev=3092&r1=3077&r2=3092
==============================================================================
--- asterisk/team/mjordan/twisted_process/lib/python/asterisk/TestCase.py (original)
+++ asterisk/team/mjordan/twisted_process/lib/python/asterisk/TestCase.py Wed Mar 14 10:54:47 2012
@@ -84,7 +84,7 @@
         self.__setup_conditions()
 
         logger.info("Executing " + self.test_name)
-        reactor.callWhenRunning(self.run)
+        reactor.callWhenRunning(self.__run)
 
     def __setup_conditions(self):
         """
@@ -199,6 +199,9 @@
         return PcapListener(device, bpf_filter, dumpfile, self.__pcap_callback)
 
     def start_asterisk(self):
+        return
+
+    def __start_asterisk(self):
         """
         Start the instances of Asterisk that were previously created.  See
         create_asterisk.  Note that this should be called before the reactor is
@@ -265,6 +268,12 @@
         logger.warning("Reactor timeout: '%s' seconds" % self.reactor_timeout)
         self.stop_reactor()
 
+    def __run(self):
+        logger.debug("Reactor is running: %s" % str(reactor.running))
+        if (self.ast):
+            self.__start_asterisk()
+        self.run()
+
     def run(self):
         """
         Base implementation of the test execution method, run.  Derived classes

Modified: asterisk/team/mjordan/twisted_process/lib/python/asterisk/TestConfig.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mjordan/twisted_process/lib/python/asterisk/TestConfig.py?view=diff&rev=3092&r1=3077&r2=3092
==============================================================================
--- asterisk/team/mjordan/twisted_process/lib/python/asterisk/TestConfig.py (original)
+++ asterisk/team/mjordan/twisted_process/lib/python/asterisk/TestConfig.py Wed Mar 14 10:54:47 2012
@@ -12,14 +12,13 @@
 import sys
 import os
 import subprocess
-import optparse
-import time
 import yaml
 import socket
 
 sys.path.append("lib/python")
 
-import utils
+import TestSuiteUtils
+
 from version import AsteriskVersion
 from asterisk import Asterisk
 from buildoptions import AsteriskBuildOptions
@@ -107,7 +106,7 @@
         self.met = False
         if "app" in dep:
             self.name = dep["app"]
-            self.met = utils.which(self.name) is not None
+            self.met = TestSuiteUtils.which(self.name) is not None
         elif "python" in dep:
             self.name = dep["python"]
             try:
@@ -184,7 +183,7 @@
         we run pjsua --help and parse the output to determine if --ipv6
         is a valid option
         '''
-        if utils.which('pjsua') is None:
+        if TestSuiteUtils.which('pjsua') is None:
             return False
 
         help_output = subprocess.Popen(['pjsua', '--help'],

Modified: asterisk/team/mjordan/twisted_process/lib/python/asterisk/asterisk.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mjordan/twisted_process/lib/python/asterisk/asterisk.py?view=diff&rev=3092&r1=3077&r2=3092
==============================================================================
--- asterisk/team/mjordan/twisted_process/lib/python/asterisk/asterisk.py (original)
+++ asterisk/team/mjordan/twisted_process/lib/python/asterisk/asterisk.py Wed Mar 14 10:54:47 2012
@@ -13,17 +13,93 @@
 
 import sys
 import os
-import signal
 import time
 import shutil
-import subprocess
-import utils
 import logging
+
+import TestSuiteUtils
 
 from config import ConfigFile
 from version import AsteriskVersion
 
+from twisted.internet import reactor, protocol, threads
+from twisted.internet import utils
+
 logger = logging.getLogger(__name__)
+
+class AsteriskCliCommand():
+    """
+    Manages an Asterisk CLI command.  This is a blocking operation
+    """
+
+    def __init__(self, host, cmd):
+        """
+        Create a new Asterisk CLI Protocol instance
+
+        host    The host this CLI instance will connect to
+        cmd     The command to spawn (Asterisk instance, with command line parameters)
+        """
+        self.__host = host
+        self.__cmd = cmd
+        self.exitcode = -1
+        self.output = ""
+        self.err = ""
+
+    def execute(self):
+        df = threads.deferToThread(self.__execute)
+        df.addErrback(self.__thread_err)
+
+    def __execute(self):
+        logger.debug("Spawning command")
+        df = utils.getProcessOutputAndValue(self.__cmd[0], self.__cmd)
+        df.addCallbacks(self.__cli_output_callback, self.__cli_error_callback)
+
+    def __cli_output_callback(self, result):
+        #self.transport.loseConnection()
+        logger.debug("Asterisk CLI %s exited %s" % (self.__host, str(result)))
+        self.__set_properties(result)
+
+    def __cli_error_callback(self, result):
+        #self.transport.loseConnection()
+        self.__set_properties(result)
+        logger.debug("Asterisk CLI %s exited %s" % (self.__host, str(result)))
+        logger.warning("Asterisk CLI %s exited %d with error: %s" % (self.__host, self.exitcode, self.err))
+
+    def __thread_err(self, result):
+        logger.warning("Asterisk CLI %s failed to spawn properly: %s" % (self.__host, str(result)))
+
+    def __set_properties(self, result):
+        out, err, code = result
+        self.exitcode = code
+        self.output = out
+        self.err = err
+
+class AsteriskProtocol(protocol.ProcessProtocol):
+    def __init__(self, host):
+        self.output = ""
+        self.__host = host
+        self.exitcode = 0
+        self.exited = False
+
+    def outReceived(self, data):
+        logger.debug("Asterisk %s received: %s" % (self.__host, data))
+        self.output += data
+
+    def connectionMade(self):
+        logger.debug("Asterisk %s - connection made" % (self.__host))
+
+    def errReceived(self, data):
+        logger.warn("Asterisk %s received error: %s" % (self.__host, data))
+
+    def processExited(self, reason):
+        logger.debug("Asterisk %s exited with code %d" % (self.__host, reason.value.exitCode,))
+        self.exitcode = reason.value.exitCode
+        self.exitcode = True
+
+    def processEnded(self, reason):
+        logger.debug("Asterisk %s ended with code %d" % (self.__host, reason.value.exitCode,))
+        self.exitcode = reason.value.exitCode
+        self.exited = True
 
 class Asterisk:
     """An instance of Asterisk.
@@ -68,7 +144,7 @@
         if base is not None:
             self.base = "%s/%s" % (self.base, base)
         self.astetcdir = Asterisk.asterisk_etc_directory
-        self.ast_binary = utils.which("asterisk") or "/usr/sbin/asterisk"
+        self.ast_binary = TestSuiteUtils.which("asterisk") or "/usr/sbin/asterisk"
         self.host = host
 
         self.__ast_conf_options = ast_conf_options
@@ -123,32 +199,25 @@
             "-f", "-g", "-q", "-m", "-n",
             "-C", "%s" % os.path.join(self.astetcdir, "asterisk.conf")
         ]
-        try:
-            self.process = subprocess.Popen(cmd)
-        except OSError:
-            logger.error("Failed to execute command: %s" % str(cmd))
-            return False
+
+        self.processProtocol = AsteriskProtocol(self.host)
+        self.process = reactor.spawnProcess(self.processProtocol, cmd[0], cmd)
 
         # Be _really_ sure that Asterisk has started up before returning.
 
         # Poll the instance to make sure we created it successfully
-        self.process.poll()
-        if self.process.returncode != None:
+        if (self.processProtocol.exited):
             """ Rut roh, Asterisk process exited prematurely """
-            logger.error("Asterisk instance %s exited prematurely with return code %d" % (self.host, self.process.returncode))
+            logger.error("Asterisk instance %s exited prematurely with return code %d" % (self.host, self.processProtocol.exitcode))
 
         start = time.time()
-        while True:
+        for i in xrange(5):
+            time.sleep(1.0)
             # This command should stall until completed, but if an
             # exception occurs, it returns the empty string.
-            if not self.cli_exec("core waitfullybooted", warn_on_fail=False):
-                if time.time() - start > 5:
-                    logger.error("Unknown state of asterisk. Stopping waitfullybooted...")
-                    break
-                logger.debug("Attempting waitfullybooted again...")
-            else:
-                # We're fully booted...
-                break
+            if self.cli_exec("core waitfullybooted", warn_on_fail=False):
+                break;
+
 
     def stop(self):
         """Stop this instance of Asterisk.
@@ -165,11 +234,8 @@
             self.cli_exec("core stop gracefully")
         for i in xrange(5):
             time.sleep(1.0)
-            if self.process.poll() is not None:
+            if self.processProtocol.exited:
                 return self.process.returncode
-
-        # Check for locks
-        self.cli_exec("core show locks")
 
         # If the graceful shutdown did not complete within 5 seconds, ask
         # Asterisk to stop right now.
@@ -179,29 +245,30 @@
             self.cli_exec("core stop now")
         for i in xrange(5):
             time.sleep(1.0)
-            if self.process.poll() is not None:
-                return self.process.returncode
+            if self.processProtocol.exited:
+                return self.processProtocol.exitcode
 
         # If even a "stop now" didn't do the trick, fall back to sending
         # signals to the process.  First, send a SIGTERM.  If it _STILL_ hasn't
         # gone away after another 5 seconds, send SIGKILL.
         try:
-            os.kill(self.process.pid, signal.SIGTERM)
+            self.process.signalProcess("TERM")
             for i in xrange(5):
                 time.sleep(1.0)
-                if self.process.poll() is not None:
-                    return self.process.returncode
-            os.kill(self.process.pid, signal.SIGKILL)
-        except OSError:
+                if self.processProtocol.exited:
+                    return self.processProtocol.exitcode
+            self.process.signalProcess("KILL")
+        except twisted.internet.error.ProcessExitedAlready:
             # Probably that we sent a signal to a process that was already
             # dead.  Just ignore it.
             pass
 
-        # We have done everything we can do at this point.  Wait for the
-        # process to exit.
-        self.process.wait()
-
-        return self.process.returncode
+        # If we get to this point and Asterisk still hasn't exited, there isn't much
+        # to do other then flag it
+        if not self.processProtocol.exited:
+            logger.error("Asterisk instance %s failed to exit" % self.host)
+
+        return self.processProtocol.exitcode
 
     def install_configs(self, cfg_path):
         """Installs all files located in the configuration directory for this
@@ -371,31 +438,18 @@
         logger.debug("Executing %s ..." % cmd)
 
         if not blocking:
-            process = subprocess.Popen(cmd)
+            cliProtocol = AsteriskProtocol(("CLI:%s" % self.host))
+            process = reactor.spawnProcess(cliProtocol, cmd[0], cmd)
             return ""
 
-        try:
-            process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
-                                       stderr=subprocess.STDOUT)
-        except OSError:
-            warn("Failed to execute command: %s" % str(cmd))
-            return ""
-
-        output = ""
-        try:
-            for l in process.stdout.readlines():
-                logger.debug(l.rstrip())
-                output += l
-        except IOError:
-            pass
-        try:
-            res = process.wait()
-            if res != None and res != 0:
-                warn("Exited non-zero [%d] while executing command %s" % (res, str(cmd)))
-                output = ""
-        except OSError:
-            pass
-        return output
+        cliProtocol = AsteriskCliCommand(self.host, cmd)
+        cliProtocol.execute()
+        for i in range(0, 5):
+            if cliProtocol.exitcode >= 0:
+                return cliProtocol.output
+            else:
+                time.sleep(1.0)
+                logger.debug("Checking again")
 
     def __make_directory_structure(self):
         """ Mirror system directory structure """

Modified: asterisk/team/mjordan/twisted_process/lib/python/asterisk/sippversion.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mjordan/twisted_process/lib/python/asterisk/sippversion.py?view=diff&rev=3092&r1=3077&r2=3092
==============================================================================
--- asterisk/team/mjordan/twisted_process/lib/python/asterisk/sippversion.py (original)
+++ asterisk/team/mjordan/twisted_process/lib/python/asterisk/sippversion.py Wed Mar 14 10:54:47 2012
@@ -8,13 +8,12 @@
 the GNU General Public License Version 2.
 """
 
-import re
 import subprocess
 import sys
 import unittest
 sys.path.append("lib/python")
 
-import utils
+import TestSuiteUtils
 
 class SIPpVersion:
     """A SIPp Version.
@@ -35,7 +34,7 @@
         self.pcap = False
 
         if version is None and feature is None:
-            sipp = utils.which("sipp")
+            sipp = TestSuiteUtils.which("sipp")
             if sipp is None:
                return
 

Modified: asterisk/team/mjordan/twisted_process/lib/python/asterisk/version.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mjordan/twisted_process/lib/python/asterisk/version.py?view=diff&rev=3092&r1=3077&r2=3092
==============================================================================
--- asterisk/team/mjordan/twisted_process/lib/python/asterisk/version.py (original)
+++ asterisk/team/mjordan/twisted_process/lib/python/asterisk/version.py Wed Mar 14 10:54:47 2012
@@ -11,12 +11,12 @@
 the GNU General Public License Version 2.
 """
 
-import sys
 import re
 import unittest
 import logging
 import subprocess
-import utils
+
+import TestSuiteUtils
 
 logger = logging.getLogger(__name__)
 
@@ -137,7 +137,7 @@
         """
         if not hasattr(cls, "_asterisk_version_from_binary"):
             version = ""
-            ast_binary = utils.which("asterisk") or "/usr/sbin/asterisk"
+            ast_binary = TestSuiteUtils.which("asterisk") or "/usr/sbin/asterisk"
             cmd = [
                 ast_binary,
                 "-V",

Modified: asterisk/team/mjordan/twisted_process/runtests.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mjordan/twisted_process/runtests.py?view=diff&rev=3092&r1=3077&r2=3092
==============================================================================
--- asterisk/team/mjordan/twisted_process/runtests.py (original)
+++ asterisk/team/mjordan/twisted_process/runtests.py Wed Mar 14 10:54:47 2012
@@ -24,7 +24,7 @@
 from asterisk.version import AsteriskVersion
 from asterisk.asterisk import Asterisk
 from asterisk.TestConfig import Dependency, TestConfig
-from asterisk import utils
+from asterisk import TestSuiteUtils
 
 TESTS_CONFIG = "tests.yaml"
 TEST_RESULTS = "asterisk-test-suite-report.xml"




More information about the asterisk-commits mailing list