corolab.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. #!/usr/bin/python
  2. '''CTS: Cluster Testing System: Lab environment module
  3. '''
  4. from __future__ import print_function
  5. __copyright__='''
  6. Copyright (c) 2010 Red Hat, Inc.
  7. '''
  8. # All rights reserved.
  9. #
  10. # Author: Angus Salkeld <asalkeld@redhat.com>
  11. #
  12. # This software licensed under BSD license, the text of which follows:
  13. #
  14. # Redistribution and use in source and binary forms, with or without
  15. # modification, are permitted provided that the following conditions are met:
  16. #
  17. # - Redistributions of source code must retain the above copyright notice,
  18. # this list of conditions and the following disclaimer.
  19. # - Redistributions in binary form must reproduce the above copyright notice,
  20. # this list of conditions and the following disclaimer in the documentation
  21. # and/or other materials provided with the distribution.
  22. # - Neither the name of the MontaVista Software, Inc. nor the names of its
  23. # contributors may be used to endorse or promote products derived from this
  24. # software without specific prior written permission.
  25. #
  26. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  30. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  32. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  33. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  34. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  36. # THE POSSIBILITY OF SUCH DAMAGE.
  37. import sys
  38. from cts.CTSscenarios import *
  39. from cts.logging import *
  40. from corotests import CoroTestList
  41. from corosync import *
  42. sys.path.append("/usr/share/pacemaker/tests/cts") # So that things work from the source directory
  43. try:
  44. from CTSlab import *
  45. except ImportError:
  46. sys.stderr.write("abort: couldn't find CTSLab in [%s]\n" %
  47. ' '.join(sys.path))
  48. sys.stderr.write("(check your install and PYTHONPATH)\n")
  49. sys.exit(-1)
  50. tests = None
  51. cm = None
  52. old_handler = None
  53. DefaultFacility = "daemon"
  54. def usage(arg):
  55. print("Illegal argument " + arg)
  56. print("usage: " + sys.argv[0] +" [options] number-of-iterations")
  57. print("\nCommon options: ")
  58. print("\t [--at-boot (1|0)], does the cluster software start at boot time")
  59. print("\t [--nodes 'node list'], list of cluster nodes separated by whitespace")
  60. print("\t [--limit-nodes max], only use the first 'max' cluster nodes supplied with --nodes")
  61. print("\t [--logfile path], where should the test software look for logs from cluster nodes")
  62. print("\t [--rrp-bindaddr addr], extra interface used for rrp, provide the bindaddr")
  63. print("\t [--outputfile path], optional location for the test software to write logs to")
  64. print("\t [--syslog-facility name], which syslog facility should the test software log to")
  65. print("\t [--choose testcase-name], run only the named test")
  66. print("\t [--list-tests], list the valid tests")
  67. print("\t [--benchmark], add the timing information")
  68. print("\t ")
  69. print("Additional (less common) options: ")
  70. print("\t [--trunc (truncate logfile before starting)]")
  71. print("\t [--xmit-loss lost-rate(0.0-1.0)]")
  72. print("\t [--recv-loss lost-rate(0.0-1.0)]")
  73. print("\t [--standby (1 | 0 | yes | no)]")
  74. print("\t [--fencing (1 | 0 | yes | no)]")
  75. print("\t [--once], run all valid tests once")
  76. print("\t [--no-loop-tests], dont run looping/time-based tests")
  77. print("\t [--no-unsafe-tests], dont run tests that are unsafe for use with ocfs2/drbd")
  78. print("\t [--valgrind-tests], include tests using valgrind")
  79. print("\t [--experimental-tests], include experimental tests")
  80. print("\t [--oprofile 'node list'], list of cluster nodes to run oprofile on]")
  81. print("\t [--qarsh] Use the QARSH backdoor to access nodes instead of SSH")
  82. print("\t [--seed random_seed]")
  83. print("\t [--set option=value]")
  84. sys.exit(1)
  85. class CoroLabEnvironment(CtsLab):
  86. def __init__(self):
  87. CtsLab.__init__(self)
  88. # Get a random seed for the random number generator.
  89. self["DoStonith"] = 0
  90. self["DoStandby"] = 0
  91. self["DoFencing"] = 0
  92. self["XmitLoss"] = "0.0"
  93. self["RecvLoss"] = "0.0"
  94. self["IPBase"] = "127.0.0.10"
  95. self["ClobberCIB"] = 0
  96. self["CIBfilename"] = None
  97. self["CIBResource"] = 0
  98. self["DoBSC"] = 0
  99. self["use_logd"] = 0
  100. self["oprofile"] = []
  101. self["RrpBindAddr"] = None
  102. self["warn-inactive"] = 0
  103. self["ListTests"] = 0
  104. self["benchmark"] = 0
  105. self["logrestartcmd"] = "systemctl restart rsyslog.service 2>&1 > /dev/null"
  106. self["syslogd"] ="rsyslog"
  107. self["Schema"] = "corosync 2.0"
  108. self["Stack"] = "corosync"
  109. self['CMclass'] = corosync_needle
  110. self["stonith-type"] = "external/ssh"
  111. self["stonith-params"] = "hostlist=all,livedangerously=yes"
  112. self["at-boot"] = 0 # Does the cluster software start automatically when the node boot
  113. self["logger"] = ([StdErrLog(self, 'corosync.log')])
  114. self["loop-minutes"] = 60
  115. self["valgrind-prefix"] = None
  116. self["valgrind-procs"] = "corosync"
  117. self["valgrind-opts"] = """--leak-check=full --show-reachable=yes --trace-children=no --num-callers=25 --gen-suppressions=all --suppressions="""+CTSvars.CTS_home+"""/cts.supp"""
  118. self["experimental-tests"] = 0
  119. self["valgrind-tests"] = 0
  120. self["unsafe-tests"] = 0
  121. self["loop-tests"] = 0
  122. self["all-once"] = 0
  123. self["LogWatcher"] = "remote"
  124. self["SyslogFacility"] = DefaultFacility
  125. self["stats"] = 0
  126. self.log = LogFactory().log
  127. #
  128. # Main entry into the test system.
  129. #
  130. if __name__ == '__main__':
  131. Environment = CoroLabEnvironment()
  132. NumIter = 0
  133. Version = 1
  134. LimitNodes = 0
  135. TestCase = None
  136. TruncateLog = 0
  137. ListTests = 0
  138. HaveSeed = 0
  139. node_list = ''
  140. #
  141. # The values of the rest of the parameters are now properly derived from
  142. # the configuration files.
  143. #
  144. # Set the signal handler
  145. signal.signal(15, sig_handler)
  146. signal.signal(10, sig_handler)
  147. # Process arguments...
  148. skipthis=None
  149. args=sys.argv[1:]
  150. for i in range(0, len(args)):
  151. if skipthis:
  152. skipthis=None
  153. continue
  154. elif args[i] == "-l" or args[i] == "--limit-nodes":
  155. skipthis=1
  156. LimitNodes = int(args[i+1])
  157. elif args[i] == "-L" or args[i] == "--logfile":
  158. skipthis=1
  159. Environment["LogFileName"] = args[i+1]
  160. elif args[i] == "--outputfile":
  161. skipthis=1
  162. Environment["OutputFile"] = args[i+1]
  163. elif args[i] == "--rrp-bindaddr":
  164. skipthis=1
  165. Environment["RrpBindAddr"] = args[i+1]
  166. elif args[i] == "--oprofile":
  167. skipthis=1
  168. Environment["oprofile"] = args[i+1].split(' ')
  169. elif args[i] == "--trunc":
  170. Environment["TruncateLog"]=1
  171. elif args[i] == "--list-tests":
  172. Environment["ListTests"]=1
  173. elif args[i] == "--benchmark":
  174. Environment["benchmark"]=1
  175. elif args[i] == "--qarsh":
  176. Environment.rsh.enable_qarsh()
  177. elif args[i] == "--fencing":
  178. skipthis=1
  179. if args[i+1] == "1" or args[i+1] == "yes":
  180. Environment["DoFencing"] = 1
  181. elif args[i+1] == "0" or args[i+1] == "no":
  182. Environment["DoFencing"] = 0
  183. else:
  184. usage(args[i+1])
  185. elif args[i] == "--xmit-loss":
  186. try:
  187. float(args[i+1])
  188. except ValueError:
  189. print ("--xmit-loss parameter should be float")
  190. usage(args[i+1])
  191. skipthis=1
  192. Environment["XmitLoss"] = args[i+1]
  193. elif args[i] == "--recv-loss":
  194. try:
  195. float(args[i+1])
  196. except ValueError:
  197. print ("--recv-loss parameter should be float")
  198. usage(args[i+1])
  199. skipthis=1
  200. Environment["RecvLoss"] = args[i+1]
  201. elif args[i] == "--choose":
  202. skipthis=1
  203. TestCase = args[i+1]
  204. elif args[i] == "--nodes":
  205. skipthis=1
  206. node_list = args[i+1].split(' ')
  207. elif args[i] == "--at-boot" or args[i] == "--cluster-starts-at-boot":
  208. skipthis=1
  209. if args[i+1] == "1" or args[i+1] == "yes":
  210. Environment["at-boot"] = 1
  211. elif args[i+1] == "0" or args[i+1] == "no":
  212. Environment["at-boot"] = 0
  213. else:
  214. usage(args[i+1])
  215. elif args[i] == "--set":
  216. skipthis=1
  217. (name, value) = args[i+1].split('=')
  218. Environment[name] = value
  219. elif args[i] == "--once":
  220. skipthis=1
  221. Environment["all-once"]=1
  222. elif args[i] == "--stack":
  223. skipthis=1
  224. Environment["Stack"] = args[i+1]
  225. else:
  226. try:
  227. NumIter=int(args[i])
  228. except ValueError:
  229. usage(args[i])
  230. if Environment["OutputFile"]:
  231. Environment["logger"].append(FileLog(Environment, Environment["OutputFile"]))
  232. if len(node_list) < 1:
  233. print("No nodes specified!")
  234. sys.exit(1)
  235. if LimitNodes > 0:
  236. if len(node_list) > LimitNodes:
  237. print("Limiting the number of nodes configured=%d (max=%d)"
  238. %(len(node_list), LimitNodes))
  239. while len(node_list) > LimitNodes:
  240. node_list.pop(len(node_list)-1)
  241. Environment["nodes"] = node_list
  242. # Create the Cluster Manager object
  243. cm = Environment['CMclass'](Environment)
  244. Audits = CoroAuditList(cm)
  245. if Environment["ListTests"] == 1 :
  246. Tests = CoroTestList(cm, Audits)
  247. Environment.log("Total %d tests"%len(Tests))
  248. for test in Tests :
  249. Environment.log(str(test.name));
  250. sys.exit(0)
  251. if TruncateLog:
  252. Environment.log("Truncating %s" % LogFile)
  253. lf = open(LogFile, "w");
  254. if lf != None:
  255. lf.truncate(0)
  256. lf.close()
  257. if TestCase != None:
  258. for test in CoroTestList(cm, Audits):
  259. if test.name == TestCase:
  260. Tests.append(test)
  261. if Tests == []:
  262. usage("--choose: No applicable/valid tests chosen")
  263. else:
  264. Tests = CoroTestList(cm, Audits)
  265. # Scenario selection
  266. if Environment["DoBSC"]:
  267. scenario = RandomTests(cm, [ BasicSanityCheck(Environment) ], Audits, Tests)
  268. elif Environment["all-once"] or NumIter == 0:
  269. NumIter = len(Tests)
  270. scenario = AllOnce(
  271. cm, [ BootCluster(Environment), TestAgentComponent(Environment), PacketLoss(Environment) ], Audits, Tests)
  272. else:
  273. scenario = RandomTests(
  274. cm, [ BootCluster(Environment), TestAgentComponent(Environment), PacketLoss(Environment) ], Audits, Tests)
  275. Environment.log(">>>>>>>>>>>>>>>> BEGINNING " + repr(NumIter) + " TESTS ")
  276. Environment.log("Stack: %s" % Environment["Stack"])
  277. Environment.log("Schema: %s" % Environment["Schema"])
  278. Environment.log("Scenario: %s" % scenario.__doc__)
  279. Environment.log("Random Seed: %s" % Environment["RandSeed"])
  280. Environment.log("System log files: %s" % Environment["LogFileName"])
  281. Environment.dump()
  282. rc = Environment.run(scenario, NumIter)
  283. sys.exit(rc)