[asterisk-scf-commits] asterisk-scf/integration/testsuite.git branch "review" updated.

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Wed Aug 24 13:51:06 CDT 2011


branch "review" has been updated
       via  a0818e07f695a8a462b1d66781e0fa2e1a7ace12 (commit)
       via  12fee4ed8e3c610952f37296d37779e4e1e2dc1e (commit)
       via  8231474b356c60e8daa094057e7b61129f39b582 (commit)
       via  138c575ea692dce41c563419dc7701071cef53f7 (commit)
       via  2e62905c0a52c979d5bc0a03b5a216a592b2413c (commit)
       via  35081f84ada6170b4c835a51c949eeb062e73917 (commit)
       via  07dd69b645b0ce968e171a6eee49c0f430bebec9 (commit)
       via  2fbbfa5e9168aa06716d0f657be39381a55098eb (commit)
       via  06207dc172df600521f558354a2f21ad7ece82bd (commit)
       via  638bd152fe63a041cdceadda6a6bc4c5f6f609ee (commit)
       via  4c314b032931524d05b1af438621db1246ab17e4 (commit)
       via  8c115990119c33217d74ad697e3619be26d9353a (commit)
       via  97ffd9dfcb666c3b1ff99de34a2a36e9fd0faaed (commit)
       via  bb6af5d690115b4395d0946b739f934c83108174 (commit)
      from  489fbe913b6d79b1b5d24530289267d05f012268 (commit)

Summary of changes:
 RemoteAgent.py                                     |  130 +++
 testsuite.py => Server.py                          |  269 +++---
 configs/GlobalVars.py                              |   59 ++
 configs/plugins/failover_conf.yaml                 |   48 +
 configs/remote/remoteAgent.yaml.sample             |    1 +
 configs/server/remoteAgents.yaml                   |    4 +
 events/shutdown.yaml                               |   15 +-
 events/startup.yaml                                |   15 +-
 lib/python/FileSystem.py                           |  193 ++++
 lib/python/Info.py                                 |   90 ++
 lib/python/Logging.py                              |   55 ++
 lib/python/Misc.py                                 |  144 +++
 lib/python/Plugin.py                               |  101 +++
 lib/python/RPC.py                                  |  140 +++
 lib/python/SubProcess.py                           |  147 ++++
 lib/python/TestSuite.py                            |  208 -----
 lib/python/Yaml.py                                 |   99 +++
 lib/python/file.py                                 |   37 -
 lib/python/interactive.py                          |  185 ++++
 lib/python/junitxml.py                             |   14 +-
 lib/python/plugins.py                              |   70 --
 lib/python/yaml_parser.py                          |   84 --
 plugins/asteriskscf_configurator.py                |  129 ++--
 plugins/asteriskscf_icebox.py                      |  268 ++++---
 plugins/build.py                                   |  237 ++++--
 plugins/failover.py                                |    7 +-
 plugins/protos.py                                  |    6 +-
 plugins/sipp.py                                    |   98 +--
 plugins/siprtp.py                                  |  100 +++
 plugins/testsuite_remotes.py                       |  138 ++--
 plugins/wireshark.py                               |   90 ++-
 remote.py                                          |  411 ---------
 .../scenarios/call_then_attended_transfer.xml      |  180 ----
 .../scenarios/wait_for_call.xml                    |   76 --
 .../scenarios/wait_for_call_do_hangup.xml          |   83 --
 .../sip/Functional_Attended_Transfer/testcase.yaml |  924 --------------------
 .../scenarios/call_then_blind_transfer.xml         |  113 ---
 .../scenarios/wait_for_call.xml                    |   76 --
 .../scenarios/wait_for_call_do_hangup.xml          |   83 --
 .../sip/Functional_Blind_Transfer/testcase.yaml    |  924 --------------------
 .../Functional_Busy_Endpoint/scenarios/call.xml    |   58 --
 .../scenarios/wait_for_call_busy.xml               |   22 -
 .../sip/Functional_Busy_Endpoint/testcase.yaml     |  384 --------
 .../scenarios/call.xml                             |   58 --
 .../scenarios/wait_for_call_congestion.xml         |   22 -
 .../Functional_Congestion_Endpoint/testcase.yaml   |  384 --------
 .../sip/Functional_Simple_Endpoint/testcase.yaml   |  377 --------
 .../Functional_Timeout_Endpoint/scenarios/call.xml |   83 --
 .../scenarios/wait_for_call_timeout.xml            |   53 --
 .../sip/Functional_Timeout_Endpoint/testcase.yaml  |  384 --------
 .../scenarios/options.xml                          |   21 -
 .../scenarios/options2.xml                         |   21 -
 .../Functional_Verify_Options_Method/testcase.yaml |  170 ----
 tests/asterisk_scf_components/tests.yaml           |    6 -
 .../sip/Functional_RTCP_Simple/testcase.yaml       |   99 +++
 .../testcase.yaml                                  |   81 --
 .../testcase.yaml                                  |   28 -
 .../testcase.yaml                                  |  135 ---
 tests/build/pjproject/patches/siprtp.patch         |   92 ++
 tests/build/pjproject/patches/siprtp_report.patch  |  148 ++++
 tests/build/pjproject/testcase.yaml                |   27 +
 61 files changed, 2586 insertions(+), 6118 deletions(-)
 create mode 100755 RemoteAgent.py
 rename testsuite.py => Server.py (61%)
 create mode 100644 configs/GlobalVars.py
 create mode 100644 configs/plugins/failover_conf.yaml
 create mode 100644 configs/remote/remoteAgent.yaml.sample
 create mode 100644 configs/server/remoteAgents.yaml
 create mode 100644 lib/python/FileSystem.py
 create mode 100644 lib/python/Info.py
 create mode 100644 lib/python/Logging.py
 create mode 100644 lib/python/Misc.py
 create mode 100644 lib/python/Plugin.py
 create mode 100644 lib/python/RPC.py
 create mode 100644 lib/python/SubProcess.py
 delete mode 100644 lib/python/TestSuite.py
 create mode 100755 lib/python/Yaml.py
 delete mode 100644 lib/python/file.py
 create mode 100644 lib/python/interactive.py
 delete mode 100644 lib/python/plugins.py
 delete mode 100755 lib/python/yaml_parser.py
 create mode 100644 plugins/siprtp.py
 delete mode 100755 remote.py
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Attended_Transfer/scenarios/call_then_attended_transfer.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Attended_Transfer/scenarios/wait_for_call.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Attended_Transfer/scenarios/wait_for_call_do_hangup.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Attended_Transfer/testcase.yaml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Blind_Transfer/scenarios/call_then_blind_transfer.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Blind_Transfer/scenarios/wait_for_call.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Blind_Transfer/scenarios/wait_for_call_do_hangup.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Blind_Transfer/testcase.yaml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Busy_Endpoint/scenarios/call.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Busy_Endpoint/scenarios/wait_for_call_busy.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Busy_Endpoint/testcase.yaml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Congestion_Endpoint/scenarios/call.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Congestion_Endpoint/scenarios/wait_for_call_congestion.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Congestion_Endpoint/testcase.yaml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Simple_Endpoint/testcase.yaml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Timeout_Endpoint/scenarios/call.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Timeout_Endpoint/scenarios/wait_for_call_timeout.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Timeout_Endpoint/testcase.yaml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Verify_Options_Method/scenarios/options.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Verify_Options_Method/scenarios/options2.xml
 delete mode 100644 tests/asterisk_scf_components/sip/Functional_Verify_Options_Method/testcase.yaml
 delete mode 100644 tests/asterisk_scf_components/tests.yaml
 create mode 100644 tests/asteriskscf/sip/Functional_RTCP_Simple/testcase.yaml
 delete mode 100644 tests/build/Build_and_Distribute_Asterisk_SCF_to_TestSuite_Remotes/testcase.yaml
 delete mode 100644 tests/build/Build_and_Distribute_SIPp_to_TestSuite_Remotes/testcase.yaml
 delete mode 100644 tests/build/Build_and_Distribute_the_Digium_TestSuite_to_Remotes/testcase.yaml
 create mode 100644 tests/build/pjproject/patches/siprtp.patch
 create mode 100644 tests/build/pjproject/patches/siprtp_report.patch
 create mode 100644 tests/build/pjproject/testcase.yaml


- Log -----------------------------------------------------------------
commit a0818e07f695a8a462b1d66781e0fa2e1a7ace12
Author: Darren Sessions <dsessions at digium.com>
Date:   Wed Aug 24 13:50:35 2011 -0500

    Finished up testing on the siprtp plugin. Finished testing the siprtp.c and siprtp_report.c patches for the siprtp test-suite plugin. Modified the subprocess library to return the console log file path which was required by siprtp in order to parse the console output to determine the tx and rx rtcp packet count. Updated the Asterisk SCF configurator plugin so that the ipv4bind and ipv6bind options were returning a proper ip address based on the parsed host data in the testcase yaml files. Updated the logging library to return the console log file for both the subprocess function and the pexpect function for use with console output parsing. Made a couple tweaks to the build plugin to correct an issue with redistributing binaries.

diff --git a/lib/python/Logging.py b/lib/python/Logging.py
index 693a585..c81272d 100644
--- a/lib/python/Logging.py
+++ b/lib/python/Logging.py
@@ -20,23 +20,19 @@ import sys
 
 class Artifacts:
     def consoleLogger(self, plugin, label):
-
-        print globalVars.testInfo
-
         baseDir = '%s/tmp/artifacts/%s' % (globalVars.cwd, globalVars.testInfo['testLogs'])
-
-        print BaseDir
+        logFilePath = '%s/console_logs/%s.%s_%s_console.log' % (baseDir, globalVars.hostInfo['hostname'], self.__artifactNameCheck(plugin), self.__artifactNameCheck(label))
 
         self.__artifactDirCheck()
 
         if not os.path.exists('%s' % baseDir):
             return self.resFailure('Artifact path does not exist on remote! %s' % baseDir)
         try:
-            logFile = open('%s/console_logs/%s.%s_%s_console.log' % (baseDir, globalVars.hostInfo['hostname'], self.__artifactNameCheck(plugin), self.__artifactNameCheck(label)), 'w')
+            logFileObject = open(logFilePath, 'w')
         except:
-            return self.resFailure('Unable to open \'%s/console_logs/%s.%s_%s_console.log\' for writing. %s' % (baseDir, globalVars.hostInfo['hostname'], self.__artifactNameCheck(plugin), self.__artifactNameCheck(label), sys.exc_info()[1]))
+            return self.resFailure('Unable to open \'%s\' for writing. %s' % (logFilePath, sys.exc_info()[1]))
 
-        return {'success':True,'logFile':logFile}
+        return {'success':True,'logFileObject':logFileObject, 'logFilePath':logFilePath}
 
     def __artifactNameCheck(self, name):
         name = name.replace(' ', '_')
diff --git a/lib/python/SubProcess.py b/lib/python/SubProcess.py
index e01f11e..3652337 100644
--- a/lib/python/SubProcess.py
+++ b/lib/python/SubProcess.py
@@ -50,19 +50,20 @@ class Exec(Misc.Utils, Logging.Artifacts):
         if res['success'] == False:
             return res
 
-        logFile = res['logFile']
+        logFilePath = res['logFilePath']
+        logFileObject = res['logFileObject']
         
-        time.sleep(1)
+        time.sleep(.5)
         if os.path.exists(execCmd[0]) and os.access(execCmd[0], os.X_OK):
