[asterisk-commits] mmichelson: branch mmichelson/phone-testsuite r3137 - in /asterisk/team/mmich...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Fri Mar 23 14:51:09 CDT 2012
Author: mmichelson
Date: Fri Mar 23 14:51:04 2012
New Revision: 3137
URL: http://svnview.digium.com/svn/testsuite?view=rev&rev=3137
Log:
Resolve conflict and reset automerge.
Added:
asterisk/team/mmichelson/phone-testsuite/tests/iax2/basic-call/configs/ast2/cdr.conf
- copied unchanged from r3136, asterisk/trunk/tests/iax2/basic-call/configs/ast2/cdr.conf
Removed:
asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/utils.py
asterisk/team/mmichelson/phone-testsuite/tests/chanspy/chanspy_barge/configs/ast1/manager.conf
asterisk/team/mmichelson/phone-testsuite/tests/iax2/basic-call/configs/ast1/manager.conf
asterisk/team/mmichelson/phone-testsuite/tests/iax2/basic-call/configs/ast2/manager.conf
asterisk/team/mmichelson/phone-testsuite/tests/udptl_v6/configs/ast1/manager.conf
asterisk/team/mmichelson/phone-testsuite/tests/udptl_v6/configs/ast2/manager.conf
Modified:
asterisk/team/mmichelson/phone-testsuite/ (props changed)
asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/CDRTestCase.py
asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/TestCase.py
asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/TestConfig.py
asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/asterisk.py
asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/cdr.py
asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/sipp.py
asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/sippversion.py
asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/version.py
asterisk/team/mmichelson/phone-testsuite/runtests.py
asterisk/team/mmichelson/phone-testsuite/tests/apps/incomplete/sip_incomplete/run-test
asterisk/team/mmichelson/phone-testsuite/tests/apps/voicemail/check_voicemail_forward_with_prepend/run-test
asterisk/team/mmichelson/phone-testsuite/tests/apps/voicemail/check_voicemail_new_user_hangup/run-test
asterisk/team/mmichelson/phone-testsuite/tests/apps/voicemail/func_vmcount/run-test
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/handle_response_address_incomplete/run-test
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/info_dtmf/run-test
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/nat_supertest/run-test
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/nat_supertest/sipp/inject.csv
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/nat_supertest/test-config.yaml
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/realtime_nosipregs/run-test
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/realtime_sipregs/run-test
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/sip_hold/run-test
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/sip_register_domain_acl/run-test
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/sip_tls_call/configs/ast1/extensions.conf
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/sip_tls_call/configs/ast2/extensions.conf
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/sip_tls_register/run-test
asterisk/team/mmichelson/phone-testsuite/tests/channels/SIP/use_contact_from_200/run-test
asterisk/team/mmichelson/phone-testsuite/tests/chanspy/chanspy_barge/run-test
asterisk/team/mmichelson/phone-testsuite/tests/chanspy/chanspy_w_mixmonitor/run-test
asterisk/team/mmichelson/phone-testsuite/tests/dynamic-modules/run-test
asterisk/team/mmichelson/phone-testsuite/tests/dynamic-modules/test-config.yaml
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/execute/run-test
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/get-data/run-test
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/hangup/run-test
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/record-file/run-test
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/say-alpha/run-test
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/say-date/run-test
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/say-datetime/run-test
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/say-digits/run-test
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/say-number/run-test
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/say-phonetic/run-test
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/say-time/run-test
asterisk/team/mmichelson/phone-testsuite/tests/fastagi/wait-for-digit/run-test
asterisk/team/mmichelson/phone-testsuite/tests/feature_attended_transfer/run-test
asterisk/team/mmichelson/phone-testsuite/tests/feature_blonde_transfer/run-test
asterisk/team/mmichelson/phone-testsuite/tests/iax2/basic-call/configs/ast1/extensions.conf
asterisk/team/mmichelson/phone-testsuite/tests/iax2/basic-call/configs/ast2/extensions.conf
asterisk/team/mmichelson/phone-testsuite/tests/iax2/basic-call/run-test
asterisk/team/mmichelson/phone-testsuite/tests/mixmonitor/run-test
asterisk/team/mmichelson/phone-testsuite/tests/mixmonitor_audiohook_inherit/run-test
asterisk/team/mmichelson/phone-testsuite/tests/udptl_v6/run-test
Propchange: asterisk/team/mmichelson/phone-testsuite/
------------------------------------------------------------------------------
automerge = *
Propchange: asterisk/team/mmichelson/phone-testsuite/
------------------------------------------------------------------------------
--- svn:mergeinfo (added)
+++ svn:mergeinfo Fri Mar 23 14:51:04 2012
@@ -1,0 +1,2 @@
+/asterisk/team/group/cdr_test_log_congestion:1823-2951
+/asterisk/trunk:1112
Propchange: asterisk/team/mmichelson/phone-testsuite/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Fri Mar 23 14:51:04 2012
@@ -1,1 +1,1 @@
-/asterisk/trunk:1-3129
+/asterisk/trunk:1-3136
Modified: asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/CDRTestCase.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/CDRTestCase.py?view=diff&rev=3137&r1=3136&r2=3137
==============================================================================
--- asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/CDRTestCase.py (original)
+++ asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/CDRTestCase.py Fri Mar 23 14:51:04 2012
@@ -73,7 +73,8 @@
self.CDRFileExpectations[recordname].append(cdrline)
def match_cdrs(self):
- #Automatically invoked at the end of the test, this will test the CDR files against the listed expectations.
+ """ Automatically invoked at the end of the test, this will test the CDR files against the listed expectations.
+ """
self.passed = True
for key in self.CDRFileExpectations:
@@ -114,7 +115,6 @@
def ami_logoff(self, ami):
"""
An AMI callback event.
-
"""
self.stop_reactor()
@@ -131,13 +131,25 @@
).addErrback(self.ami_logoff)
def ami_test_done(self, ami, event):
+ """ Check to see if the test is done during a hangup event
+ """
+ logger.debug(str(event))
if event.get("event") == "Hangup":
- if self.no_active_channels():
- try:
- self.stop_reactor()
- except ReactorNotRunning:
- # No problemo.
- pass
+ self.check_active_channels()
+
+ def check_active_channels(self):
+ """ Check to see if we have any active channels. If we don't kill the reactor.
+ """
+ def __parse_output(result):
+ first_line = result.output.split('\n', 1)[0]
+ first_number = first_line.split(' ', 1)[0]
+ if first_number == '0':
+ logger.debug("No channels detected; stopping reactor")
+ self.stop_reactor()
+
+ for asterisk in self.ast:
+ cli_deferred = asterisk.cli_exec('core show channels count')
+ cli_deferred.addCallback(__parse_output)
def run(self):
"""
Modified: asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/TestCase.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/TestCase.py?view=diff&rev=3137&r1=3136&r2=3137
==============================================================================
--- asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/TestCase.py (original)
+++ asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/TestCase.py Fri Mar 23 14:51:04 2012
@@ -13,7 +13,7 @@
import os
import datetime
import time
-from twisted.internet import reactor
+from twisted.internet import reactor, defer
from starpy import manager, fastagi
from asterisk import Asterisk
@@ -59,6 +59,7 @@
self.testStateController = None
self.pcap = None
self.pcapfilename = None
+ self.__stopping = False
self.testlogdir = os.path.join(Asterisk.test_suite_root, self.base, str(os.getpid()))
self.ast_version = AsteriskVersion()
@@ -84,7 +85,7 @@
self.__setup_conditions()
logger.info("Executing " + self.test_name)
- reactor.callWhenRunning(self.run)
+ reactor.callWhenRunning(self.__run)
def __setup_conditions(self):
"""
@@ -199,63 +200,120 @@
return PcapListener(device, bpf_filter, dumpfile, self.__pcap_callback)
def start_asterisk(self):
+ """ This is kept mostly for legacy support. When the TestCase previously
+ used a synchronous, blocking mechanism independent of the twisted
+ reactor to spawn Asterisk, this method was called. Now the Asterisk
+ instances are started immediately after the reactor has started.
+
+ Derived methods can still implement this and have it be called when
+ the reactor is running, but immediately before instances of Asterisk
+ are launched.
+ """
+ pass
+
+ 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
- told to run.
- """
+ create_asterisk. Note that this should be the first thing called
+ when the reactor has started to run
+ """
+ def __check_success_failure(result):
+ """ Make sure the instances started properly """
+ for (success, value) in result:
+ if not success:
+ logger.error(value)
+ self.stop_reactor()
+ return result
+
+ def __perform_pre_checks(result):
+ """ Execute the pre-condition checks """
+ self.testConditionController.evaluate_pre_checks()
+ return result
+
+ def __run_callback(result):
+ """ Notify the test that we are running """
+ self.run()
+ return result
+
+ # Call the method that derived objects can override
+ self.start_asterisk()
+
+ # Gather up the deferred objects from each of the instances of Asterisk
+ # and wait until all are finished before proceeding
+ start_defers = []
for index, item in enumerate(self.ast):
logger.info("Starting Asterisk instance %d" % (index + 1))
- self.ast[index].start()
- self.testConditionController.evaluate_pre_checks()
+ temp_defer = self.ast[index].start()
+ start_defers.append(temp_defer)
+
+ d = defer.DeferredList(start_defers, consumeErrors=True)
+ d.addCallback(__check_success_failure)
+ d.addCallback(__perform_pre_checks)
+ d.addCallback(__run_callback)
def stop_asterisk(self):
"""
- Stop the instances of Asterisk that were previously started. See
- start_asterisk. Note that this should be called after the reactor has
- returned from its run.
-
- If there were errors exiting asterisk, this function will return False.
- """
- res = True
+ Called when the instances of Asterisk are being stopped. Note that previously,
+ this explicitly stopped the Asterisk instances: now they are stopped automatically
+ when the reactor is stopped.
+
+ Derived methods can still implement this and have it be called when
+ the reactor is running, but immediately before instances of Asterisk
+ are stopped.
+ """
+ pass
+
+ def __stop_asterisk(self):
+ """ Stops the instances of Asterisk.
+
+ Stops the instances of Asterisk - called when stop_reactor is called. This
+ returns a deferred object that can be used to be notified when all instances
+ of Asterisk have stopped.
+ """
+ def __check_success_failure(result):
+ """ Make sure the instances stopped properly """
+ for (success, value) in result:
+ if not success:
+ logger.warning(value)
+ # This should already be called when the reactor is being terminated.
+ # If we couldn't stop the instance of Asterisk, there isn't much else to do
+ # here other then complain
+ return result
+
+ # Call the overridable method
+ self.stop_asterisk()
+
self.testConditionController.evaluate_post_checks()
+
+ # Gather up the stopped defers; check success failure of stopping when
+ # all instances of Asterisk have stopped
+ stop_defers = []
for index, item in enumerate(self.ast):
logger.info("Stopping Asterisk instance %d" % (index + 1))
- returncode = self.ast[index].stop()
- if returncode < 0:
- # XXX setting passed here might be overridden later in a
- # derived class. This is bad.
- self.passed = False
- logger.error("Asterisk instance %d exited with signal %d" % (index + 1, abs(returncode)))
- res = False
- elif returncode > 0:
- # XXX same here
- self.passed = False
- logger.error("Asterisk instance %d exited with non-zero return code %d" % (index + 1, returncode))
- res = False
-
- return res
-
- def no_active_channels(self):
- """
- Return true if all our asterisk children have 0 active channels.
- """
- for asterisk in self.ast:
- # 0 active channels
- first_line = asterisk.cli_exec('core show channels count').split('\n', 1)[0]
- # 0
- first_number = first_line.split(' ', 1)[0]
- if first_number != '0':
- return False
- return True
+ temp_defer = self.ast[index].stop()
+ stop_defers.append(temp_defer)
+
+ d = defer.DeferredList(stop_defers, consumeErrors=True)
+ d.addCallback(__check_success_failure)
+ return d
def stop_reactor(self):
"""
Stop the reactor and cancel the test.
"""
- logger.info("Stopping Reactor")
- if reactor.running:
- reactor.stop()
+ def __stop_reactor(result):
+ """ Called when the Asterisk instances are stopped """
+ logger.info("Stopping Reactor")
+ if reactor.running:
+ try:
+ reactor.stop()
+ except twisted.internet.error.ReactorNotRunning:
+ # Something stopped it between our checks - at least we're stopped
+ pass
+ if not self.__stopping:
+ df = self.__stop_asterisk()
+ df.addCallback(__stop_reactor)
+ self.__stopping = True
def __reactor_timeout(self):
"""
@@ -264,6 +322,18 @@
"""
logger.warning("Reactor timeout: '%s' seconds" % self.reactor_timeout)
self.stop_reactor()
+
+ def __run(self):
+ """
+ Private entry point called when the reactor first starts up.
+ This needs to first ensure that Asterisk is fully up and running before
+ moving on.
+ """
+ if (self.ast):
+ self.__start_asterisk()
+ else:
+ # If no instances of Asterisk are needed, go ahead and just run
+ self.run()
def run(self):
"""
@@ -305,7 +375,6 @@
pass
def __pcap_callback(self, packet):
- logger.debug("Received packet: %s\n" % (packet,))
self.pcap_callback(packet)
def handleOriginateFailure(self, reason):
Modified: asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/TestConfig.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/TestConfig.py?view=diff&rev=3137&r1=3136&r2=3137
==============================================================================
--- asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/TestConfig.py (original)
+++ asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/TestConfig.py Fri Mar 23 14:51:04 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/mmichelson/phone-testsuite/lib/python/asterisk/asterisk.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/asterisk.py?view=diff&rev=3137&r1=3136&r2=3137
==============================================================================
--- asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/asterisk.py (original)
+++ asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/asterisk.py Fri Mar 23 14:51:04 2012
@@ -13,17 +13,130 @@
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, defer, utils, error
+
logger = logging.getLogger(__name__)
+
+class AsteriskCliCommand():
+ """
+ Class that manages an Asterisk CLI command.
+ """
+
+ def __init__(self, host, cmd):
+ """ Create a new Asterisk CLI Protocol instance
+
+ This class wraps an Asterisk instance that executes a CLI command
+ against another instance of Asterisk.
+
+ Keyword Arguments:
+ host The host this CLI instance will connect to
+ cmd List of command arguments to spawn. The first argument must be
+ the location of the Asterisk executable; each subsequent argument should define
+ the CLI command to run and the instance of Asterisk to run it against.
+ """
+ self.__host = host
+ self.__cmd = cmd
+ self.exitcode = -1
+ self.output = ""
+ self.err = ""
+
+ def execute(self):
+ """ Execute the CLI command.
+
+ Returns a deferred that will be called when the operation completes. The
+ parameter to the deferred is this object.
+ """
+ def __cli_output_callback(result):
+ """ Callback from getProcessOutputAndValue """
+ self.__set_properties(result)
+ logger.debug("Asterisk CLI %s exited %d" % (self.__host, self.exitcode))
+ if self.exitcode:
+ self.__deferred.errback(self)
+ else:
+ self.__deferred.callback(self)
+
+ def __cli_error_callback(result):
+ """ Errback from getProcessOutputAndValue """
+ self.__set_properties(result)
+ logger.warning("Asterisk CLI %s exited %d with error: %s" % (self.__host, self.exitcode, self.err))
+ self.__deferred.errback(self)
+
+ self.__deferred = defer.Deferred()
+ df = utils.getProcessOutputAndValue(self.__cmd[0], self.__cmd)
+ df.addCallbacks(__cli_output_callback, __cli_error_callback)
+
+ return self.__deferred
+
+ def __set_properties(self, result):
+ """ Set the properties based on the result of the getProcessOutputAndValue call """
+ out, err, code = result
+ self.exitcode = code
+ self.output = out
+ self.err = err
+
+class AsteriskProtocol(protocol.ProcessProtocol):
+ """
+ Class that manages an Asterisk instance
+ """
+
+ def __init__(self, host, stop_deferred):
+ """ Create an AsteriskProtocol object
+
+ Create an AsteriskProtocol object, which manages the interactions with
+ the Asterisk process
+
+ Keyword Arguments:
+ host - the hostname or address of the Asterisk instance
+ stop_deferred - a twisted Deferred object that will be called when the
+ process has exited
+ """
+
+ self.output = ""
+ self.__host = host
+ self.exitcode = 0
+ self.exited = False
+ self.__stop_deferred = stop_deferred
+
+ def outReceived(self, data):
+ """ Override of ProcessProtocol.outReceived """
+ logger.debug("Asterisk %s received: %s" % (self.__host, data))
+ self.output += data
+
+ def connectionMade(self):
+ """ Override of ProcessProtocol.connectionMade """
+ logger.debug("Asterisk %s - connection made" % (self.__host))
+
+ def errReceived(self, data):
+ """ Override of ProcessProtocol.errReceived """
+ logger.warn("Asterisk %s received error: %s" % (self.__host, data))
+
+ def processEnded(self, reason):
+ """ Override of ProcessProtocol.processEnded """
+ message = ""
+ if reason.value and reason.value.exitCode:
+ message = "Asterisk %s ended with code %d" % (self.__host, reason.value.exitCode,)
+ self.exitcode = reason.value.exitCode
+ else:
+ message = "Asterisk %s ended " % self.__host
+ try:
+ # When Asterisk gets itself terminated with a KILL signal, this may (or may not)
+ # ever get called, in which case the Asterisk object itself that is terminating
+ # this process will attempt to raise the stop deferred. Prevent calling the
+ # object twice.
+ if not self.__stop_deferred.called:
+ self.__stop_deferred.callback(message)
+ except defer.AlreadyCalledError:
+ logger.warning("Asterisk %s stop deferred already called" % self.__host)
+ self.exited = True
class Asterisk:
"""An instance of Asterisk.
@@ -68,7 +181,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
@@ -105,9 +218,10 @@
self.directories[var] = val
def start(self):
- """Start this instance of Asterisk.
-
- This function starts up this instance of Asterisk.
+ """ Start this instance of Asterisk.
+
+ Returns:
+ A deferred object that will be called when Asterisk is fully booted.
Example Usage:
asterisk.start()
@@ -115,6 +229,21 @@
Note that calling this will install the default testsuite
config files, if they have not already been installed
"""
+
+ def __wait_fully_booted_callback(cli_command):
+ """ Callback for CLI command waitfullybooted """
+ self.__start_deferred.callback("Successfully started Asterisk %s" % self.host)
+
+ def __wait_fully_booted_error(cli_command):
+ """ Errback for CLI command waitfullybooted """
+ if time.time() - self.__start_asterisk_time > 5:
+ logger.error("Asterisk core waitfullybooted for %s failed" % self.host)
+ self.__start_deferred.errback("Command core waitfullybooted failed")
+ else:
+ logger.debug("Asterisk core waitfullybooted failed, attempting again...")
+ cli_deferred = self.cli_exec("core waitfullybooted")
+ cli_deferred.addCallbacks(__wait_fully_booted_callback, __wait_fully_booted_error)
+
self.install_configs(os.getcwd() + "/configs")
self.__setup_configs()
@@ -123,85 +252,143 @@
"-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
-
- # 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:
- """ Rut roh, Asterisk process exited prematurely """
- logger.error("Asterisk instance %s exited prematurely with return code %d" % (self.host, self.process.returncode))
-
- start = time.time()
- while True:
- # 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
+
+ # Make the start/stop deferreds - this method will return
+ # the start deferred, and pass the stop deferred to the AsteriskProtocol
+ # object. The stop deferred will be raised when the Asterisk process
+ # exits
+ self.__start_deferred = defer.Deferred()
+ self.__stop_deferred = defer.Deferred()
+ self.processProtocol = AsteriskProtocol(self.host, self.__stop_deferred)
+ self.process = reactor.spawnProcess(self.processProtocol, cmd[0], cmd)
+
+ # Begin the wait fully booted cycle
+ self.__start_asterisk_time = time.time()
+ cli_deferred = self.cli_exec("core waitfullybooted")
+ cli_deferred.addCallbacks(__wait_fully_booted_callback, __wait_fully_booted_error)
+ return self.__start_deferred
def stop(self):
"""Stop this instance of Asterisk.
This function is used to stop this instance of Asterisk.
+ Returns:
+ A deferred that can be used to detect when Asterisk exits,
+ or if it fails to exit.
+
Example Usage:
asterisk.stop()
"""
+
+ def __send_stop_gracefully():
+ """ Send a core stop gracefully CLI command """
+ if self.ast_version < AsteriskVersion("1.6.0"):
+ cli_deferred = self.cli_exec("stop gracefully")
+ else:
+ cli_deferred = self.cli_exec("core stop gracefully")
+ cli_deferred.addCallbacks(__stop_gracefully_callback, __stop_gracefully_error)
+
+ def __stop_gracefully_callback(cli_command):
+ """ Callback handler for the core stop gracefully CLI command """
+ logger.debug("Successfully stopped Asterisk %s" % self.host)
+ self.__stop_attempts = 0
+
+ def __stop_gracefully_error(cli_command):
+ """ Errback for the core stop gracefully CLI command """
+ if self.__stop_attempts > 5:
+ self.__stop_attempts = 0
+ logger.warning("Asterisk graceful stop for %s failed" % self.host)
+ else:
+ logger.debug("Asterisk graceful stop failed, attempting again...")
+ self.__stop_attempts += 1
+ __send_stop_gracefully()
+
+ def __send_stop_now():
+ """ Send a core stop now CLI command """
+ if self.ast_version < AsteriskVersion("1.6.0"):
+ cli_deferred = self.cli_exec("stop now")
+ else:
+ cli_deferred = self.cli_exec("core stop now")
+ if cli_deferred:
+ cli_deferred.addCallbacks(__stop_now_callback, __stop_now_error)
+
+ def __stop_now_callback(cli_command):
+ """ Callback handler for the core stop now CLI command """
+ logger.debug("Successfully stopped Asterisk %s" % self.host)
+ self.__stop_attempts = 0
+
+ def __stop_now_error(cli_command):
+ """ Errback handler for the core stop now CLI command """
+ if self.__stop_attempts > 5:
+ self.__stop_attempts = 0
+ logger.warning("Asterisk graceful stop for %s failed" % self.host)
+ else:
+ logger.debug("Asterisk stop now failed, attempting again...")
+ self.__stop_attempts += 1
+ cli_deferred = __send_stop_now()
+ if cli_deferred:
+ cli_deferred.addCallbacks(__stop_now_callback, __stop_now_error)
+
+ def __send_term():
+ """ Send a TERM signal to the Asterisk instance """
+ try:
+ logger.info("Sending TERM to Asterisk %s" % self.host)
+ self.process.signalProcess("TERM")
+ except error.ProcessExitedAlready:
+ # Probably that we sent a signal to a process that was already
+ # dead. Just ignore it.
+ pass
+
+ def __send_kill():
+ """ Check to see if the process is running and kill it with fire """
+ try:
+ if not self.processProtocol.exited:
+ logger.info("Sending KILL to Asterisk %s" % self.host)
+ self.process.signalProcess("KILL")
+ except error.ProcessExitedAlready:
+ # Pass on this
+ pass
+ # If you kill the process, the ProcessProtocol may never get the note
+ # that its dead. Call the stop callback to notify everyone that we did
+ # indeed kill the Asterisk instance.
+ try:
+ # Attempt to signal the process object that it should lose its
+ # connection - it may already be gone however.
+ self.process.loseConnection()
+ except:
+ pass
+ try:
+ if not self.__stop_deferred.called:
+ self.__stop_deferred.callback("Asterisk %s KILLED" % self.host)
+ except defer.AlreadyCalledError:
+ logger.warning("Asterisk %s stop deferred already called" % self.host)
+
+ def __cancel_stops(reason):
+ """ Cancel all stop actions - called when the process exits """
+ for token in self.__stop_cancel_tokens:
+ try:
+ if token.active():
+ token.cancel()
+ except error.AlreadyCalled:
+ # If we're canceling something that's already been called, move on
+ pass
+ return reason
+
+ self.__stop_cancel_tokens = []
+ self.__stop_attempts = 0
# Start by asking to stop gracefully.
- if self.ast_version < AsteriskVersion("1.6.0"):
- self.cli_exec("stop gracefully")
- else:
- self.cli_exec("core stop gracefully")
- for i in xrange(5):
- time.sleep(1.0)
- if self.process.poll() is not None:
- 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.
- if self.ast_version < AsteriskVersion("1.6.0"):
- self.cli_exec("stop now")
- else:
- 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 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)
- 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:
- # 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
+ __send_stop_gracefully()
+
+ # Schedule progressively more aggressive mechanisms of stopping Asterisk. If any
+ # stop mechanism succeeds, all are canceled
+ self.__stop_cancel_tokens.append(reactor.callLater(5, __send_stop_now))
+ self.__stop_cancel_tokens.append(reactor.callLater(10, __send_term))
+ self.__stop_cancel_tokens.append(reactor.callLater(15, __send_kill))
+
+ self.__stop_deferred.addCallback(__cancel_stops)
+
+ return self.__stop_deferred
def install_configs(self, cfg_path):
"""Installs all files located in the configuration directory for this
@@ -320,8 +507,14 @@
used. If no extension is given, the 's' extension will be used.
Keyword Arguments:
- blocking -- When True, do not return from this function until the CLI
- command finishes running. The default is True.
+ blocking -- This used to specify that we should block until the
+ CLI command finished executing. When the Asterisk process was turned
+ over to twisted, that's no longer the case. The keyword argument
+ was kept merely for backwards compliance; callers should *not* expect
+ their calls to block.
+
+ Returns:
+ A deferred object that can be used to listen for command completion
Example Usage:
asterisk.originate("Local/a_exten at context extension b_exten at context")
@@ -345,17 +538,23 @@
<tech/data> extension <exten>@<context>"
if self.ast_version < AsteriskVersion("1.6.2"):
- self.cli_exec("originate %s" % argstr, blocking=blocking)
+ return self.cli_exec("originate %s" % argstr, blocking=blocking)
else:
- self.cli_exec("channel originate %s" % argstr, blocking=blocking)
+ return self.cli_exec("channel originate %s" % argstr, blocking=blocking)
def cli_exec(self, cli_cmd, blocking=True, warn_on_fail=True):
"""Execute a CLI command on this instance of Asterisk.
Keyword Arguments:
cli_cmd -- The command to execute.
- blocking -- When True, do not return from this function until the CLI
- command finishes running. The default is True.
+ blocking -- This used to specify that we should block until the
+ CLI command finished executing. When the Asterisk process was turned
+ over to twisted, that's no longer the case. The keyword argument
+ was kept merely for backwards compliance; callers should *not* expect
+ their calls to block.
+
+ Returns:
+ A deferred object that will be signaled when the process has exited
Example Usage:
asterisk.cli_exec("core set verbose 10")
@@ -370,32 +569,8 @@
]
logger.debug("Executing %s ..." % cmd)
- if not blocking:
- process = subprocess.Popen(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)
+ return cliProtocol.execute()
def __make_directory_structure(self):
""" Mirror system directory structure """
Modified: asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/cdr.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/cdr.py?view=diff&rev=3137&r1=3136&r2=3137
==============================================================================
--- asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/cdr.py (original)
+++ asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/cdr.py Fri Mar 23 14:51:04 2012
@@ -15,6 +15,7 @@
import csv
import re
import logging
+import time
logger = logging.getLogger(__name__)
@@ -86,13 +87,17 @@
return
self.__records = []
+
+ cdr = None
try:
cdr = csv.DictReader(open(fn, "r"), AsteriskCSVCDRLine.get_fields(), ",")
- except IOError:
- logger.error("Failed to open CDR file '%s'" % (fn))
- return
+ except IOError as (errno, strerror):
+ logger.debug("IOError %d[%s] while opening CDR file '%s'" % (errno, strerror, fn))
except:
- logger.error("Unexpected error: %s" % (sys.exc_info()[0]))
+ logger.debug("Unexpected error: %s" % (sys.exc_info()[0]))
+
+ if not cdr:
+ logger.error("Unable to open CDR file '%s'" % (fn))
return
for r in cdr:
Modified: asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/sipp.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/sipp.py?view=diff&rev=3137&r1=3136&r2=3137
==============================================================================
--- asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/sipp.py (original)
+++ asterisk/team/mmichelson/phone-testsuite/lib/python/asterisk/sipp.py Fri Mar 23 14:51:04 2012
@@ -14,13 +14,77 @@
import os
import subprocess
import logging
-
-from twisted.internet import reactor
+import TestSuiteUtils
+
[... 2693 lines stripped ...]
More information about the asterisk-commits
mailing list