[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
Thu May 26 13:04:31 CDT 2011
branch "review" has been updated
via 186633752f88b475d0c59eb5bc9dd9e19d388def (commit)
from 2397333b2869f584c86fc7b96acb44ca580c53e5 (commit)
Summary of changes:
lib/python/TestSuite.py | 91 +++++++++++++++++++++++
lib/python/util.py | 188 -----------------------------------------------
plugins/failover.py | 47 ++++++------
testsuite.py | 117 +++++++++++++++++++++++++----
4 files changed, 216 insertions(+), 227 deletions(-)
create mode 100644 lib/python/TestSuite.py
delete mode 100644 lib/python/util.py
- Log -----------------------------------------------------------------
commit 186633752f88b475d0c59eb5bc9dd9e19d388def
Author: Darren Sessions <dsessions at digium.com>
Date: Thu May 26 13:04:23 2011 -0500
did some cleanup with the utility functions and incorporated them into the base classes being used by the plugins via a super class. added some checks in the failover code to make sure the shared ip address specified in the configs isnt already in use on the network.
diff --git a/lib/python/TestSuite.py b/lib/python/TestSuite.py
new file mode 100644
index 0000000..c6322cb
--- /dev/null
+++ b/lib/python/TestSuite.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+'''
+
+ Test-Suite Support Classes
+
+ Asterisk SCF Test-Suite
+ Copyright (C) 2011, Digium, Inc.
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License Version 2.
+
+'''
+
+import sys
+import xmlrpclib
+import subprocess
+
+from urlparse import urlparse
+
+class utils():
+ def ping(self, host):
+ process = subprocess.Popen(
+ 'ping -c1 %s' % host,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ shell=True)
+ process.wait()
+ if process.returncode == 0:
+ return True
+ else:
+ return False
+
+ def rpc(self, host):
+ rpc = xmlrpclib.Server('http://%s:8000' % host)
+ try:
+ rpc.reset()
+ except:
+ return {'success':False,'msg':'Connection to %s was refused.' % host}
+ return {'success':True,'rpc':rpc}
+
+ class file:
+ def write(self, component):
+ try:
+ f = open("%s/temp_config_%s.conf" % (self._cwd, component), "w")
+ except:
+ return "Unexpected error: %s" % sys.exc_info()
+ f.write(config)
+ f.close()
+ self._results['file_cleanup'].append('%s/temp_config_%s.conf' % (self._cwd, component))
+
+ def read(self, fn):
+ try:
+ f = open("%s" % fn, "r")
+ except IOError:
+ print "Failed to open test results output file:"
+ except:
+ print "Unexpected error: %s" % sys.exc_info()[0]
+ else:
+ fc = f.read()
+ f.close()
+ return fc.strip()
+
+ class url:
+ def fetchRepoInfo(self, url):
+ repoName = ''
+ url = urlparse(url)
+ if not url[0] == 'git' and not url[0] == 'svn':
+ return {'success':False,'msg':'The URL must be a properly formatted git or svn url.'}
+ for part in url[2].split('/'):
+ repoName = part
+ return {'success':True, 'repoName':repoName, 'repoType':'%s' % url[0]}
+
+class BaseClass(utils):
+ def run(self, testData, testPath):
+ if not 'cmd' in testData:
+ return {'success':False,'msg':'No command specified.'}
+ return self.main(testData, testPath)
+
+class RemoteBaseClass(utils):
+ def run(self, testData, testPath):
+ if not 'cmd' in testData:
+ return {'success':False,'msg':'No command specified.'}
+ if not 'testsuite_remote' in testData:
+ return {'success':False,'msg':'No testsuite remote specified.'}
+ rpc = xmlrpclib.Server('http://%s:8000' % testData['testsuite_remote'])
+ try:
+ rpc.reset()
+ except:
+ return {'success':False,'msg':'Connection to %s was refused.' % testData['testsuite_remote']}
+ return self.main(testData, testPath, rpc)
diff --git a/lib/python/util.py b/lib/python/util.py
deleted file mode 100644
index 2a7a170..0000000
--- a/lib/python/util.py
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/usr/bin/env python
-'''
-
- Utility Classes
-
- Asterisk SCF Test-Suite
- Copyright (C) 2011, Digium, Inc.
-
- This program is free software, distributed under the terms of
- the GNU General Public License Version 2.
-
-'''
-
-import os
-import imp
-import sys
-import time
-import inspect
-import xmlrpclib
-
-from xml.dom import minidom
-from xml.etree.ElementTree import Element, SubElement, tostring
-from urlparse import urlparse
-
-class TestSuiteBaseClass:
- def run(self, testData, testPath):
- if not 'cmd' in testData:
- return {'success':False,'msg':'No command specified.'}
- return self.main(testData, testPath)
-
- def rpc(self, host):
- rpc = xmlrpclib.Server('http://%s:8000' % host)
- try:
- rpc.reset()
- except:
- return {'success':False,'msg':'Connection to %s was refused.' % host}
- return {'success':True,'rpc':rpc}
-
-class TestSuiteRemoteBaseClass:
- def run(self, testData, testPath):
- if not 'cmd' in testData:
- return {'success':False,'msg':'No command specified.'}
- if not 'testsuite_remote' in testData:
- return {'success':False,'msg':'No testsuite remote specified.'}
- rpc = xmlrpclib.Server('http://%s:8000' % testData['testsuite_remote'])
- try:
- rpc.reset()
- except:
- return {'success':False,'msg':'Connection to %s was refused.' % testData['testsuite_remote']}
- return self.main(testData, testPath, rpc)
-
-class url:
- def fetchRepoInfo(self, url):
- repoName = ''
- url = urlparse(url)
- if not url[0] == 'git' and not url[0] == 'svn':
- return {'success':False,'msg':'The URL must be a properly formatted git or svn url.'}
- for part in url[2].split('/'):
- repoName = part
- return {'success':True, 'repoName':repoName, 'repoType':'%s' % url[0]}
-
-class plugins:
- def execute(self, name, module, testData, testPath):
- try:
- func = getattr(module, 'testsuite')
- except AttributeError:
- return {'success':False,'msg':"Unable to execute module '%s'. %s" % (name, sys.exc_info()[1])}
- else:
- results = func().run(testData, testPath)
- if not type(results) == dict:
- return {'success':False,'msg':"The '%s' module return invalid or missing data." % name}
- return results
-
- def init(self, name):
- loadResults = self.load(name)
- if not loadResults['success'] == True:
- return loadResults
- checkResults = self.check(name, loadResults['module'])
- if not checkResults['success'] == True:
- return checkResults
- return loadResults
-
- def load(self, name):
- try:
- return {'success':True,'module':sys.modules[name]}
- except KeyError:
- pass
- try:
- fp, pathName, desc = imp.find_module(name)
- except ImportError:
- return {'success':False,'msg':'%s' % sys.exc_info()[1]}
- try:
- return {'success':True,'module':imp.load_module(name, fp, pathName, desc)}
- finally:
- if fp:
- fp.close()
-
- def check(self, name, module):
- try:
- inspect.ismethod(module.testsuite().main)
- except AttributeError:
- return {'success':False,'msg':'The required "main" method in the "testsuite" class is not implemented in the "%s" plugin.' % name}
- except:
- return {'success':False,'msg':'The "main" method in the "testsuite" class in "%s" is not responding properly. %s.' % (name, sys.exc_info())}
-
- argCheck = inspect.getargspec(module.testsuite().main)
- if argCheck[0] != ['self','testData', 'testPath'] and argCheck[0] != ['self','testData', 'testPath', 'rpc']:
- return {'success':False,'msg':"The 'main' method in the 'testsuite' class within '%s' has improperly labeled args! i.e. def main(self, testData, testPath)." % name}
-
- return {'success':True}
-
-class file:
- def write(self, component):
- try:
- f = open("%s/temp_config_%s.conf" % (self._cwd, component), "w")
- except:
- return "Unexpected error: %s" % sys.exc_info()
- f.write(config)
- f.close()
- self._results['file_cleanup'].append('%s/temp_config_%s.conf' % (self._cwd, component))
-
- def read(self, fn):
- try:
- f = open("%s" % fn, "r")
- except IOError:
- print "Failed to open test results output file:"
- except:
- print "Unexpected error: %s" % sys.exc_info()[0]
- else:
- fc = f.read()
- f.close()
- return fc.strip()
-
-class general:
- def flattenDict(self, d):
- s = [d]
- while s:
- e = s[-1]
- for k, v in e.items():
- if isinstance(v, dict):
- s.append(v)
- else:
- yield k, v
- s.remove(e)
-
- def logFailure(self, x, errorMsgs, name):
- print ' |- %s .. FAILED' % (name)
- print " \ - %s" % '\n | - '.join(errorMsgs)
- return x, []
-
- def fatal(self, msg):
- return {'success':False,'execute':False,'errorMsg':['%s' % msg]}
-
- def status(self, status):
- if status == False: return("FAILED!!")
- elif status == True: return("succeeded!")
-
-class xml:
- def initTestSuite(self):
- return Element('testsuites')
-
- def prettyXml(self, x):
- rough_string = tostring(x, 'utf-8')
- reparsed = minidom.parseString(rough_string)
- return reparsed.toprettyxml(indent=" ")
-
- def convToString(self, x):
- return tostring(x)
-
- def addTestType(self, element, name, type):
- subElement = SubElement(element, type)
- subElement.set('name', name)
- return element, subElement
-
- def addTestSuite(self, element, name):
- return self.addTestType(element, name, 'testsuite')
-
- def addTestCase(self, element, name):
- return self.addTestType(element, name, 'testcase')
-
- def addFailure(self, element, msgs):
- subElement = SubElement(element, 'failure')
- subElement.text = unicode(' '.join(msgs))
- return element, []
-
- def setProperty(self, element, key, val):
- element.set(key, '%s' % val)
- return element
diff --git a/plugins/failover.py b/plugins/failover.py
index c1da885..0498b2b 100644
--- a/plugins/failover.py
+++ b/plugins/failover.py
@@ -9,10 +9,10 @@
'''
-import util
import time
+import TestSuite
-class testsuite(util.TestSuiteBaseClass):
+class plugin(TestSuite.BaseClass):
def main(self, testData, testPath):
corosyncExec = ['corosync','-f']
pacemakerExec = ['pacemaker']
@@ -23,26 +23,28 @@ class testsuite(util.TestSuiteBaseClass):
standbyNode = {}
if not 'testsuite_remote_active' in testData['cmd']:
- return {'success':False,'msg':'No testsuite-remote specified for the active failover configuration.'}
+ return {'success':False,'msg':'No testsuite-remote specified for the active failover configuration.','shutdownExempt':True}
if not 'testsuite_remote_standby' in testData['cmd']:
- return {'success':False,'msg':'No testsuite-remote specified for the standby failover configuration.'}
+ return {'success':False,'msg':'No testsuite-remote specified for the standby failover configuration.','shutdownExempt':True}
if not 'ipv4oripv6' in testData['cmd']:
- return {'success':False,'msg':'No ip version specified.'}
+ return {'success':False,'msg':'No ip version specified.','shutdownExempt':True}
else:
if not testData['cmd']['ipv4oripv6'] == 'ipv4' and not testData['cmd']['ipv4oripv6'] == 'ipv6':
- return {'success':False,'msg':'IP version must be ipv4 or ipv6.'}
+ return {'success':False,'msg':'IP version must be ipv4 or ipv6.','shutdownExempt':True}
else:
ipv = testData['cmd']['ipv4oripv6']
if not 'shared_ip' in testData['cmd']:
- return {'success':False,'msg':'No shared IP address specified.'}
+ return {'success':False,'msg':'No shared IP address specified.','shutdownExempt':True}
else:
sharedIP = testData['cmd']['shared_ip']
+ if self.ping(sharedIP):
+ return {'success':False, 'msg':'The %s shared ip address is responding before cluster services have been started.' % sharedIP, 'shutdownList':shutdownList}
if ipv and not 'ipv4_network_addr' in testData['cmd']:
- return {'success':False,'msg':'The ipv4_network_addr must be specified on IPv4 networks.'}
+ return {'success':False,'msg':'The ipv4_network_addr must be specified on IPv4 networks.','shutdownExempt':True}
else:
if ipv == 'ipv4':
activeNode['nAddr'] = testData['cmd']['ipv4_network_addr']
@@ -81,27 +83,18 @@ class testsuite(util.TestSuiteBaseClass):
if not results['success'] == True:
return self._addShutdownList(results, shutdownList)
- time.sleep(100)
-
- activeNode['rpc'].writeFile('pacemaker.cli', '\n'.join(self._pacemakerConfig(testData['cmd']['shared_ip'], testData['cmd']['testsuite_remote_active'])))
+ activeNode['rpc'].writeFile('pacemaker.cli', '\n'.join(self._pacemakerConfig(sharedIP, testData['cmd']['testsuite_remote_active'])))
if not results['success'] == True:
return self._addShutdownList(results, shutdownList)
results = activeNode['rpc'].run('failover', 'crm', ['crm', '-f', '!!TMP!!/pacemaker.cli'], True)
-
-# results = activeNode['rpc'].run('failover', 'cibadmin', ['cibadmin', '-E', '--force'], True)
-# if not results['success'] == True:
-# return self._addShutdownList(results, shutdownList)
-# results = standbyNode['rpc'].run('failover', 'cibadmin', ['cibadmin', '-E', '--force'], True)
-# if not results['success'] == True:
-# return self._addShutdownList(results, shutdownList)
time.sleep(100)
-
+ if not self.ping(sharedIP):
+ return {'success':False, 'msg':'The %s shared ip address is failing to respond.' % sharedIP, 'shutdownList':shutdownList}
return {'success':True,'shutdownList':shutdownList}
-
def _addShutdownList(self, results, shutdownList):
results['shutdownList'] = shutdownList
return results
@@ -111,6 +104,12 @@ class testsuite(util.TestSuiteBaseClass):
'compatibility: whitetank',
'totem {',
' version: 2',
+ ' token: 160',
+ ' token_retransmits_before_loss_const: 3',
+ ' join: 30',
+ ' consensus: 300',
+ ' max_messages: 20',
+ ' threads: 0',
' secauth: off',
' nodeid: %s' % nodeID,
' interface {',
@@ -145,7 +144,7 @@ class testsuite(util.TestSuiteBaseClass):
' to_logfile: no',
' to_syslog: yes',
' syslog_facility: daemon',
- ' debug: off',
+ ' debug: on',
' timestamp: on',
' logger_subsys {',
' subsys: AMF',
@@ -157,16 +156,18 @@ class testsuite(util.TestSuiteBaseClass):
def _pacemakerConfig(self, sharedIP, activeHost):
config = [
+ 'resource stop failover-ip',
'configure',
'erase',
'commit',
- 'primitive failover-ip ocf:heartbeat:IPaddr params ip="%s" op monitor interval=1s meta is-managed="true" target-role="Started"' % sharedIP,
- 'location cli-prefer-failover-ip failover-ip rule $id="cli-prefer-rule-failover-ip" inf: #uname eq %s' % activeHost,
'property cluster-infrastructure="openais"',
'property expected-quorum-votes="2"',
'property stonith-enabled=false',
'property no-quorum-policy="ignore"',
'property default-resource-stickiness="infinity"',
+ 'commit',
+ 'primitive failover-ip ocf:heartbeat:IPaddr params ip="%s" op monitor interval=1s meta is-managed="true" target-role="Started"' % sharedIP,
+ 'location cli-prefer-failover-ip failover-ip rule $id="cli-prefer-rule-failover-ip" inf: #uname eq %s' % activeHost,
'commit']
return config
diff --git a/testsuite.py b/testsuite.py
index a235426..c590046 100755
--- a/testsuite.py
+++ b/testsuite.py
@@ -11,15 +11,19 @@
'''
import os
+import imp
import sys
+import inspect
import platform
import xmlrpclib
+from xml.dom import minidom
+from xml.etree.ElementTree import Element, SubElement, tostring
+
homeDir = os.path.dirname(os.path.realpath(__file__))
sys.path.append("%s/lib/python" % homeDir)
sys.path.append("%s/plugins" % homeDir)
-import util
import yaml_parser
class __main__:
@@ -32,48 +36,48 @@ class __main__:
pluginData = {}
shutdownList = []
- x = util.xml().initTestSuite()
+ x = xml().initTestSuite()
for list in self.yamlData:
for testCategory in list:
- x, categoryElement = util.xml().addTestSuite(x, testCategory)
+ x, categoryElement = xml().addTestSuite(x, testCategory)
print "\n Starting '%s' tests . . \n ---------------------------------" % testCategory
for testData in list[testCategory]:
- categoryElement, testElement = util.xml().addTestSuite(categoryElement, testData['name'])
+ categoryElement, testElement = xml().addTestSuite(categoryElement, testData['name'])
print ' |- Testcase "%s" ' % testData['name']
#print testData['options']
for subTestCase in testData['tests']:
for testName in subTestCase:
- testElement, subTestElement = util.xml().addTestCase(testElement, testName)
+ testElement, subTestElement = xml().addTestCase(testElement, testName)
if subTestCase[testName]:
success = True
if 'expected_failure' in subTestCase[testName]:
if subTestCase[testName]['expected_failure']:
- subTestElement = util.xml().setProperty(subTestElement, 'expected_failure', subTestCase[testName]['expected_failure'])
+ subTestElement = xml().setProperty(subTestElement, 'expected_failure', subTestCase[testName]['expected_failure'])
#print subTestCase[testName]['expected_failure']
''' import plugins '''
for plugin in [plugin for timeLine in subTestCase[testName]['timeline'] for plugin in timeLine]:
- pluginData[plugin] = util.plugins().init(plugin)
+ pluginData[plugin] = plugins().init(plugin)
if not pluginData[plugin]['success'] == True:
errorMsgs.append(pluginData[plugin]['msg'])
success = False
if success == False:
print ' |- Test "' + testName + '" - FAILED!\n \- ' + '\n'.join(errorMsgs)
- subTestElement, errorMsgs = util.xml().addFailure(subTestElement, errorMsgs)
+ subTestElement, errorMsgs = xml().addFailure(subTestElement, errorMsgs)
break
''' execute testcase timeline '''
for timeLine in subTestCase[testName]['timeline']:
for plugin in timeLine:
- runResults = util.plugins().execute(plugin, pluginData[plugin]['module'], timeLine[plugin], testData['path'])
+ runResults = plugins().execute(plugin, pluginData[plugin]['module'], timeLine[plugin], testData['path'])
if not 'shutdownList' in runResults:
if not 'shutdownExempt' in runResults:
- shutdownList.append({testData['testsuite_remote']:plugin})
+ shutdownList.append({testData['testsuite_remote']:plugin})
else:
for remote in runResults['shutdownList']:
shutdownList.append({remote:plugin})
@@ -83,27 +87,108 @@ class __main__:
runResults['msg'] = '%s: %s' % (plugin, runResults['msg'])
break
- print shutdownList
''' shutdown if 'restart_persistant_plugins_between_tests' is true or not specified '''
for d in shutdownList:
for host in d:
remote = xmlrpclib.Server('http://%s:8000' % host)
- remote.shutdown(d[host])
+ try:
+ remote.shutdown(d[host])
+ except:
+ pass
if runResults['success'] == False:
print ' |- Test "' + testName + '" - FAILED!\n \- ' + runResults['msg']
- subTestElement, errorMsgs = util.xml().addFailure(subTestElement, ['%s' % runResults['msg']])
+ subTestElement, errorMsgs = xml().addFailure(subTestElement, ['%s' % runResults['msg']])
break
print ' |- Test "' + testName + '" - PASSED!'
# setProperty(self, element, key, val): for xml results
else:
print ' |- Test "' + testName + '" - FAILED!\n \- No test data defined!'
- subTestElement, errorMsgs = util.xml().addFailure(subTestElement, ['No test data defined!'])
+ subTestElement, errorMsgs = xml().addFailure(subTestElement, ['No test data defined!'])
break
#print xml().convToString(x)
- print "\n\n" + util.xml().prettyXml(x)
+ print "\n\n" + xml().prettyXml(x)
return
-__main__()
+class plugins:
+ def execute(self, name, module, testData, testPath):
+ try:
+ func = getattr(module, 'plugin')
+ except AttributeError:
+ return {'success':False,'msg':"Unable to execute module '%s'. %s" % (name, sys.exc_info()[1])}
+ else:
+ results = func().run(testData, testPath)
+ if not type(results) == dict:
+ return {'success':False,'msg':"The '%s' module return invalid or missing data." % name}
+ return results
+
+ def init(self, name):
+ loadResults = self.load(name)
+ if not loadResults['success'] == True:
+ return loadResults
+ checkResults = self.check(name, loadResults['module'])
+ if not checkResults['success'] == True:
+ return checkResults
+ return loadResults
+
+ def load(self, name):
+ try:
+ return {'success':True,'module':sys.modules[name]}
+ except KeyError:
+ pass
+ try:
+ fp, pathName, desc = imp.find_module(name)
+ except ImportError:
+ return {'success':False,'msg':'%s' % sys.exc_info()[1]}
+ try:
+ return {'success':True,'module':imp.load_module(name, fp, pathName, desc)}
+ finally:
+ if fp:
+ fp.close()
+
+ def check(self, name, module):
+ try:
+ inspect.ismethod(module.plugin().main)
+ except AttributeError:
+ return {'success':False,'msg':'The required "main" method in the "plugin" class is not implemented in the "%s" plugin.' % name}
+ except:
+ return {'success':False,'msg':'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', 'testPath'] and argCheck[0] != ['self','testData', 'testPath', 'rpc']:
+ return {'success':False,'msg':"The 'main' method in the 'plugin' class within '%s' has improperly labeled args! i.e. def main(self, testData, testPath)." % name}
+ return {'success':True}
+
+class xml:
+ def initTestSuite(self):
+ return Element('testsuites')
+
+ def prettyXml(self, x):
+ rough_string = tostring(x, 'utf-8')
+ reparsed = minidom.parseString(rough_string)
+ return reparsed.toprettyxml(indent=" ")
+
+ def convToString(self, x):
+ return tostring(x)
+
+ def addTestType(self, element, name, type):
+ subElement = SubElement(element, type)
+ subElement.set('name', name)
+ return element, subElement
+
+ def addTestSuite(self, element, name):
+ return self.addTestType(element, name, 'testsuite')
+
+ def addTestCase(self, element, name):
+ return self.addTestType(element, name, 'testcase')
+
+ def addFailure(self, element, msgs):
+ subElement = SubElement(element, 'failure')
+ subElement.text = unicode(' '.join(msgs))
+ return element, []
+
+ def setProperty(self, element, key, val):
+ element.set(key, '%s' % val)
+ return element
+__main__()
-----------------------------------------------------------------------
--
asterisk-scf/integration/testsuite.git
More information about the asterisk-scf-commits
mailing list