-            p = subprocess.Popen(execCmd, bufsize=0, shell=False, stdout=logFile, stderr=logFile)
-            time.sleep(1)
+            p = subprocess.Popen(execCmd, bufsize=0, shell=False, stdout=logFileObject, stderr=logFileObject)
+            time.sleep(.5)
             p.poll()
             if wait == True:
                 p.wait()
             if not p.returncode == None and wait == False:
                 return self.resFailure('Could not execute \'%s\'.' % ' '.join(execCmd))
             elif not p.returncode == None and wait == True:
-                return {'success':True, 'returncode':p.returncode}
+                return {'success':True, 'returncode':p.returncode, 'logFilePath':logFilePath}
         else:
             return self.resFailure('FAILED TO EXECUTE \'%s\'! It must exist and be executable!' % ' '.join(execCmd))
 
@@ -70,9 +71,9 @@ class Exec(Misc.Utils, Logging.Artifacts):
             if not plugin in globalVars.processList:
                 globalVars.processList[plugin] = {}
             if not label in globalVars.processList[plugin]:
-                globalVars.processList[plugin][label] = {'exec':' '.join(execCmd), 'subprocess':p, 'logFile':logFile}
+                globalVars.processList[plugin][label] = {'exec':' '.join(execCmd), 'subprocess':p, 'logFileObject':logFileObject}
 
-        return self.resSuccess()
+        return {'success':True, 'logFilePath':logFilePath}
 
     def pExpect(self, hostName, globalVars, plugin, label, execCmd, expect, send, countOnIndex, errorOnIndex = 9999, timeout=None, terminate=True):
         if not type(execCmd) == list or not type(expect) == list or not type(send) == list:
@@ -84,7 +85,7 @@ class Exec(Misc.Utils, Logging.Artifacts):
         if res['success'] == False:
             return res
 
-        logFile = res['logFile']
+        logFileObject = res['logFileObject']
 
         count = 0
         loop = True
@@ -92,9 +93,9 @@ class Exec(Misc.Utils, Logging.Artifacts):
 
         try:
             p = pexpect.spawn(' '.join(execCmd))
-            p.logfile = logFile
+            p.logfile = logFileObject
         except:
-            logFile.close()
+            logFileObject.close()
             return {'success':False,'msg':'[pExpect] Unable to execute %s' % execCmd}
 
         while (loop):
@@ -104,13 +105,13 @@ class Exec(Misc.Utils, Logging.Artifacts):
                 else:
                      index = p.expect(expect)
             except:
-                logFile.close()
+                logFileObject.close()
                 return {'success':True} 
             if index == countOnIndex:
                 count = count + 1
             if index == errorOnIndex:
                 p.terminate()
-                logFile.close()
+                logFileObject.close()
                 return {'success':False, 'msg':'ErrorOnIndex %s matched.' % errorOnIndex, 'count':count}
             if len(send[index]) > 1:
                  if re.match('ctrl+', send[index]):
@@ -136,7 +137,7 @@ class Exec(Misc.Utils, Logging.Artifacts):
             except:
                 msg.append("Unable to kill pid for %s in %s." % (label, plugin))
 
-            globalVars.processList[plugin][label]['logFile'].close()
+            globalVars.processList[plugin][label]['logFileObject'].close()
 
         del globalVars.processList[plugin]
 
diff --git a/plugins/asteriskscf_configurator.py b/plugins/asteriskscf_configurator.py
index da88834..603cea9 100644
--- a/plugins/asteriskscf_configurator.py
+++ b/plugins/asteriskscf_configurator.py
@@ -462,11 +462,21 @@ class RtpSectionVisitors(SectionVisitors):
         workerItem = self.AsteriskSCF.Configuration.MediaRTPPJMedia.V1.WorkerThreadCountItem()
         mapper.map('workerthreadcount', workerItem, 'count', self.AsteriskSCF.Configuration.MediaRTPPJMedia.V1.WorkerThreadCountItemName, self.chkOption(config, 'workerthreadcount'), 4)
 
+        ipv4bind = ipv6bind = None
+        if not self.chkOption(config, 'ipv4bind') == None:
+            res = self.hostLabelResolver(config['ipv4bind'])
+            if res['success'] == True:
+                ipv4bind = res['ip']
+        if not self.chkOption(config, 'ipv6bind') == None:
+            res = self.hostLabelResolver(config['ipv6bind'])
+            if res['success'] == True:
+                ipv6bind = res['ip']
+                
         ipv4BindingItem = self.AsteriskSCF.Configuration.MediaRTPPJMedia.V1.BindingIPv4Item()
-        mapper.map('ipv4bind', ipv4BindingItem, 'address', self.AsteriskSCF.Configuration.MediaRTPPJMedia.V1.BindingIPv4AddressItemName, self.chkOption(config, 'ipv4bind'), None)
+        mapper.map('ipv4bind', ipv4BindingItem, 'address', self.AsteriskSCF.Configuration.MediaRTPPJMedia.V1.BindingIPv4AddressItemName, ipv4bind, None)
 
         ipv6BindingItem = self.AsteriskSCF.Configuration.MediaRTPPJMedia.V1.BindingIPv6Item()
-        mapper.map('ipv6bind', ipv6BindingItem, 'address', self.AsteriskSCF.Configuration.MediaRTPPJMedia.V1.BindingIPv6AddressItemName, self.chkOption(config, 'ipv6bind'), None)
+        mapper.map('ipv6bind', ipv6BindingItem, 'address', self.AsteriskSCF.Configuration.MediaRTPPJMedia.V1.BindingIPv6AddressItemName, ipv6bind, None)
  
         for option in config:
             mapper.execute(group, section, option)
diff --git a/plugins/asteriskscf_icebox.py b/plugins/asteriskscf_icebox.py
index af6920c..a67b536 100644
--- a/plugins/asteriskscf_icebox.py
+++ b/plugins/asteriskscf_icebox.py
@@ -40,8 +40,6 @@ class plugin(Plugin.BaseClass):
                     if res['success'] == False:
                         return res
 
-                    print res['ip']
-
                     configArgs['service_locator'] = ' -h %s' % res['ip']
                 else:
                     configArgs['service_locator'] = ''
diff --git a/plugins/build.py b/plugins/build.py
index ab6e748..a3e2061 100644
--- a/plugins/build.py
+++ b/plugins/build.py
@@ -90,11 +90,11 @@ class plugin(Plugin.BaseClass):
             if update == True and not testData['cmd']['dl_type'] == 'wget':
                 res = globalVars.rpcCmd[host]('changeDir', '!!TMP!!/%s' % testData['cmd']['cd'])
                 if res['success'] == True:
-                    res = globalVars.rpcCmd[host]('run', remote['hostname'], 'build', testData['cmd']['dl_url'], refreshCmd, True)
+                    res = globalVars.rpcCmd[host]('run', 'build', testData['cmd']['dl_url'], refreshCmd, True)
                     if res['success'] == False:
                         return res
                 else:
-                    res = globalVars.rpcCmd[host]('run', remote['hostname'], 'build', testData['cmd']['dl_url'], getCmd, True)
+                    res = globalVars.rpcCmd[host]('run', 'build', testData['cmd']['dl_url'], getCmd, True)
                     if res['success'] == False:
                         return res
             else:
diff --git a/plugins/siprtp.py b/plugins/siprtp.py
new file mode 100644
index 0000000..dfc53ba
--- /dev/null
+++ b/plugins/siprtp.py
@@ -0,0 +1,100 @@
+'''
+
+ PJProject PJSIP Sample Apps SIPRTP Plugin
+
+ Test-Suite - Copyright (C) 2011, Digium, Inc.
+ 
+ Written by Darren Sessions
+
+ See http://wiki.asterisk.org for more information.
+
+ This program is free software, distributed under the 
+ terms of the GNU General Public License Version 2.
+
+'''
+
+from GlobalVars import globalVars
+
+import time
+import yaml
+
+import Plugin
+
+class plugin(Plugin.BaseClass):
+    Plugin.BaseClass().cleanup('process', 'siprtp')
+
+    def main(self, testData):
+        siprtpLoc = {'Linux':'!!TMP!!/pjproject-1.10/pjsip-apps/bin/samples/x86_64-unknown-linux-gnu'}
+        procOrder = ['uas', 'uac']
+
+        if not 'cmd' in testData:
+            return self.resFailure('No command specified')
+         
+        if not 'uac' in testData['cmd'] and not 'uas' in testData['cmd']:
+            return self.resFailure('Unsupported mode (%s). Should be either \'uac\' or \'uas\'.')
+
+        for mode in procOrder:
+            if not mode in testData['cmd']:
+                continue
+
+            if not 'remote_agent' in testData['cmd'][mode]:
+                return self.resFailure('\'remote_agent\' missing from %s mode configuration' % mode)
+ 
+            if not testData['cmd'][mode]['remote_agent'] in globalVars.remoteInfo:
+                return self.resFailure('The remote agent specified, %s,  was not initialized during the test-suite startup.' % testData['cmd'][mode]['remote_agent'])
+
+            if not 'local_port' in testData['cmd'][mode]:
+                return self.resFailure('\'local_port\' missing from %s mode configuration' % mode)
+
+            if not 'call_count' in testData['cmd'][mode]:
+                return self.resFailure('\'call_count\' missing from %s mode configuration' % mode)
+
+            if not 'listen_on' in testData['cmd'][mode]:
+                return self.resFailure('\'listen_on\' missing from %s mode configuration' % mode)
+
+            res = self.hostLabelResolver(testData['cmd'][mode]['listen_on'])
+            if res['success'] == False:
+                return res
+ 
+            execCmd = [
+                '--ip-addr=%s' % res['ip'], 
+                '--local-port=%s' % testData['cmd'][mode]['local_port'],
+                '--count=%s' % testData['cmd'][mode]['call_count']]
+
+            if mode == 'uas':
+                waitForPidToFinish = False
+                if not 'timeout' in testData['cmd'][mode]:
+                    return self.resFailure('\'timeout\' missing from %s mode configuration' % mode)
+                execCmd = execCmd + ['--duration=%s' % testData['cmd'][mode]['timeout']]
+
+            if mode == 'uac':
+                waitForPidToFinish = True
+                if not 'targethost' in testData['cmd'][mode]:
+                    return self.resFailure('\'targethost\' missing from %s mode configuration' % mode)
+                res = self.hostLabelResolver(testData['cmd'][mode]['targethost'])
+                if res['success'] == False:
+                    return res
+                execCmd = execCmd + ['--app-log-level=1', '--call-report', '--auto-quit', 'sip:service@%s' % res['ip']]
+
+            execCmd = ['%s/siprtp' % siprtpLoc[globalVars.remoteInfo[testData['cmd'][mode]['remote_agent']]['hostInfo']['plat']]] + execCmd
+
+            res = globalVars.rpcCmd[testData['cmd'][mode]['remote_agent']]('run', 'siprtp', 'mode', execCmd, waitForPidToFinish)
+            if res['success'] == False:
+                return res
+     
+            if mode == 'uac':   
+                res = globalVars.rpcCmd[testData['cmd'][mode]['remote_agent']]('readFile', res['logFilePath'], False, False, False)                
+                if res['success'] == False:
+                    return res
+
+                res = res['data']
+                res = res.split('\n')
+                res.pop(0)
+                res.pop(len(res) - 1)
+                res = yaml.load('\n'.join(res))
+
+                for call in res:
+                    if not int(res[call]['TX']['pkts']) > 0 and not int(res[call]['RX']['pkts']) > 0:
+                        return self.resFailure('One or more call failed. RTCP TX = %s. RTCP RX = %s.' % (res[call]['TX']['pkts'], res[call]['RX']['pkts']))
+
+        return self.resSuccess()
diff --git a/plugins/testsuite_remotes.py b/plugins/testsuite_remotes.py
index 0adf0c3..a153472 100644
--- a/plugins/testsuite_remotes.py
+++ b/plugins/testsuite_remotes.py
@@ -25,23 +25,6 @@ class plugin(Plugin.BaseClass):
         rpc = {}
         ''' start and stop individual components. shutdown is implemented in the main testsuite code ''' 
         if testData['cmd'] == 'startup':
