2
# -*- mode: python; indent-tabs-mode: nil; -*-
3
# vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
5
# Copyright (C) 2010 Patrick Crews
7
# This program is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 2 of the License, or
10
# (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with this program; if not, write to the Free Software
19
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
code related to the execution of test cases
24
We are provided access to a testManager with
25
mode-specific testCases. We contact the executionManager
26
to produce the system and server configurations we need
37
""" class for handling the execution of testCase
38
objects. Mode-specific executors inherit
43
def __init__(self, execution_manager, name, verbose, debug):
44
self.skip_keys = [ 'execution_manager'
49
self.verbose = verbose
51
self.status = 0 # not running
52
self.execution_manager = execution_manager
53
self.system_manager = self.execution_manager.system_manager
54
self.testcase_repeat_count = self.execution_manager.testcase_repeat_count
55
self.cmd_prefix = self.system_manager.cmd_prefix
56
self.logging = self.system_manager.logging
57
self.test_manager = self.execution_manager.test_manager
58
self.server_manager = self.execution_manager.server_manager
59
self.time_manager = self.system_manager.time_manager
61
self.working_environment = {} # we pass env dict to define what we need
62
self.dirset = { self.name : { 'log': None } }
63
self.workdir = self.system_manager.create_dirset( self.system_manager.workdir
65
self.logdir = os.path.join(self.workdir,'log')
66
self.master_server = self.server_manager.allocate_server( self.name
71
self.record_flag=self.execution_manager.record_flag
72
self.current_servers = [self.master_server]
73
self.current_testcase = None
74
self.current_test_status = None
75
self.current_test_retcode = None
76
self.current_test_output = None
77
self.current_test_exec_time = 0
79
self.logging.debug_class(self)
81
def execute(self, start_and_exit):
82
""" Execute a test case. The details are *very* mode specific """
83
self.status = 1 # we are running
85
self.logging.verbose("Executor: %s beginning test execution..." %(self.name))
86
while self.test_manager.has_tests() and keep_running == 1:
88
for i in range(self.testcase_repeat_count):
90
self.handle_system_reqs()
91
self.handle_server_reqs()
92
self.handle_utility_reqs()
93
self.handle_start_and_exit(start_and_exit)
94
if self.current_test_status != 'fail':
95
self.execute_testCase()
96
self.record_test_result()
97
if self.current_test_status == 'fail' and not self.execution_manager.force:
98
self.logging.error("Failed test. Use --force to execute beyond the first test failure")
100
self.current_test_status = None # reset ourselves
103
def get_testCase(self):
104
""" Ask our execution_manager for a testCase to work on """
106
#self.test_manager.mutex.acquire()
107
self.current_testcase = self.test_manager.get_testCase(self.name)
108
#self.test_manager.mutex.release()
111
def handle_server_reqs(self):
112
""" Get the servers required to execute the testCase
113
and ensure that we have servers and they were started
114
as expected. We take necessary steps if not
115
We also handle --start-and-exit here
119
server_requirements = self.current_testcase.server_requirements
120
if server_requirements:
121
(self.current_servers,bad_start) = self.server_manager.request_servers( self.name
123
, self.current_testcase.cnf_path
124
, self.current_testcase.server_requests
125
, server_requirements
127
if self.current_servers == 0 or bad_start:
128
# error allocating servers, test is a failure
129
self.logging.warning("Problem starting server(s) for test...failing test case")
130
self.current_test_status = 'fail'
131
self.set_server_status(self.current_test_status)
135
self.current_servers[0].report()
136
self.master_server = self.current_servers[0]
137
if len(self.current_servers) > 1:
138
# We have a validation server or something we need to communicate with
139
# We export some env vars with EXECUTOR_SERVER and expect the randge
140
# code to know enough to look for this marker
142
for server in self.current_servers:
143
variable_name = "%s_%s" %(self.name.upper(), server.name.upper())
144
variable_value = str(server.master_port)
145
extra_reqs[variable_name] = variable_value
146
variable_name = variable_name + "_PID"
147
variable_value = str(server.pid)
148
extra_reqs[variable_name] = variable_value
149
self.working_environment.update(extra_reqs)
152
def handle_start_and_exit(self, start_and_exit):
153
""" Do what needs to be done if we have the
154
--start-and-exit flag
158
# We blow away any port_management files for our ports
159
# Technically this won't let us 'lock' any ports that
160
# we aren't explicitly using (visible to netstat scan)
161
# However one could argue that if we aren't using it,
162
# We shouldn't hog it ; )
163
# We might need to do this better later
164
for server in self.current_servers:
165
if server != self.master_server:
167
server.cleanup() # this only removes any port files
168
self.logging.info("User specified --start-and-exit. kewpie.py exiting and leaving servers running...")
171
def handle_utility_reqs(self):
172
""" Call any utilities we want to use before starting a test
173
At present this is envisioned for use with datagen
174
but there may be other things we wish to use
175
At that point, we may need to explore other ways of
176
defining our testing environment, such as with
177
nice config files / modules
181
# We call gendata against the server(s) with the
183
if self.execution_manager.gendata_file:
184
dsn = "--dsn=dbi:%s:host=127.0.0.1:port=%d:user=root:password="":database=test" %( self.master_server.type
185
, self.master_server.master_port)
186
gendata_cmd = "./gendata.pl %s --spec=%s" %( dsn
187
, self.execution_manager.gendata_file
189
#self.system_manager.execute_cmd(gendata_cmd)
190
gendata_subproc = subprocess.Popen( gendata_cmd
192
, cwd=self.system_manager.randgen_path
196
gendata_subproc.wait()
197
gendata_retcode = gendata_subproc.returncode
199
self.logging.error("gendata command: %s failed with retcode: %d" %(gendata_cmd
202
def execute_testCase(self):
203
""" Do whatever evil voodoo we must do to execute a testCase """
204
self.logging.verbose("Executor: %s executing test: %s" %(self.name, self.current_testcase.fullname))
206
def record_test_result(self):
207
""" We get the test_manager to record the result """
209
self.test_manager.record_test_result( self.current_testcase
210
, self.current_test_status
211
, self.current_test_output
212
, self.current_test_exec_time )
215
def set_server_status(self, test_status):
216
""" We update our servers to indicate if a test passed or failed """
217
for server in self.current_servers:
218
if test_status == 'fail':
219
server.failed_test = 1
222
def handle_system_reqs(self):
223
""" We check our test case and see what we need to do
224
system-wise to get ready. This is likely to be
225
mode-dependent and this is just a placeholder
230
self.process_environment_reqs()
231
self.process_symlink_reqs()
232
self.process_master_sh()
235
def process_master_sh(self):
236
""" We do what we need to if we have a master.sh file """
237
if self.current_testcase.master_sh:
238
retcode, output = self.system_manager.execute_cmd("/bin/sh %s" %(self.current_testcase.master_sh))
239
self.logging.debug("retcode: %retcode")
240
self.logging.debug("%output")
242
def process_environment_reqs(self):
243
""" We generate the ENV vars we need set
244
and then ask systemManager to do so
247
# We need to have a default set / file / whatever based
248
# on the dbqp config file / what we're using dbqp for
249
# will move this dict elsewhere to make this method more generic
251
# We're also doing the kludgy thing of basing env_reqs on
252
# the master_server type. At some point, we'll see about making
253
# this more flexible / extensible.
255
if self.master_server.type == 'drizzle':
256
env_reqs = { 'DRIZZLETEST_VARDIR': self.master_server.vardir
257
, 'DRIZZLE_TMP_DIR': self.master_server.tmpdir
258
, 'MASTER_MYSOCK': self.master_server.socket_file
259
, 'MASTER_MYPORT': str(self.master_server.master_port)
260
, 'MC_PORT': str(self.master_server.mc_port)
261
, 'PBMS_PORT': str(self.master_server.pbms_port)
262
, 'JSON_SERVER_PORT': str(self.master_server.json_server_port)
263
, 'RABBITMQ_NODE_PORT': str(self.master_server.rabbitmq_node_port)
264
, 'DRIZZLE_TCP_PORT': str(self.master_server.drizzle_tcp_port)
265
, 'EXE_DRIZZLE': self.master_server.drizzle_client
266
, 'MASTER_SERVER_SLAVE_CONFIG' : self.master_server.slave_config_file
267
, 'DRIZZLE_DUMP': "%s --no-defaults -uroot -p%d" %( self.master_server.drizzledump
268
, self.master_server.master_port)
269
, 'DRIZZLE_SLAP': "%s -uroot -p%d" %( self.master_server.drizzleslap
270
, self.master_server.master_port)
271
, 'DRIZZLE_IMPORT': "%s -uroot -p%d" %( self.master_server.drizzleimport
272
, self.master_server.master_port)
273
, 'DRIZZLE': "%s -uroot -p%d" %( self.master_server.drizzle_client
274
, self.master_server.master_port)
275
, 'DRIZZLE_BASEDIR' : self.system_manager.code_manager.code_trees['drizzle'][0].basedir
276
, 'DRIZZLE_TEST_WORKDIR' : self.system_manager.workdir
277
, 'SQLBENCH_DIR' : os.path.join( self.system_manager.testdir
280
elif self.master_server.type in ['mysql','percona','galera']:
281
env_reqs = { 'MYSQLTEST_VARDIR': self.master_server.vardir
282
, 'MYSQL_TMP_DIR': self.master_server.tmpdir
283
, 'MASTER_MYSOCK': self.master_server.socket_file
284
, 'MASTER_MYPORT': str(self.master_server.master_port)
285
, 'EXE_MYSQL': self.master_server.mysql_client
286
, 'MYSQL_DUMP': "%s --no-defaults -uroot -p%d" %( self.master_server.mysqldump
287
, self.master_server.master_port)
288
, 'MYSQL_SLAP': "%s -uroot -p%d" %( self.master_server.mysqlslap
289
, self.master_server.master_port)
290
, 'MYSQL_IMPORT': "%s -uroot -p%d" %( self.master_server.mysqlimport
291
, self.master_server.master_port)
292
, 'MYSQL_UPGRADE': "%s -uroot --datadir=%s" %( self.master_server.mysql_upgrade
293
, self.master_server.datadir)
294
, 'MYSQL': "%s -uroot -p%d" %( self.master_server.mysql_client
295
, self.master_server.master_port)
296
#, 'MYSQL_BASEDIR' : self.system_manager.code_manager.code_trees['mysql'][0].basedir
297
, 'MYSQL_TEST_WORKDIR' : self.system_manager.workdir
298
, 'SQLBENCH_DIR' : os.path.join( self.system_manager.testdir
302
self.working_environment = self.system_manager.env_manager.create_working_environment(env_reqs)
304
def process_symlink_reqs(self):
305
""" Create any symlinks we may need """
308
self.system_manager.create_symlinks(needed_symlinks)