-            ''' Create inital remote RPC server proxies '''
-            for host in globalVars.remoteAgents:
-                res = self.rpcConnect([host, 'rpc', 'ipv4'])
-                if res['success'] == False:
-                    return res
-
-            res = self.rpcBatchCmd(globalVars.remoteAgents, ('restart',))
-            if res['success'] == False:
-                return res
-
-            time.sleep(1)
-
-            for host in globalVars.remoteAgents:
-                res = self.rpcConnect([host, 'rpc', 'ipv4'])
-                if res['success'] == False:
-                    return res
-
             res = self.rpcBatchCmd(globalVars.remoteAgents, ('makeDir', '!!CWD!!/tmp'))
     
             res = self.rpcBatchCmd(globalVars.remoteAgents, ('removeDir', '!!TMP!!/artifacts'))
@@ -80,6 +63,11 @@ class plugin(Plugin.BaseClass):
             for files in globalVars.cleanup['tempFile']:
                 res = self.rpcBatchCmd(globalVars.remoteAgents, ('removeFile', '!!TMP!!/%s' % files))
 
+            for process in globalVars.cleanup['process']:
+                res = self.rpcBatchCmd(globalVars.remoteAgents, ('run', 'startup', 'pkill', ['pkill', '-9', process], True))
+                if res['success'] == False:
+                    return res
+
             return {'success':True}
         else:
             return {'success':False,'msg':'The %s command is invalid.' % testData['cmd']}
diff --git a/plugins/wireshark.py b/plugins/wireshark.py
index 432febb..1d08bf4 100644
--- a/plugins/wireshark.py
+++ b/plugins/wireshark.py
@@ -76,14 +76,13 @@ class plugin(Plugin.BaseClass):
                     '/tmp/capture.pcap']
        
                 res = globalVars.rpcCmd[testData['remote_agent']]('run', 'wireshark', 'start', runCmd) 
-                print 'wtf = %s' % res
                 if res['success'] == False:
                     return res
 
                 return res
 
             elif cmd == 'stop':
-                res = globalVars.rpcCmd[testData['remote_agent']]('moveFile', '/tmp/capture.pcap', '!!TMP!!/artifacts/%s/%s.capture.pcap' % (globalVars.testInfo['testPath'], globalVars.remoteInfo[testData['remote_agent']]['hostInfo']['hostname']))
+                res = globalVars.rpcCmd[testData['remote_agent']]('moveFile', '/tmp/capture.pcap', '!!TMP!!/artifacts/%s/%s.capture.pcap' % (globalVars.testInfo['testLogs'], globalVars.remoteInfo[testData['remote_agent']]['hostInfo']['hostname']))
                 if res['success'] == False:
                     return res
 
@@ -92,7 +91,7 @@ class plugin(Plugin.BaseClass):
                         if testData['cmd']['stop']['call_flow_graph'] == False:
                             return res
 
-                res = globalVars.rpcCmd[testData['remote_agent']]('changeDir', '!!TMP!!/artifacts/%s' % globalVars.testInfo['testPath'])
+                res = globalVars.rpcCmd[testData['remote_agent']]('changeDir', '!!TMP!!/artifacts/%s' % globalVars.testInfo['testLogs'])
                 if res['success'] == False:
                     return res
 
@@ -100,7 +99,7 @@ class plugin(Plugin.BaseClass):
                 if res['success'] == False:
                     return res
 
-                callflowDir = '!!TMP!!/artifacts/%s/%s.capture' % (globalVars.testInfo['testPath'], globalVars.remoteInfo[testData['remote_agent']]['hostInfo']['hostname'])
+                callflowDir = '!!TMP!!/artifacts/%s/%s.capture' % (globalVars.testInfo['testLogs'], globalVars.remoteInfo[testData['remote_agent']]['hostInfo']['hostname'])
 
                 res = globalVars.rpcCmd[testData['remote_agent']]('moveFile', '%s/index.html' % callflowDir, '%s/index_standard.html' % callflowDir)
                 if res['success'] == False:
diff --git a/tests/asteriskscf/sip/Functional_RTCP_Simple/testcase.yaml b/tests/asteriskscf/sip/Functional_RTCP_Simple/testcase.yaml
new file mode 100644
index 0000000..2bca58b
--- /dev/null
+++ b/tests/asteriskscf/sip/Functional_RTCP_Simple/testcase.yaml
@@ -0,0 +1,99 @@
+name : Functional_Simple_RTCP
+docs :
+    template : generic
+    category : functional
+    summary : Test simple call setup using a SIPp UAC <> Asterisk SCF <> SIPp UAS scenario.
+    description : 'This test starts Asterisk SCF on a test-suite remote agent and uses the configurator to push a configuration for two endpoints to the Asterisk SCF SIP Session Gateway on that remote.{br}{br}Two SIPp processes are then started on separate test-suite remote agents. One test-suite remote agent uses the SIPp embedded UAC scenario, and the other uses the SIPp embedded UAS scenario.{br}{br}A call is sent from the SIPp UAC to Asterisk SCF with the expectation that it will forward the call to the second SIPp process running as a UAS.{br}{br}This test case is comprised of several sub-tests that include various transport configurations including: ipv4 <> ipv4, ipv4 <> ipv6, ipv6 <> ipv4, and ipv6 <> ipv6.'
+    repo_url : 'http://git.asterisk.org/gitweb/?p=asterisk-scf/release/testsuite.git;a=tree;f=bamboo;hb=HEAD'
+    components : 'Service Locator, Bridge, Routing, Sip Session Gateway, Media_RTP_PJMedia, and Media Format Generic'
+    requirements : 'Asterisk SCF, SIPp, Wireshark (optional), CallFlow (optional)'
+tests :
+    - 'ipv4toipv4toipv4-linux' :
+        expected_failure : False 
+        timeline:
+            - wireshark :
+                remote_agent : testsuite-remote-1.digium.internal
+                cmd :
+                    start:
+                        listen_on : public1
+                        host_filter :
+                            - [testsuite-remote-1.digium.internal, public1]
+                            - [testsuite-remote-2.digium.internal, public1]
+                            - [testsuite-remote-3.digium.internal, public1]
+                        protocol_filter :
+                            - sip
+                            - rtp
+                            - icmp
+                            - t38
+                            - dns
+            - asteriskscf_icebox :
+                remote_agent : testsuite-remote-1.digium.internal
+                cmd :
+                    start :
+                        components :
+                            - service_locator
+                            - bridge
+                            - routing
+                            - sip_session_gateway
+                            - media_rtp_pjmedia
+                            - media_format_generic
+                        mode : standalone
+                        service_locator : [testsuite-remote-1.digium.internal, private, ipv4]
+            - asteriskscf_configurator :
+                service_locator_host : [testsuite-remote-1.digium.internal, private, ipv4]
+                configuration_wipe : False
+                cmd :
+                    sip :
+                        - listen_udp4 :
+                            type : transport_udp
+                            host : [testsuite-remote-1.digium.internal, public1, ipv4]
+                            port : 5060
+                            ipv4oripv6 : ipv4
+                        - service :
+                            type : endpoint
+                            targethost : [testsuite-remote-2.digium.internal, public1, ipv4]
+                            targetport : 5060
+                            sourcehost : [testsuite-remote-1.digium.internal, public1, ipv4]
+                            sourceport : 5060
+                            direction : both
+                            securetransport : none
+                            rtpoveripv6 : False
+                            formats : 'ulaw/8000'
+                        - siprtpuac :
+                            type : endpoint
+                            targethost : [testsuite-remote-3.digium.internal, public1, ipv4]
+                            targetport : 5060
+                            sourcehost : [testsuite-remote-1.digium.internal, public1, ipv4]
+                            sourceport : 5060
+                            direction : both
+                            securetransport : none
+                            rtpoveripv6 : False
+                            formats : 'ulaw/8000'
+                    rtp :
+                         - general :
+                            ipv4bind : [testsuite-remote-1.digium.internal, public1, ipv4]
+                            startport : 10001
+                            endport : 20000
+                            workerthreadcount : 4
+            - siprtp :
+                cmd :
+                    uas :
+                        remote_agent : testsuite-remote-2.digium.internal
+                        listen_on : [testsuite-remote-2.digium.internal, public1, ipv4]
+                        local_port : 5060
+                        call_count : 1
+                        timeout : 5
+            - siprtp :
+                cmd :
+                    uac :
+                        remote_agent : testsuite-remote-3.digium.internal
+                        listen_on : [testsuite-remote-3.digium.internal, public1, ipv4]
+                        local_port : 5060
+                        targethost : [testsuite-remote-1.digium.internal, public1, ipv4]
+                        call_count : 1
+                        timeout : 5
+            - wireshark :
+                run_on_test_failure : True
+                remote_agent : testsuite-remote-1.digium.internal
+                cmd :
+                    stop :
diff --git a/tests/build/pjproject/patches/siprtp.patch b/tests/build/pjproject/patches/siprtp.patch
new file mode 100644
index 0000000..afa6643
--- /dev/null
+++ b/tests/build/pjproject/patches/siprtp.patch
@@ -0,0 +1,92 @@
+--- OLD_siprtp.c	2011-08-10 12:56:29.000000000 -0500
++++ siprtp.c	2011-08-24 13:42:22.000000000 -0500
+@@ -19,9 +19,6 @@
+  */
+ 
+ 
+-
+-
+-
+ /* Usage */
+ static const char *USAGE = 
+ " PURPOSE:								    \n"
+@@ -769,7 +766,6 @@
+ 	    print_call(call->index);
+ 	}
+ 
+-
+ 	call->inv = NULL;
+ 	inv->mod_data[mod_siprtp.id] = NULL;
+ 
+@@ -982,7 +978,7 @@
+ 	app.uri_to_call = pj_str(argv[pj_optind]);
+ 
+     /* Build local URI and contact */
+-    pj_ansi_sprintf( local_uri, "sip:%s:%d", app.local_addr.ptr, app.sip_port);
++    pj_ansi_sprintf( local_uri, "sip:siprtpuac@%s:%d", app.local_addr.ptr, app.sip_port);
+     app.local_uri = pj_str(local_uri);
+     app.local_contact = app.local_uri;
+ 
+@@ -1812,10 +1808,8 @@
+ 
+ }
+ 
+-
+ #include "siprtp_report.c"
+ 
+-
+ static void list_calls()
+ {
+     unsigned i;
+@@ -2014,7 +2008,7 @@
+ 
+ 
+ static FILE *log_file;
+-
++static FILE *report_log_file;
+ 
+ static void app_log_writer(int level, const char *buffer, int len)
+ {
+@@ -2028,6 +2022,14 @@
+ 	PJ_UNUSED_ARG(count);
+ 	fflush(log_file);
+     }
++
++    if (level <= app.app_log_level) {
++    if (report_log_file) {
++    int count = fwrite(buffer, len, 1, report_log_file);
++    PJ_UNUSED_ARG(count);
++    fflush(report_log_file);
++    }
++    }
+ }
+ 
+ 
+@@ -2048,6 +2050,15 @@
+ 	}
+     }
+ 
++    if (app.report_filename) {
++    report_log_file = fopen(app.report_filename, "wt");
++    if (report_log_file == NULL) {
++        PJ_LOG(1,(THIS_FILE, "Unable to open log file %s",
++              app.report_filename));
++        return -1;
++    }
++    }
++
+     return PJ_SUCCESS;
+ }
+ 
+@@ -2060,6 +2071,11 @@
+ 	fclose(log_file);
+ 	log_file = NULL;
+     }
++
++    if (report_log_file) {
++    fclose(report_log_file);
++    report_log_file = NULL;
++    }
+ }
+ 
+ 
diff --git a/tests/build/pjproject/patches/siprtp_report.patch b/tests/build/pjproject/patches/siprtp_report.patch
new file mode 100644
index 0000000..777871d
--- /dev/null
+++ b/tests/build/pjproject/patches/siprtp_report.patch
@@ -0,0 +1,148 @@
+--- siprtp_report.c	2011-08-10 12:56:41.000000000 -0500
++++ siprtp_report.c	2011-08-10 13:55:43.000000000 -0500
+@@ -55,7 +55,7 @@
+ 
+ 	PJ_TIME_VAL_SUB(now, call->connect_time);
+ 
+-	sprintf(duration, " [duration: %02ld:%02ld:%02ld.%03ld]",
++	sprintf(duration, "%02ld:%02ld:%02ld.%03ld",
+ 		now.sec / 3600,
+ 		(now.sec % 3600) / 60,
+ 		(now.sec % 60),
+@@ -68,8 +68,8 @@
+ 
+ 
+     /* Call number and state */
+-    PJ_LOG(3, (THIS_FILE,
+-	      "Call #%d: %s%s", 
++    PJ_LOG(1, (THIS_FILE,
++	      "'%d':\n    status : %s\n    duration : %s", 
+ 	      call_index, pjsip_inv_state_name(inv->state), 
+ 	      duration));
+ 
+@@ -82,7 +82,7 @@
+     else
+ 	userinfo[len] = '\0';
+ 
+-    PJ_LOG(3, (THIS_FILE, "   %s", userinfo));
++    PJ_LOG(1, (THIS_FILE, "    To : '%s'", userinfo));
+ 
+ 
+     if (call->inv == NULL || call->inv->state < PJSIP_INV_STATE_CONFIRMED ||
+@@ -101,7 +101,7 @@
+ 	if (call->response_time.sec) {
+ 	    t = call->response_time;
+ 	    PJ_TIME_VAL_SUB(t, call->start_time);
+-	    sprintf(pdd, "got 1st response in %ld ms", PJ_TIME_VAL_MSEC(t));
++	    sprintf(pdd, "%ld", PJ_TIME_VAL_MSEC(t));
+ 	} else {
+ 	    pdd[0] = '\0';
+ 	}
+@@ -109,19 +109,19 @@
+ 	if (call->connect_time.sec) {
+ 	    t = call->connect_time;
+ 	    PJ_TIME_VAL_SUB(t, call->start_time);
+-	    sprintf(connectdelay, ", connected after: %ld ms", 
++	    sprintf(connectdelay, "%ld", 
+ 		    PJ_TIME_VAL_MSEC(t));
+ 	} else {
+ 	    connectdelay[0] = '\0';
+ 	}
+ 
+-	PJ_LOG(3, (THIS_FILE, 
+-		   "   Signaling quality: %s%s", pdd, connectdelay));
++	PJ_LOG(1, (THIS_FILE, 
++		   "    pdd_ms : %s\n    connect_delay_ms : %s", pdd, connectdelay));
+     }
+ 
+ 
+-    PJ_LOG(3, (THIS_FILE,
+-	       "   Stream #0: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)",
++    PJ_LOG(1, (THIS_FILE,
++	       "    codec : %.*s\n    sample_rate_Hz : %d\n    ptime_ms : %d\n    rate_per_sec : %sB\n    rate_per_sec_w_hdr : %sB",
+    	(int)audio->si.fmt.encoding_name.slen,
+ 	audio->si.fmt.encoding_name.ptr,
+ 	audio->clock_rate,
+@@ -141,13 +141,26 @@
+ 		now.msec);
+     }
+ 
+-    PJ_LOG(3, (THIS_FILE, 
+-	   "              RX stat last update: %s\n"
+-	   "                 total %s packets %sB received (%sB +IP hdr)%s\n"
+-	   "                 pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
+-	   "                       (msec)    min     avg     max     last\n"
+-	   "                 loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
+-	   "                 jitter     : %7.3f %7.3f %7.3f %7.3f%s",
++    PJ_LOG(1, (THIS_FILE, 
++	   "    RX :\n"
++       "        last_update: %s\n"
++	   "        pkts : %s\n" 
++       "        rcv :  %sB\n"
++       "        rcv_w_hdr : %sB%s\n"
++	   "        pkt_loss_count : %d\n"
++       "        pkt_loss_percent : %3.1f%%\n"
++       "        pkt_dup_count : %d\n"
++       "        pkt_dup_percent : %3.1f%%\n"
++       "        pkt_reorder_count : %d\n"
++       "        pkt_reorder_percent : %3.1f%%%s\n"
++       "        loss_ms_min : %7.3f\n"
++       "        loss_ms_avg : %7.3f\n"
++       "        loss_ms_max : %7.3f\n"
++       "        loss_ms_last : %7.3f%s\n"
++       "        jitter_ms_min : %7.3f\n"
++       "        jitter_ms_avg : %7.3f\n"
++       "        jitter_ms_max : %7.3f\n"
++       "        jitter_ms_last : %7.3f%s",
+ 	   last_update,
+ 	   good_number(packets, audio->rtcp.stat.rx.pkt),
+ 	   good_number(bytes, audio->rtcp.stat.rx.bytes),
+@@ -185,13 +198,26 @@
+ 		now.msec);
+     }
+ 
+-    PJ_LOG(3, (THIS_FILE,
+-	   "              TX stat last update: %s\n"
+-	   "                 total %s packets %sB sent (%sB +IP hdr)%s\n"
+-	   "                 pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
+-	   "                       (msec)    min     avg     max     last\n"
+-	   "                 loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
+-	   "                 jitter     : %7.3f %7.3f %7.3f %7.3f%s",
++    PJ_LOG(1, (THIS_FILE,
++       "    TX :\n"
++       "        last_update: %s\n"
++       "        pkts : %s\n"    
++       "        sent :  %sB\n"
++       "        sent_w_hdr : %sB%s\n"
++       "        pkt_loss_count : %d\n"
++       "        pkt_loss_percent : %3.1f%%\n"
++       "        pkt_dup_count : %d\n" 
++       "        pkt_dup_percent : %3.1f%%\n"
++       "        pkt_reorder_count : %d\n" 
++       "        pkt_reorder_percent : %3.1f%%%s\n"
++       "        loss_ms_min : %7.3f\n"
++       "        loss_ms_avg : %7.3f\n"
++       "        loss_ms_max : %7.3f\n"
++       "        loss_ms_last : %7.3f%s\n"
++       "        jitter_ms_min : %7.3f\n"
++       "        jitter_ms_avg : %7.3f\n"
++       "        jitter_ms_max : %7.3f\n"
++       "        jitter_ms_last : %7.3f%s",
+ 	   last_update,
+ 	   good_number(packets, audio->rtcp.stat.tx.pkt),
+ 	   good_number(bytes, audio->rtcp.stat.tx.bytes),
+@@ -217,8 +243,12 @@
+ 	   ));
+ 
+ 
+-    PJ_LOG(3, (THIS_FILE,
+-	   "             RTT delay      : %7.3f %7.3f %7.3f %7.3f%s\n", 
++    PJ_LOG(1, (THIS_FILE,
++       "    RTT :\n"
++	   "        delay_ms_min : %7.3f\n"
++       "        delay_ms_avg : %7.3f\n"
++       "        delay_ms_max : %7.3f\n"
++       "        delay_ms_last : %7.3f%s", 
+ 	   audio->rtcp.stat.rtt.min / 1000.0,
+ 	   audio->rtcp.stat.rtt.mean / 1000.0,
+ 	   audio->rtcp.stat.rtt.max / 1000.0,
diff --git a/tests/build/pjproject/testcase.yaml b/tests/build/pjproject/testcase.yaml
new file mode 100644
index 0000000..3a7a4b1
--- /dev/null
+++ b/tests/build/pjproject/testcase.yaml
@@ -0,0 +1,27 @@
+name : pjproject
+options :
+    stop_tests_on_failure : True
+tests :
+    - siprtp :
+        timeline :
+            - build :
+                hosts :
+                    - testsuite-builder-1.digium.internal
+                cmd :
+                    dl_type : wget
+                    dl_url : 'http://www.pjsip.org/release/1.10/pjproject-1.10.tar.bz2'
+                    dl_unpack : 'pjproject-1.10.tar.bz2'
+                    patches :
+                        - 'siprtp.patch' : '!!TMP!!/pjproject-1.10/pjsip-apps/src/samples/siprtp.c'
+                        - 'siprtp_report.patch' : '!!TMP!!/pjproject-1.10/pjsip-apps/src/samples/siprtp_report.c'
+                    cd : 'pjproject-1.10'
+                    make_cmd :
+                        - ./configure
+                        - make
+                    redistribute :
+                        remotes :
+                            - testsuite-remote-1.digium.internal
+                            - testsuite-remote-2.digium.internal
+                            - testsuite-remote-3.digium.internal
+                        send_dir : '!!TMP!!/pjproject-1.10'
+                        install_dir : '!!TMP!!'

commit 12fee4ed8e3c610952f37296d37779e4e1e2dc1e
Author: Darren Sessions <dsessions at digium.com>
Date:   Tue Aug 23 11:37:34 2011 -0500

    Refactored how the test information is passed to the remotes in order to properly initialize the remotes during startup (this process restarts the remotes and the initial remote proxies thus become invalid).

diff --git a/RemoteAgent.py b/RemoteAgent.py
index cff6029..2e9fef4 100755
--- a/RemoteAgent.py
+++ b/RemoteAgent.py
@@ -40,7 +40,6 @@ class TestSuiteRemoteAgent(FileSystem.Handler, Misc.Utils, SubProcess.Exec, RPC.
         try:
             self.server = SimpleXMLRPCServer.SimpleXMLRPCServer((globalVars.ifaceInfo['rpc']['ipv4'], 8000), logRequests=False, allow_none=True)
         except:
-            globalVars.ipc.write('stop')
             print ' Unable to start RPC Server!\n %s!\n' % sys.exc_info()[1]
             return
 
@@ -51,12 +50,12 @@ class TestSuiteRemoteAgent(FileSystem.Handler, Misc.Utils, SubProcess.Exec, RPC.
         while globalVars.exitMainLoop == False:
             self.server.handle_request()
 
-    def ping(self, testInfo=None):
-        if not testInfo == None:
-            globalVars.testInfo = testInfo
-            return 'pong'
-        else:
-            return ''
+    def setTestInfo(self, testInfo):
+        globalVars.testInfo = testInfo
+        return self.resSuccess()
+
+    def ping(self):
+        return 'pong'
 
     def hostInfo(self):
         return (globalVars.ifaceInfo, globalVars.hostInfo)
@@ -88,19 +87,10 @@ class Startup(FileSystem.Handler):
             pass
 
         while globalVars.childRestart == True:
-            r, w = os.pipe()
             pid = os.fork()
             if pid:
-                os.close(w)
-                r = os.fdopen(r)
-                globalVars.ipc = r.read()
-                if globalVars.ipc == 'stop':
-                    break
                 os.waitpid(pid, 0)
             else:
-                os.close(r)
-                globalVars.ipc = os.fdopen(w, 'w')
-                #globalVars.ipc.write('reboot')
                 break
         if pid:
             sys.exit(0)
@@ -134,11 +124,6 @@ class Startup(FileSystem.Handler):
         else:
             TestSuiteRemoteAgent()
 
-        try:
-            w.close()
-        except:
-            pass
-        
         sys.exit(0)
 
 if __name__=='__main__':
diff --git a/Server.py b/Server.py
index 245a31a..bbe4003 100755
--- a/Server.py
+++ b/Server.py
@@ -16,6 +16,7 @@
 
 import os
 import sys
+import time
 import datetime
 import xmlrpclib
 import SimpleXMLRPCServer
@@ -29,6 +30,7 @@ from GlobalVars import globalVars
 
 globalVars.cwd = cdir
 
+import RPC
 import Info
 import Misc
 import Yaml
@@ -37,7 +39,7 @@ import JUnitXML
 import FileSystem
 import Interactive
 
-class TestSuiteServer(Misc.Utils, Plugin.Loader):
+class TestSuiteServer(Misc.Utils, Plugin.Loader, RPC.BaseClass):
     def __init__(self, argv=None):
         print '\n' + '\n'.join(Misc.Utils().banner()) + '\n ' + '-'*43 + '\n'
         if not os.path.exists('%s/tmp' % globalVars.cwd):
@@ -94,6 +96,26 @@ class TestSuiteServer(Misc.Utils, Plugin.Loader):
         if globalVars.mode == 'testing':
             x = JUnitXML.xml().initTestSuite()
 
+        ''' init the test-suite remote agents '''
+        for host in globalVars.remoteAgents:
+            res = self.rpcConnect([host, 'rpc', 'ipv4'])
+            if res['success'] == False:
+                print >> sys.stderr, '\n Unable to initialize remote agent on %s. %s\n' % (host, res['msg'])
+                return
+
+        res = self.rpcBatchCmd(globalVars.remoteAgents, ('restart',))
+        if res['success'] == False:
+            print >> sys.stderr, '\n Unable to restart remote agents. %s' % res['msg'] 
+            return
+
+        time.sleep(1)
+
+        for host in globalVars.remoteAgents:
+            res = self.rpcConnect([host, 'rpc', 'ipv4'])
+            if res['success'] == False:
+                print >> sys.stderr, '\n Unable to initialize remote agent on %s. %s\n' % (host, res['msg'])
+                return 
+
         ''' main test processing loop '''
         for list in yamlData:
             if stopTests == True:
@@ -139,7 +161,12 @@ class TestSuiteServer(Misc.Utils, Plugin.Loader):
                                 'testPath':'%s/%s' % (testCategory, testData['name']),
                                 'testOpts': testData['options']
                             }
-                        
+
+                            res = self.rpcBatchCmd(globalVars.remoteAgents, ('setTestInfo', globalVars.testInfo))
+                            if res['success'] == False:
+                                print >> sys.stderr, '   |- FATAL ERROR SETTING TESTINFO ON REMOTES!! %s' % res['msg']
+                                return
+
                             if globalVars.mode == 'testing':
                                 testElement, subTestElement = JUnitXML.xml().addTestCase(testElement, testName)
                             elif globalVars.mode == 'docs':
@@ -152,31 +179,14 @@ class TestSuiteServer(Misc.Utils, Plugin.Loader):
                                     if subTestCase[testName]['expected_failure'] == True:
                                        subTestElement = JUnitXML.xml().setProperty(subTestElement, 'expected_failure', subTestCase[testName]['expected_failure'])
 
-                                #''' import plugins '''
-                                #for plugin in [plugin for timeLine in subTestCase[testName]['timeline'] for plugin in timeLine]:
-                                #    
-                                #    if plugin in pluginData:
-                                #        pluginData[plugin] = self._dataMerge(self.pluginInit(plugin), pluginData[plugin])
-                                #    else:
-                                #        pluginData[plugin] = self.pluginInit(plugin)
-
-                                #    if pluginData[plugin]['success'] == False:
-                                #        errorMsgs.append(pluginData[plugin]['msg'])
-                                #        success = False
-
-                                #if success == False:
-                                #    print >> sys.stderr, '   |- ' + testName + ' - FAILED!\n    \- ' + '\n'.join(errorMsgs)
-                                #    subTestElement, errorMsgs = JUnitXML.xml().addFailure(subTestElement, errorMsgs)
-                                #    break
-
                                 ''' execute testcase timeline '''
                                 for timeLine in subTestCase[testName]['timeline']:
                                     for plugin in timeLine:
                                         globalVars.testInfo['testPlugin'] = plugin
-                                        print timeLine[plugin]
+
                                         runResults = self.pluginExecute(plugin, timeLine[plugin])
-                                        if globalVars.mode == 'testing': 
 
+                                        if globalVars.mode == 'testing': 
                                             if 'pluginData' in runResults:
                                                 pluginData[plugin] = runResults['pluginData']
 
@@ -187,13 +197,16 @@ class TestSuiteServer(Misc.Utils, Plugin.Loader):
                                             else:
                                                 for remote in runResults['shutdownList']:
                                                     shutdownList.append({remote:plugin})
+
                                             if runResults['success'] == False and globalVars.testSuccess == True:
                                                 globalVars.testSuccess = False
                                                 if 'remote' in runResults:
                                                     globalVars.testMsg = runResults['msg'] = "'%s' plugin on '%s': %s" % (plugin, runResults['remote'], runResults['msg'])
                                                 else:
                                                     globalVars.testMsg = runResults['msg'] = "'%s' plugin: %s" % (plugin, runResults['msg'])
+
                                                 break
+
                                         if runResults['success'] == 'docs':
                                             docsPluginData[testCategory][testData['name']]['tests'][testName]['pluginData'].append({'pluginName':plugin,'results':runResults['docs']})
 
@@ -220,11 +233,13 @@ class TestSuiteServer(Misc.Utils, Plugin.Loader):
                                             globalVars.testMsg = 'Test passed but the expected failure flag is set to true!'
                                         print >> sys.stderr, '   |- Test "' + testName + '" - FAILED!\n    \- ' + globalVars.testMsg
                                         xmlFailureMsg = '%s' % globalVars.testMsg
+
                                         if getattr(globalVars, 'bambooBuildURL', None):
                                             xmlFailureMsg = '%s\nIf artifacts specific to this test were generated,\n' % globalVars.testMsg
                                             xmlFailureMsg = '%s they can be found pasting the link below in your browser or by clicking the Artifacts tab above.\n\n' % xmlFailureMsg
                                             xmlFailureMsg = '%s%s%s' % (xmlFailureMsg, globalVars.bambooBuildURL, globalVars.testInfo['testPath'])
                                         subTestElement, errorMsgs = JUnitXML.xml().addFailure(subTestElement, [xmlFailureMsg])
+
                                         if 'stop_tests_on_failure' in globalVars.testInfo['testOpts']:
                                             print >> sys.stderr, '\n Stop tests on failure = True\n'
                                             if globalVars.testInfo['testOpts']['stop_tests_on_failure'] == True:
diff --git a/configs/GlobalVars.py b/configs/GlobalVars.py
index af3723c..37d47d5 100644
--- a/configs/GlobalVars.py
+++ b/configs/GlobalVars.py
@@ -19,10 +19,12 @@ import sys
 class globalVars:
     debug = False
     pname = os.path.split(sys.argv[0])[1]
-
     mode = ''
+
     rpcCmd = {}
     rpcProxy = {}
+
+    testInfo = {}
     remoteInfo = {}
     hostInfo = {'interfaceList':[], 'plat':'', 'arch':'', 'hostname':'', 'fqdn':''}
 
@@ -48,7 +50,6 @@ class globalVars:
         plugins = {}
         pluginConfigs = '!!CWD!!/configs/plugins'
 
-        testInfo = {}
         testSuccess = True
         testMainDir = 'tests'
         testCategoryYaml = 'tests.yaml'
diff --git a/events/shutdown.yaml b/events/shutdown.yaml
index 191865a..bc04f6d 100644
--- a/events/shutdown.yaml
+++ b/events/shutdown.yaml
@@ -2,7 +2,7 @@ name : Cleanup
 options :
     stop_tests_on_failure : True
 tests :
-    - shutdown :
+    - remotes :
         timeline:
             - testsuite_remotes :
                 cmd :
diff --git a/lib/python/FileSystem.py b/lib/python/FileSystem.py
index 71fb9bd..790f94c 100644
--- a/lib/python/FileSystem.py
+++ b/lib/python/FileSystem.py
@@ -20,6 +20,7 @@ import sys
 import yaml
 import shutil
 import tarfile
+import xmlrpclib
 
 import Misc
 
diff --git a/lib/python/Logging.py b/lib/python/Logging.py
index d86ca19..693a585 100644
--- a/lib/python/Logging.py
+++ b/lib/python/Logging.py
@@ -21,9 +21,11 @@ import sys
 class Artifacts:
     def consoleLogger(self, plugin, label):
 
+        print globalVars.testInfo
+
         baseDir = '%s/tmp/artifacts/%s' % (globalVars.cwd, globalVars.testInfo['testLogs'])
 
-        print '**********%s' % baseDir
+        print BaseDir
 
         self.__artifactDirCheck()
 
diff --git a/lib/python/Plugin.py b/lib/python/Plugin.py
index 4bc89ee..b078552 100644
--- a/lib/python/Plugin.py
+++ b/lib/python/Plugin.py
@@ -28,11 +28,11 @@ class BaseClass(RPC.BaseClass, FileSystem.Handler, Misc.Utils):
         if globalVars.testSuccess == False:
             if 'run_on_test_failure' in testData:
                 if testData['run_on_test_failure'] == False:
-                    return {'success':False,'msg':'Test has already failed.'}
+                    return self.resFailure('Test has already failed.')
             else:
-                return {'success':False,'msg':'Test has already failed.'}
+                return self.resFailure('Test has already failed.')
         if not 'cmd' in testData:
-            return {'success':False,'msg':'No command specified.'}
+            return self.resFailure('No command specified.')
         return self.main(testData)
 
     def cleanup(self, whatToInit, val):
diff --git a/lib/python/RPC.py b/lib/python/RPC.py
index 48f60c1..b4d93b0 100644
--- a/lib/python/RPC.py
+++ b/lib/python/RPC.py
@@ -51,7 +51,7 @@ class BaseClass(Misc.Utils):
 
             try:
                 timerStart = datetime.datetime.now().second
-                if not globalVars.rpcProxy[host[0]].ping(globalVars.testInfo) == 'pong':
+                if not globalVars.rpcProxy[host[0]].ping() == 'pong':
                     return self.resFailure('%s did not PONG!' % host[0])
                 timerDelta = datetime.datetime.now().second - timerStart
             except:

commit 8231474b356c60e8daa094057e7b61129f39b582
Author: Darren Sessions <dsessions at digium.com>
Date:   Mon Aug 22 11:09:55 2011 -0500

    Added a global variable class to both the remote and server testsuite components to make passing data to and from various things (plugins, remote, etc.) a lot easier to deal with. Added a few configuration files that make configuring plugins and the remote and server component a bit more dynamic in terms of not having lists of hosts in five or six different places, etc.

diff --git a/configs/GlobalVars.py b/configs/GlobalVars.py
new file mode 100644
index 0000000..af3723c
--- /dev/null
+++ b/configs/GlobalVars.py
@@ -0,0 +1,58 @@
+'''
+
+ Server and Remote Agent Global Variables
+
+ Test-Suite - Copyright (C) 2011, Digium, Inc.
+ 
+ Written by Darren Sessions
+
+ See http://wiki.asterisk.org for more information.
+
+ This program is free software, distributed under the 
+ terms of the GNU General Public License Version 2.
+
+'''
+
+import os
+import sys
+
+class globalVars:
+    debug = False
+    pname = os.path.split(sys.argv[0])[1]
+
+    mode = ''
+    rpcCmd = {}
+    rpcProxy = {}
+    remoteInfo = {}
+    hostInfo = {'interfaceList':[], 'plat':'', 'arch':'', 'hostname':'', 'fqdn':''}
+
+    if pname == 'RemoteAgent.py':
+        childRestart = True
+        exitMainLoop = False
+        startupErrors = False
+        configuredProperly = False
+        processList = {}
+
+        ifaces = {'rpc':'', 'public1':'', 'public2':'', 'private':''}
+        ifaceInfo = {'rpc':{'ipv4':'', 'ipv6':'', 'mac':''}, 'public1':{'ipv4':'', 'ipv6':'', 'mac':''}, 'public2':{'ipv4':'', 'ipv6':'', 'mac':''}, 'private':{'ipv4':'', 'ipv6':'', 'mac':''}}
+
+        interactive = {'qData':{}, 'menuData':{}}
+        configFile = '!!CWD!!/configs/remote/remoteAgent.yaml'
+
+    elif pname == 'Server.py':
+        docs = []
+        overrides = {}
+
+        cleanup = {'process':[], 'tempFile':[]}
+
+        plugins = {}
+        pluginConfigs = '!!CWD!!/configs/plugins'
+
+        testInfo = {}
+        testSuccess = True
+        testMainDir = 'tests'
+        testCategoryYaml = 'tests.yaml'
+        testCaseYaml = 'testcase.yaml'
+
+        remoteAgents = []
+        remoteAgentsListFile = '!!CWD!!/configs/server/remoteAgents.yaml'
diff --git a/configs/plugins/failover_conf.yaml b/configs/plugins/failover_conf.yaml
new file mode 100644
index 0000000..718bc60
--- /dev/null
+++ b/configs/plugins/failover_conf.yaml
@@ -0,0 +1,48 @@
+## 
+##  This config file is used to construct a python dict that is used to
+##  allocate shared ip addresses within the failover plugin. 
+##
+pools :
+    failover_pool_1 :
+        ipv4 :
+            - 10.19.139.44 :
+                network : 10.19.136.0
+                netmask : 255.255.252.0
+                broadcast : 10.19.139.255
+            - 10.19.139.45 :
+                network : 10.19.136.0
+                netmask : 255.255.252.0
+                broadcast : 10.19.139.255
+            - 10.19.139.46 :
+                network : 10.19.136.0
+                netmask : 255.255.252.0
+                broadcast : 10.19.139.255
+            - 10.19.139.47 :
+                network : 10.19.136.0
+                netmask : 255.255.252.0
+                broadcast : 10.19.139.255
+            - 10.19.139.48 :
+                network : 10.19.136.0
+                netmask : 255.255.252.0
+                broadcast : 10.19.139.255
+            - 10.19.139.49 :
+                network : 10.19.136.0
+                netmask : 255.255.252.0
+                broadcast : 10.19.139.255
+            - 10.19.139.50 : 
+                network : 10.19.136.0
+                netmask : 255.255.252.0
+                broadcast : 10.19.139.255
+            - 10.19.139.51 :
+                network : 10.19.136.0
+                netmask : 255.255.252.0
+                broadcast : 10.19.139.255
+        ipv6 :
+            - fdf0:801b:c228:171d::0100
+            - fdf0:801b:c228:171d::0200
+            - fdf0:801b:c228:171d::0300
+            - fdf0:801b:c228:171d::0400
+            - fdf0:801b:c228:171d::0500
+            - fdf0:801b:c228:171d::0600
+            - fdf0:801b:c228:171d::0700
+            - fdf0:801b:c228:171d::0800
diff --git a/configs/remote/remoteAgent.yaml.sample b/configs/remote/remoteAgent.yaml.sample
new file mode 100644
index 0000000..337139c
--- /dev/null
+++ b/configs/remote/remoteAgent.yaml.sample
@@ -0,0 +1 @@
+ifaces: {private: en1, public1: en1, public2: en1, rpc: en1}
diff --git a/configs/server/remoteAgents.yaml b/configs/server/remoteAgents.yaml
new file mode 100644
index 0000000..29d4f6e
--- /dev/null
+++ b/configs/server/remoteAgents.yaml
@@ -0,0 +1,4 @@
+- testsuite-remote-1.digium.internal
+- testsuite-remote-2.digium.internal
+- testsuite-remote-3.digium.internal
+- testsuite-builder-1.digium.internal

commit 138c575ea692dce41c563419dc7701071cef53f7
Author: Darren Sessions <dsessions at digium.com>
Date:   Mon Aug 22 11:02:19 2011 -0500

    Refactored the plugin loader code to streamline the cleanup process by allowing process cleanup to be specified during the test-suite server startup instead of during test-case execution. Added cleanup functions for processes and temporary files. Refactored the plugin load class and consolidated the plugin inspect into that class to reduce code and reduce complexity within the class. Refactored the RPC code to drastically reduce the complexity and allow for parallel RPC calls to be made to multiple hosts to drastically increase certain batch commands (i.e. startup, shutdown, binary distribution, etc.).

diff --git a/Server.py b/Server.py
index ac02d61..245a31a 100755
--- a/Server.py
+++ b/Server.py
@@ -37,11 +37,6 @@ import JUnitXML
 import FileSystem
 import Interactive
 
-#import plugins
-#import JUnitXML
-#import confluence
-#import yaml_parser
-
 class TestSuiteServer(Misc.Utils, Plugin.Loader):
     def __init__(self, argv=None):
         print '\n' + '\n'.join(Misc.Utils().banner()) + '\n ' + '-'*43 + '\n'
@@ -70,6 +65,11 @@ class TestSuiteServer(Misc.Utils, Plugin.Loader):
         if not globalVars.mode == 'testing' and not globalVars.mode == 'docs':
             self.fatalError('%s is an invalid mode.' % globalVars.mode)
 
+        res = self.readFile(globalVars.remoteAgentsListFile, yamlBool=True)
+        if res['success'] == False:
+            self.fatalError('Unable to load remote agent list!')
+        globalVars.remoteAgents = res['data']
+
         res = Yaml.Load().testData()
         if res['success'] == False:
             self.fatalError('Unable to load tests!\n\n %s' % res['msg'])
@@ -84,9 +84,17 @@ class TestSuiteServer(Misc.Utils, Plugin.Loader):
 
         Info.Host().Get()
 
+        ''' import plugins ''' 
+        res = self.loadPlugins()
+        if res['success'] == False:
+            print >> sys.stderr, '\n Unable to load plugins! %s' % res['msg']
+            return
+
+        ''' init the JUnit xml '''
         if globalVars.mode == 'testing':
             x = JUnitXML.xml().initTestSuite()
 
+        ''' main test processing loop '''
         for list in yamlData:
             if stopTests == True:
                 break
@@ -131,13 +139,6 @@ class TestSuiteServer(Misc.Utils, Plugin.Loader):
                                 'testPath':'%s/%s' % (testCategory, testData['name']),
                                 'testOpts': testData['options']
                             }
-                            print globalVars.testInfo
-                            #globalVars.testInfo['testCategory'] = testCategory
-                            #globalVars.testInfo['testCase'] = testData['name']
-                            #globalVars.testInfo['testName'] = testName
-                            #globalVars.testInfo['testLogs'] = "%s/%s/%s" % (testCategory, testData['name'], testName)
-                            #globalVars.testInfo['testPath'] = "%s/%s" % (testCategory, testData['name'])
-                            #globalVars.testInfo['testOpts'] = testData['options'] 
                         
                             if globalVars.mode == 'testing':
                                 testElement, subTestElement = JUnitXML.xml().addTestCase(testElement, testName)
@@ -151,28 +152,29 @@ class TestSuiteServer(Misc.Utils, Plugin.Loader):
                                     if subTestCase[testName]['expected_failure'] == True:
                                        subTestElement = JUnitXML.xml().setProperty(subTestElement, 'expected_failure', subTestCase[testName]['expected_failure'])
 
-                                ''' import plugins '''
-                                for plugin in [plugin for timeLine in subTestCase[testName]['timeline'] for plugin in timeLine]:
-                                    
-                                    if plugin in pluginData:
-                                        pluginData[plugin] = self._dataMerge(self.pluginInit(plugin), pluginData[plugin])
-                                    else:
-                                        pluginData[plugin] = self.pluginInit(plugin)
+                                #''' import plugins '''
+                                #for plugin in [plugin for timeLine in subTestCase[testName]['timeline'] for plugin in timeLine]:
+                                #    
+                                #    if plugin in pluginData:
+                                #        pluginData[plugin] = self._dataMerge(self.pluginInit(plugin), pluginData[plugin])
+                                #    else:
+                                #        pluginData[plugin] = self.pluginInit(plugin)
 
-                                    if pluginData[plugin]['success'] == False:
-                                        errorMsgs.append(pluginData[plugin]['msg'])
-                                        success = False
+                                #    if pluginData[plugin]['success'] == False:
+                                #        errorMsgs.append(pluginData[plugin]['msg'])
+                                #        success = False
 
-                                if success == False:
-                                    print >> sys.stderr, '   |- ' + testName + ' - FAILED!\n    \- ' + '\n'.join(errorMsgs)
-                                    subTestElement, errorMsgs = JUnitXML.xml().addFailure(subTestElement, errorMsgs)
-                                    break
+                                #if success == False:
+                                #    print >> sys.stderr, '   |- ' + testName + ' - FAILED!\n    \- ' + '\n'.join(errorMsgs)
+                                #    subTestElement, errorMsgs = JUnitXML.xml().addFailure(subTestElement, errorMsgs)
+                                #    break
 
                                 ''' execute testcase timeline '''
                                 for timeLine in subTestCase[testName]['timeline']:
                                     for plugin in timeLine:
                                         globalVars.testInfo['testPlugin'] = plugin
-                                        runResults = self.pluginExecute(plugin, pluginData[plugin], timeLine[plugin])
+                                        print timeLine[plugin]
+                                        runResults = self.pluginExecute(plugin, timeLine[plugin])
                                         if globalVars.mode == 'testing': 
 
                                             if 'pluginData' in runResults:
@@ -310,7 +312,6 @@ class TestSuiteServer(Misc.Utils, Plugin.Loader):
             results = self.writeFile('!!CWD!!/testsuite_results.xml', JUnitXML.xml().prettyXml(x))
             if results['success'] == False:
                 print >> sys.stderr, '\n\n Unable to generate XML test results file. %s' % results['msg']
-            cleanup()
 
         return
 
@@ -329,15 +330,6 @@ class TestSuiteServer(Misc.Utils, Plugin.Loader):
                     #args = args.replace('/tmp/', './')
                     #print args
 
-class cleanup:
-    def __init__(self):
-        self.dirs()
-
-    def dirs(self):
-        if not os.path.exists('%s/tmp' % globalVars.cwd):
-            return {'success':False,'msg':'The %s path does not exist.' % dir}
-        return {'success':True}
-
 if __name__=='__main__':
     TestSuiteServer(sys.argv)
     print >> sys.stderr, '\n'
diff --git a/events/shutdown.yaml b/events/shutdown.yaml
index a97c33d..191865a 100644
--- a/events/shutdown.yaml
+++ b/events/shutdown.yaml
@@ -6,15 +6,4 @@ tests :
         timeline:
             - testsuite_remotes :
                 cmd :
-                    cleanup :
-                        - testsuite-remote-1.digium.internal
-                        - testsuite-remote-2.digium.internal
-                        - testsuite-remote-3.digium.internal
-                        - testsuite-remote-4.digium.internal
-                        - testsuite-remote-5.digium.internal
-                        - testsuite-remote-6.digium.internal
-                        - testsuite-remote-7.digium.internal
-                        - testsuite-remote-8.digium.internal
-                        - testsuite-remote-9.digium.internal
-                        - testsuite-remote-10.digium.internal
-                        - testsuite-builder-1.digium.internal
+                    cleanup
diff --git a/events/startup.yaml b/events/startup.yaml
index 3d001e0..4e983c9 100644
--- a/events/startup.yaml
+++ b/events/startup.yaml
@@ -2,19 +2,8 @@ name : Init Remotes
 options :
     stop_tests_on_failure : True
 tests :
-    - startup :
+    - remotes :
         timeline:
             - testsuite_remotes :
                 cmd :
-                    startup :
-                        - testsuite-remote-1.digium.internal
-                        - testsuite-remote-2.digium.internal
-                        - testsuite-remote-3.digium.internal
-                        - testsuite-remote-4.digium.internal
-                        - testsuite-remote-5.digium.internal
-                        - testsuite-remote-6.digium.internal
-                        - testsuite-remote-7.digium.internal
-                        - testsuite-remote-8.digium.internal
-                        - testsuite-remote-9.digium.internal
-                        - testsuite-remote-10.digium.internal
-                        - testsuite-builder-1.digium.internal
+                    startup
diff --git a/lib/python/FileSystem.py b/lib/python/FileSystem.py
index a296a06..71fb9bd 100644
--- a/lib/python/FileSystem.py
+++ b/lib/python/FileSystem.py
@@ -41,7 +41,7 @@ class Handler(Misc.Utils):
         fout.close()
         return self.resSuccess()
 
-    def readFile(self, fn, yamlBool=False, tempDataStorage=False):
+    def readFile(self, fn, yamlBool=False, tempDataStorage=False, rpcTransfer=False):
         fn = self.replaceMarkers(fn)
         try:
             fin = open('%s' % fn, 'rb')
@@ -57,8 +57,9 @@ class Handler(Misc.Utils):
         if tempDataStorage == True:
             globalVars.tempDataStorage = data
             return self.resSuccess()
-        else:
-            return self.resSuccess(addRes={'data':data})
+        elif rpcTransfer == True:
+            data = xmlrpclib.Binary(data)
+        return self.resSuccess(addRes={'data':data})
 
     def removeFile(self, fn):
         fn = self.replaceMarkers(fn)
diff --git a/lib/python/Misc.py b/lib/python/Misc.py
index fe4491f..04d6574 100644
--- a/lib/python/Misc.py
+++ b/lib/python/Misc.py
@@ -97,7 +97,7 @@ class Utils():
         try:
             globalVars.remoteInfo[hostList[0]]['ifaceInfo'][hostList[1]][hostList[2]]
         except:
-            return self.resFailure('The remote, %s, has missing or invalid data for the interface label or ip version specified.' % hostList[0])
+            return self.resFailure('The remote, %s, has missing or invalid data for the interface label or ip version specified: %s' % (hostList[0], hostList[1]))
 
         return {'success':True, 'ip':globalVars.remoteInfo[hostList[0]]['ifaceInfo'][hostList[1]][hostList[2]]}
         
@@ -131,4 +131,14 @@ class Utils():
 
         return {'success':True}
 
+    def listDirFiles(self, fp, ext):
+        if not os.path.exists(fp) or not os.access(fp, os.X_OK):
+            return self.resFailure('The \'%s\' path does not exist!')
+        matchedFiles = []
+        for f in os.listdir(fp):
+            if len(f.split('.')) == 2:
+                if f.split('.')[1] == ext:
+                    matchedFiles.append(f.split('.')[0])
+        return matchedFiles
+
     resSuccess = resFailure = resultsHandler
diff --git a/lib/python/Plugin.py b/lib/python/Plugin.py
index 8dbe65e..4bc89ee 100644
--- a/lib/python/Plugin.py
+++ b/lib/python/Plugin.py
@@ -24,7 +24,7 @@ import Misc
 import FileSystem
 
 class BaseClass(RPC.BaseClass, FileSystem.Handler, Misc.Utils):
-    def run(self, testData, pluginData):
+    def run(self, testData):
         if globalVars.testSuccess == False:
             if 'run_on_test_failure' in testData:
                 if testData['run_on_test_failure'] == False:
@@ -33,57 +33,69 @@ class BaseClass(RPC.BaseClass, FileSystem.Handler, Misc.Utils):
                 return {'success':False,'msg':'Test has already failed.'}
         if not 'cmd' in testData:
             return {'success':False,'msg':'No command specified.'}
+        return self.main(testData)
 
-        return self.main(testData, pluginData)
+    def cleanup(self, whatToInit, val):
+        if not whatToInit in globalVars.cleanup:
+            print '%s is not a valid cleanup element.' % whatToInit
+            return
+        try:
+            globalVars.cleanup[whatToInit].index(val)
+        except:
+            globalVars.cleanup[whatToInit].append(val)
+        return
 
 class Loader(FileSystem.Handler, Misc.Utils):
-    def pluginExecute(self, name, pluginData, testData):
+    def pluginExecute(self, pluginName, testData):
         try:
-            func = getattr(pluginData['module'], 'plugin')
+            func = getattr(globalVars.plugins[pluginName], 'plugin')
         except AttributeError:
-            return self.resFailure('Unable to execute module \'%s\'. %s' % (name, sys.exc_info()[1]))
+            return self.resFailure('Unable to execute module \'%s\'. %s' % (pluginName, sys.exc_info()[1]))
         else:
-            results = func().run(testData, pluginData)
+            results = func().run(testData)
             if not type(results) == dict:
-                return self.resFailure('The \'%s\' module return invalid or missing data.' % name)
+                return self.resFailure('The \'%s\' module return invalid or missing data.' % pluginName)
             return results
 
-    def pluginInit(self, name):
-        loadResults = self.__load(name)
+    def pluginInit(self):
+        loadResults = self.__load()
         if loadResults['success'] == False:
             return loadResults
-        checkResults = self.__check(name, loadResults['module'])
+        checkResults = self.__check(loadResults['module'])
         if checkResults['success'] == False:
             return checkResults
         return loadResults
 
-    def __load(self, name):
-        pluginConfig = None
-        try:
-            return {'success':True,'module':sys.modules[name]}
-        except KeyError:
-            pass
-        try:
-            fp, pathName, desc = imp.find_module(name)
-        except ImportError:
-            return self.resFailure('%s' % sys.exc_info()[1])
-        try:
-            res = self.readFile('%s/%s.conf' % (globalVars.pluginConfigs, name), False, True)
-            if res['success'] == True:
-                pluginConfig = res['data']
-            return self.resSuccess(addRes={'module':imp.load_module(name, fp, pathName, desc), 'pluginConfigFile':pluginConfig})
-        finally:
-            if fp:
-                fp.close()
-
-    def __check(self, name, module):
-        try:
-            inspect.ismethod(module.plugin().main)
-        except AttributeError:
-            return self.resFailure('The required "main" method in the "plugin" class is not implemented in the "%s" plugin.' % name)
-        except:
-            return self.resFailure('The "main" method in the "plugin" class in "%s" is not responding properly. %s.' % (name, sys.exc_info()))
-        argCheck = inspect.getargspec(module.plugin().main)
-        if argCheck[0] != ['self','testData', 'pluginData'] and argCheck[0] != ['self','testData', 'pluginData', 'testCaseRPC']:
-            return self.resFailure('The \'main\' method in the \'plugin\' class within \'%s\' has improperly labeled args! i.e. def main(self, testData, testInfo).' % name)
+    def loadPlugins(self):
+        for pluginName in self.listDirFiles('./plugins', 'py'):
+            pluginConfig = None
+            try:
+                return {'success':True,'module':sys.modules[pluginName]}
+            except KeyError:
+                pass
+            try:
+                fp, pathName, desc = imp.find_module(pluginName)
+            except ImportError:
+                return self.resFailure('%s' % sys.exc_info()[1])
+            try:
+                res = self.readFile('%s/%s.conf' % (globalVars.pluginConfigs, pluginName), False, True)
+                if res['success'] == True:
+                    pluginConfig = res['data']
+                try:
+                    module = imp.load_module(pluginName, fp, pathName, desc)
+                except:
+                    return self.resFailure('Loading the \'%s\' plugin generated an exception: %s' % (pluginName, sys.exc_info()))
+                try:
+                    inspect.ismethod(module.plugin().main)
+                except AttributeError:
+                    return self.resFailure('The required "main" method in the "plugin" class is not implemented in the "%s" plugin.' % pluginName)
+                except:
+                    return self.resFailure('The "main" method in the "plugin" class in "%s" is not responding properly. %s.' % (pluginName, sys.exc_info()))
+                argCheck = inspect.getargspec(module.plugin().main)
+                if argCheck[0] != ['self','testData']:
+                    return self.resFailure('The \'main\' method in the \'plugin\' class within \'%s\' has improperly labeled args! i.e. def main(self, testData).' % pluginName)
+                globalVars.plugins[pluginName] = module
+            finally:
+                if fp:
+                    fp.close()
         return self.resSuccess()
diff --git a/lib/python/RPC.py b/lib/python/RPC.py
index d9bfc5c..48f60c1 100644
--- a/lib/python/RPC.py
+++ b/lib/python/RPC.py
@@ -82,7 +82,6 @@ class BaseClass(Misc.Utils):
             return {'success':'docs','docs':self.docs}
 
         def _runCmd(self, cmd, *args):
-            print '%s - %s - %s' % (self.host, cmd, args)
             msg = {}
             try:
                 func = getattr(globalVars.rpcProxy[self.host], cmd)
@@ -113,9 +112,6 @@ class BaseClass(Misc.Utils):
         if res['success'] == False:
             return res
 
-        #timerDelta = datetime.datetime.now() - timerStart
-        #print '    \ - Transfered files to \'%s\' in: %s.%s seconds.' % (remoteHost, timerDelta.seconds, timerDelta.microseconds)
-
         return self.resSuccess()
 
     def rpcBatchCmd(self, hosts, cmd):
@@ -127,7 +123,7 @@ class BaseClass(Misc.Utils):
             ipc[host]['pid'] = os.fork()
             if ipc[host]['pid']:
                 parent = True
-                print 'spawning child %s' % ipc[host]['pid']
+                #print 'spawning child %s' % ipc[host]['pid']
                 os.close(ipc[host]['w'])
             else:
                 os.close(ipc[host]['r'])
@@ -138,8 +134,7 @@ class BaseClass(Misc.Utils):
         if parent:
             for host in hosts:
                 res = ast.literal_eval(os.fdopen(ipc[host]['r']).read())
+                os.waitpid(ipc[host]['pid'], 0)
                 if res['success'] == False:
-                    print res
                     return res
-                os.waitpid(ipc[host]['pid'], 0)
         return self.resSuccess()
diff --git a/lib/python/Yaml.py b/lib/python/Yaml.py
index e490ccb..07eb698 100755
--- a/lib/python/Yaml.py
+++ b/lib/python/Yaml.py
@@ -85,7 +85,6 @@ class Load(FileSystem.Handler):
             if res['success'] == False:
                 return res
             test = res['data']
-            print test
             test['name'] = 'init'
         elif event == 'shutdown':
             res = self.readFile('events/shutdown.yaml', yamlBool=True)
diff --git a/plugins/asteriskscf_configurator.py b/plugins/asteriskscf_configurator.py
index c8b10e1..da88834 100644
--- a/plugins/asteriskscf_configurator.py
+++ b/plugins/asteriskscf_configurator.py
@@ -20,23 +20,23 @@ import sys
 import time
 
 import Misc
-import PluginBaseClass
+import Plugin
 
 sys.path.append('/opt/Ice-3.4.2/python')
 
 import Ice
 
-class plugin(PluginBaseClass.ManualRPC):
-    def main(self, testData, pluginData):
+class plugin(Plugin.BaseClass):
+    def main(self, testData):
         if globalVars.mode == 'docs':
-            rpc = self.manualRPC('testsuite-remote-1.digium.internal')
+            #rpc = self.manualRPC('testsuite-remote-1.digium.internal')
             for cmd in testData['cmd']:
                 if cmd == 'sip':
                     execCmd = ['./SipConfigurator.py', '--config=<path to sip config>', '--locator="LocatorService:tcp -p 4411 -h <service locator host"']
                 elif cmd == 'rtp':
                     execCmd = ['./RtpConfigurator.py', '--config=<path to rtp config>', '--locator="LocatorService:tcp -p 4411 -h <service locator host"']
-                results = rpc['rpc']('run', 'random host', globalVars, 'asteriskscf_configurator', testData['cmd'], execCmd)
-            return results
+                res = rpc['rpc']('run', 'random host', globalVars, 'asteriskscf_configurator', testData['cmd'], execCmd)
+            return res
 
         if not 'configuration_wipe' in testData:
             testData['configuration_wipe'] = None
@@ -68,12 +68,16 @@ class plugin(PluginBaseClass.ManualRPC):
             else:
                 return {'success':False,'shutdownExempt':'True','msg':'The %s command is invalid.' % cmd}
 
-            results = ConfiguratorApp(globalVars, cmd, testData['service_locator_host'], testData['cmd'][cmd], AsteriskSCF, testData['configuration_wipe'], serviceLocatorParams).main([''])
-            if 'msg' in results:
-                results['msg'] = "Error detected while configuring the \'%s\' component. %s" % (cmd, results['msg'])
+            res = self.hostLabelResolver(testData['service_locator_host'])
+            if res['success'] == False:
+                return res
 
-            if results['success'] == False:
-                return {'success':False, 'shutdownExempt':'True', 'msg':results['msg']}
+            res = ConfiguratorApp(globalVars, cmd, res['ip'], testData['cmd'][cmd], AsteriskSCF, testData['configuration_wipe'], serviceLocatorParams).main([''])
+            if 'msg' in res:
+                res['msg'] = "Error detected while configuring the \'%s\' component. %s" % (cmd, res['msg'])
+
+            if res['success'] == False:
+                return {'success':False, 'shutdownExempt':'True', 'msg':res['msg']}
 
         return {'success':True, 'shutdownExempt':'True'}
 
@@ -158,7 +162,7 @@ class ConfiguratorApp(Ice.Application, Misc.Utils):
 
         visitor.AsteriskSCF = self.AsteriskSCF
       
-        locator = 'LocatorService:tcp -p 4411 -h %s' % globalVars.remoteInfo[self.host[0]]['ifaceInfo'][self.host[1]][self.host[2]]
+        locator = 'LocatorService:tcp -p 4411 -h %s' % self.host
         configurationServiceName = ""
 
         try:
@@ -181,9 +185,9 @@ class ConfiguratorApp(Ice.Application, Misc.Utils):
 
         for config in self.config:
             for section in config:
-                results = visitor.visit(config[section], section)
-                if results['success'] == False:
-                    return results
+                res = visitor.visit(config[section], section)
+                if res['success'] == False:
+                    return res
 
         if visitor.groups:
             if self.configWipe == True:
@@ -245,17 +249,11 @@ class SectionVisitors(Misc.Utils):
         except:
             return self.resFailure('The testcase host is invalid in the %s section. Must be a list containing hostname, iface label, and ip version. ' % section)
 
-        try:
-            globalVars.remoteInfo[config[hostLabel][0]]
-        except:
-            return self.resFailure('The host specified, %s, was not initialized during the test-suite startup event and thus can not be used.' % config[hostLabel][0])
-
-        try:
-            globalVars.remoteInfo[config[hostLabel][0]]['ifaceInfo'][config[hostLabel][1]][config[hostLabel][2]]
-        except:
-            return self.resFailure('The remote, %s, has missing or invalid data for the interface label or ip version specified.' % config[hostLabel][0])
+        res = self.hostLabelResolver(config[hostLabel])
+        if res['success'] == False:
+            return res
             
-        return {'success':True, hostLabel:globalVars.remoteInfo[config[hostLabel][0]]['ifaceInfo'][config[hostLabel][1]][config[hostLabel][2]], portLabel:config[portLabel]}
+        return {'success':True, hostLabel:res['ip'], portLabel:config[portLabel]}
 
 class SipSectionVisitors(SectionVisitors):
     def visit_general(self, config, section):
diff --git a/plugins/asteriskscf_icebox.py b/plugins/asteriskscf_icebox.py
index ecff7ac..af6920c 100644
--- a/plugins/asteriskscf_icebox.py
+++ b/plugins/asteriskscf_icebox.py
@@ -17,10 +17,12 @@ from GlobalVars import globalVars
 
 import time
 
-import PluginBaseClass
+import Plugin
 
-class plugin(PluginBaseClass.TestCaseRPC):
-    def main(self, testData, pluginData, testCaseRPC):
+class plugin(Plugin.BaseClass):
+    Plugin.BaseClass().cleanup('process', 'icebox')
+
+    def main(self, testData):
         iceBoxCmd = ['/opt/Ice-3.4.2/bin/icebox', '--Ice.Config=!!TMP!!/']
 
         ''' start and stop individual components. shutdown is implemented in the main testsuite code ''' 
@@ -34,7 +36,13 @@ class plugin(PluginBaseClass.TestCaseRPC):
                     return {'success':False, 'msg':'The mode element must exist when the start command is used. Valid modes are either standalone or replicated.'}
 
                 if 'service_locator' in testData['cmd']['start']:
-                    configArgs['service_locator'] = ' -h %s' % testData['cmd']['start']['service_locator']
+                    res = self.hostLabelResolver(testData['cmd']['start']['service_locator'])
+                    if res['success'] == False:
+                        return res
+
+                    print res['ip']
+
+                    configArgs['service_locator'] = ' -h %s' % res['ip']
                 else:
                     configArgs['service_locator'] = ''
 
@@ -47,15 +55,17 @@ class plugin(PluginBaseClass.TestCaseRPC):
                     time.sleep(.5)
                     configArgs['config'] = '%s_%s' % (component, testData['cmd']['start']['mode'])
 
-                    res = testCaseRPC['cmd']('writeFile', '%s.conf' % component, self.configGen(configArgs))
+                    res = globalVars.rpcCmd[testData['remote_agent']]('writeFile', '!!TMP!!/%s.conf' % component, self.configGen(configArgs))
                     if res['success'] == False:
                         return res
+        
+                    self.cleanup('tempFile', '%s.conf' % component)
 
-                    res = testCaseRPC['cmd']('setEnvVar', 'libpath', '!!TMP!!/gitall/build/lib')
+                    res = globalVars.rpcCmd[testData['remote_agent']]('setEnvVar', 'libpath', '!!TMP!!/gitall/build/lib')
                     if res['success'] == False:
                         return res
 
-                    res = testCaseRPC['cmd']('run', 'asteriskscf_icebox', component, [iceBoxCmd[0], "%s%s.conf" % (iceBoxCmd[1], component)])
+                    res = globalVars.rpcCmd[testData['remote_agent']]('run', 'asteriskscf_icebox', component, [iceBoxCmd[0], "%s%s.conf" % (iceBoxCmd[1], component)])
                     if res['success'] == False:
                         return res
 
diff --git a/plugins/build.py b/plugins/build.py
index feb68ce..ab6e748 100644
--- a/plugins/build.py
+++ b/plugins/build.py
@@ -20,8 +20,10 @@ import datetime
 
 import Plugin
 
+
+
 class plugin(Plugin.BaseClass):
-    def main(self, testData, pluginData):
+    def main(self, testData):
         if not 'cmd' in testData or not 'hosts' in testData:
             return {'success':False,'msg':'Host(s) must be specified.'}
 
@@ -169,7 +171,6 @@ class plugin(Plugin.BaseClass):
                     if res['success'] == False:
                         return res
 
-                    print host
                     res = globalVars.rpcCmd[host]('remoteSendFile', '!!TMP!!/tmp.tar.gz', testData['cmd']['redistribute']['remotes'])
                     if res['success'] == False:
                         return res
diff --git a/plugins/failover.py b/plugins/failover.py
index 7b278d4..9e60e0b 100644
--- a/plugins/failover.py
+++ b/plugins/failover.py
@@ -10,12 +10,11 @@
 '''
 
 import time
-import TestSuite
 
-import yaml_parser
+import Plugin
 
-class plugin(TestSuite.BaseClass):
-    def main(self, testData, testPath, globalVars, pluginData):
+class plugin(Plugin.BaseClass):
+    def main(self, testData):
 
         for cmd in testData['cmd']:
 
diff --git a/plugins/protos.py b/plugins/protos.py
index f15ab98..66be021 100644
--- a/plugins/protos.py
+++ b/plugins/protos.py
@@ -10,10 +10,10 @@
 '''
 
 import time
-import TestSuite
+import Plugin
 
-class plugin(TestSuite.RemoteBaseClass):
-    def main(self, testData, testPath, globalVars, pluginData, remote):
+class plugin(Plugin.BaseClass):
+    def main(self, testData):
         if not 'cmd' in testData:
             return {'success':False,'msg':'No command specified.'}
 
diff --git a/plugins/sipp.py b/plugins/sipp.py
index 20d392b..7b63d5e 100644
--- a/plugins/sipp.py
+++ b/plugins/sipp.py
@@ -17,10 +17,10 @@ from GlobalVars import globalVars
 
 import time
 
-import PluginBaseClass
+import Plugin
 
-class plugin(PluginBaseClass.ManualRPC):
-    def main(self, testData, testPath, pluginData):
+class plugin(Plugin.BaseClass):
+    def main(self, testData):
         self.hosts = {}
 
         config = {}
@@ -39,38 +39,37 @@ class plugin(PluginBaseClass.ManualRPC):
             if not 'testsuite_remote_host' in testData:
                 return {'success':False,'msg':'No testsuite remote host specified.'}
 
-            remote = self.RPC().connect(globalVars, testData['testsuite_remote_host'])
+            remote = self.manualRPC(testData['testsuite_remote_host'])
             if remote['success'] == False:
                 return remote
 
             for mode in modes:
                 if mode in config:
                     if mode == 'uas':
-                        results = remote['rpc']('listProcesses')
-                        if results['success'] == False:
-                            return results
-
... 12416 lines suppressed ...


-- 
asterisk-scf/integration/testsuite.git



More information about the asterisk-scf-commits mailing list