~drizzle-trunk/drizzle/development

2144.1.1 by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control
1
#! /usr/bin/env python
2235.5.2 by Stewart Smith
dbqp source (and all its libs) should use emacs python mode, not emacs C mode
2
# -*- mode: python; indent-tabs-mode: nil; -*-
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
3
# vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
4
#
5
# Copyright (C) 2010 Patrick Crews
6
#
2121.3.2 by patrick crews
Updated license verbiage
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.
11
#
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.
16
#
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
2121.3.1 by patrick crews
Added licensing text to dbqp files
20
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
21
"""system_management.py
22
   code for dealing with system-level 'stuff'.
23
   This includes setting environment variables, looking for clients,
24
   so on and so forth.
25
26
   These things are / should be constant regardless of the testing being done
27
   We do an initial preflight / setup, we then call the mode-specific
28
   system_initialise() to do whatever the testing mode requires to do that
29
   voodoo that it do so well
30
31
"""
32
33
# imports
34
import os
35
import sys
2144.1.1 by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control
36
import copy
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
37
import shutil
2124.2.1 by Patrick Crews
Updates to system_management.py to have better naming for symlinks in shm
38
import getpass
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
39
import commands
40
2211.3.2 by patrick crews
Including uuid module so we can work with red hat...grr. We could require packages and whatnot, but just inlcuding the small file until such time as redhat moves to python 2.5 seems good
41
from lib.uuid import uuid4
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
42
from lib.sys_mgmt.port_management import portManager
43
from lib.sys_mgmt.logging_management import loggingManager
2088.9.18 by patrick crews
Updates to allow for timing of test cases and reporting and whatnot
44
from lib.sys_mgmt.time_management import timeManager
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
45
46
class systemManager:
47
    """Class to deal with the basics of system-level interaction
48
       and awareness
49
50
       Uses other managers to handle sub-tasks like port management
51
52
    """
53
    def __init__(self, variables, tree_type='drizzle'):
54
        self.logging = loggingManager(variables)
55
        if variables['verbose']:
56
            self.logging.verbose("Initializing system manager...")
57
58
        self.skip_keys = [ 'code_tree'
2088.9.22 by patrick crews
Updated code to set LD_LIBRARY env vars
59
                         , 'ld_lib_paths'
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
60
                         , 'port_manager'
61
                         , 'logging_manager'
62
                         , 'environment_reqs'
63
                         , 'env_var_delimiter']
64
        self.debug = variables['debug']
65
        self.verbose = variables['verbose']
66
        self.env_var_delimiter = ':'
67
        self.no_shm = variables['noshm']
68
        self.shm_path = self.find_path(["/dev/shm", "/tmp"], required=0)
2121.5.1 by patrick crews
changes to parse netstat output better on freebsd for port management
69
        self.cur_os = os.uname()[0]
2124.2.1 by Patrick Crews
Updates to system_management.py to have better naming for symlinks in shm
70
        self.cur_user = getpass.getuser()
2124.2.6 by Patrick Crews
make target for dbqp! : )
71
        self.workdir = os.path.abspath(variables['workdir'])
2194.2.1 by patrick crews
Integrated randgen with dbqp. We now have mode=randgen and a set of randgen test suites (very basic now). Output = same as dtr : ) We also have mode=cleanup to kill any servers we have started. Docs updates too. Gendata utility allows us to populate test servers
72
        self.testdir = os.path.abspath(variables['testdir'])
2151.8.1 by patrick crews
Updates to allow several dbqp's run run on one system (via uuid) and fix to return a relevant code post-execution (ie 1 if tests failed or timed out))
73
        self.datadir = os.path.abspath(os.path.join(variables['testdir'],'dbqp_data'))
2124.2.6 by Patrick Crews
make target for dbqp! : )
74
        self.top_srcdir = os.path.abspath(variables['topsrcdir'])
75
        self.top_builddir = os.path.abspath(variables['topbuilddir'])
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
76
        self.start_dirty = variables['startdirty']
2088.9.24 by patrick crews
Added code to handle valgrind. Currently not working - getting bad error on malloc-fill=DEADBEEF : (, checking in anyway as it is largely there
77
        self.valgrind = variables['valgrind']
2121.6.1 by patrick crews
Updates to allow for reorder option. Also added placeholder for --fast (it is one in test-run.pl as well), and a minor fix of output regarding libtool
78
        self.gdb = variables['gdb']
2158.2.1 by patrick crews
Added in --gdb and --manual-gdb option as well as --drizzled option
79
        self.manual_gdb = variables['manualgdb']
2194.2.1 by patrick crews
Integrated randgen with dbqp. We now have mode=randgen and a set of randgen test suites (very basic now). Output = same as dtr : ) We also have mode=cleanup to kill any servers we have started. Docs updates too. Gendata utility allows us to populate test servers
80
        self.randgen_path = variables['randgenpath']
2088.9.24 by patrick crews
Added code to handle valgrind. Currently not working - getting bad error on malloc-fill=DEADBEEF : (, checking in anyway as it is largely there
81
82
        # we use this to preface commands in order to run valgrind and such
83
        self.cmd_prefix = '' 
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
84
        
85
        self.port_manager = portManager(self,variables['debug'])
2088.9.18 by patrick crews
Updates to allow for timing of test cases and reporting and whatnot
86
        self.time_manager = timeManager(self)
2151.8.1 by patrick crews
Updates to allow several dbqp's run run on one system (via uuid) and fix to return a relevant code post-execution (ie 1 if tests failed or timed out))
87
                   
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
88
        # Make sure the tree we are testing looks good
89
        self.code_tree = self.get_code_tree(variables, tree_type)
90
2144.1.1 by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control
91
        self.ld_lib_paths = self.join_env_var_values(self.code_tree.ld_lib_paths)
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
92
93
        # Some ENV vars are system-standard
94
        # We describe and set them here and now
95
        # The format is name: (value, append, suffix)
2144.1.1 by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control
96
        self.environment_reqs = { 'UMASK':'0660'
97
                                , 'UMASK_DIR' : '0770'
98
                                , 'LC_ALL' : 'C'
99
                                , 'LC_CTYPE' : 'C'
100
                                , 'LC_COLLATE' : 'C'
101
                                , 'USE_RUNNING_SERVER' : "0"
102
                                , 'TOP_SRCDIR' : self.top_srcdir
103
                                , 'TOP_BUILDDIR' : self.top_builddir
104
                                , 'DRIZZLE_TEST_DIR' : self.code_tree.testdir
105
                                , 'DTR_BUILD_THREAD' : "-69.5"
106
                                , 'LD_LIBRARY_PATH' : self.append_env_var( 'LD_LIBRARY_PATH'
107
                                                                         , self.ld_lib_paths
108
                                                                         , suffix = 0
109
                                                                         , quiet = 1
110
                                                                         )
111
                                , 'DYLD_LIBRARY_PATH' : self.append_env_var( 'DYLD_LIBRARY_PATH'
112
                                                                         , self.ld_lib_paths
113
                                                                         , suffix = 0
114
                                                                         , quiet = 1
115
                                                                         )
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
116
                                }
117
        # set the env vars we need
2144.1.1 by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control
118
        # self.process_environment_reqs(self.environment_reqs)
119
        self.update_environment_vars(self.environment_reqs)
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
120
2151.8.1 by patrick crews
Updates to allow several dbqp's run run on one system (via uuid) and fix to return a relevant code post-execution (ie 1 if tests failed or timed out))
121
        # We find or generate our id file
122
        # We use a uuid to identify the symlinked
123
        # workdirs.  That way, each installation
124
        # Will have a uuid/tmpfs workingdir
125
        # We store in a file so we know what
126
        # is ours
127
        self.uuid = self.get_uuid()
128
        self.symlink_name = 'dbqp_workdir_%s_%s' %(self.cur_user, self.uuid)
129
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
130
        # initialize our workdir
131
        self.process_workdir()
132
2088.9.24 by patrick crews
Added code to handle valgrind. Currently not working - getting bad error on malloc-fill=DEADBEEF : (, checking in anyway as it is largely there
133
        # check for libtool
134
        self.libtool = self.libtool_check()
135
2151.8.4 by patrick crews
gdb! : ) We try to set things up to launch the native Mac Terminal if we are running on Darwin
136
        # See if we need to do any further processing for special
137
        # options like valgrind and gdb
138
        self.handle_additional_reqs(variables)
139
     
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
140
        if self.debug:
141
            self.logging.debug_class(self)
142
        
143
    def get_code_tree(self, variables, tree_type):
144
        """Find out the important files, directories, and env. vars
145
           for a particular type of tree.  We import a definition module
146
           depending on the tree_type.  The module lets us know
147
           what to look for, etc
148
149
        """
150
        
151
        # Import the appropriate module that defines
152
        # where we find what we need depending on 
153
        # tree type
154
        test_tree = self.process_tree_type(tree_type, variables)
155
        return test_tree
156
157
    def process_tree_type(self, tree_type, variables):
158
        """Import the appropriate module depending on the type of tree
159
           we are testing. 
160
161
           Drizzle is the only supported type currently
162
163
        """
2151.8.1 by patrick crews
Updates to allow several dbqp's run run on one system (via uuid) and fix to return a relevant code post-execution (ie 1 if tests failed or timed out))
164
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
165
        if self.verbose:
166
            self.logging.verbose("Processing source tree under test...")
167
        if tree_type == 'drizzle':
168
            # base_case
169
            from lib.sys_mgmt.codeTree import drizzleTree
170
            test_tree = drizzleTree(variables,self)
171
            return test_tree
172
        else:
173
            self.logging.error("Tree_type: %s not supported yet" %(tree_type))
2144.1.1 by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control
174
            sys.exit(1)        
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
175
2151.8.1 by patrick crews
Updates to allow several dbqp's run run on one system (via uuid) and fix to return a relevant code post-execution (ie 1 if tests failed or timed out))
176
    
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
177
    def create_dirset(self, rootdir, dirset):
178
        """ We produce the set of directories defined in dirset
179
            dirset is a set of dictionaries like
180
            {'dirname': 'subdir'}
181
            or {'dirname': {'subdir':'subsubdir}}...
182
183
            We generally expect there to be only a single
184
            top-level key.  The intent is to produce a dirset
185
            rooted at key[0], with various subdirs under that
186
            subsequest dirsets should be handles in separate calls...
187
188
        """
189
        for dirname in dirset.keys():
190
            full_path = os.path.join(rootdir, dirname)
191
            subdirset = dirset[dirname]
192
            if type(subdirset) is str:
193
                self.create_symlink(subdirset,full_path)
194
            else:
195
                self.create_dir(full_path)        
196
                # dirset[dirname] is a new dictionary
197
                if subdirset is None:
198
                    {}
199
                else:
200
                    self.create_dirset(full_path,subdirset)
201
202
        return full_path    
203
2151.8.1 by patrick crews
Updates to allow several dbqp's run run on one system (via uuid) and fix to return a relevant code post-execution (ie 1 if tests failed or timed out))
204
    def get_uuid(self):
205
        """ We look to see if a uuid file exists
206
            If so, we use that to know where to work
207
            If not we produce one so future runs
208
            have a definitive id to use
209
210
        """
211
212
        uuid_file_name = os.path.join(self.datadir, 'uuid')
213
        if os.path.exists(uuid_file_name):
214
            uuid_file = open(uuid_file_name,'r')
215
            uuid = uuid_file.readline().strip()
216
            uuid_file.close()
217
        else:
218
            uuid = uuid4()
219
            uuid_file = open(uuid_file_name,'w')
220
            uuid_file.write(str(uuid))
221
            uuid_file.close()
222
        return uuid
223
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
224
    def process_workdir(self):
225
        """ We create our workdir, analyze relevant variables
226
            to see if we should/shouldn't symlink to shm
227
            We do nothing if we have --start-dirty
228
229
        """
230
        
231
        if os.path.exists(self.workdir):
232
            # our workdir already exists
233
            if self.start_dirty:
234
                self.logging.info("Using --start-dirty, not attempting to touch directories")
235
                return
236
            else:
2194.2.1 by patrick crews
Integrated randgen with dbqp. We now have mode=randgen and a set of randgen test suites (very basic now). Output = same as dtr : ) We also have mode=cleanup to kill any servers we have started. Docs updates too. Gendata utility allows us to populate test servers
237
                self.cleanup() # We crawl / try to kill any server pids we find
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
238
                self.remove_dir(self.workdir)
239
        self.allocate_workdir()
240
    
241
242
    def allocate_workdir(self):
243
        """ Create a workdir according to user-supplied specs """
244
        if self.no_shm:
245
            self.logging.info("Using --no-shm, will not link workdir to shm")
246
            self.create_dir(self.workdir, subdir=0)
247
        elif self.shm_path == None:
248
            self.logging.info("Could not find shared memory path for use.  Not linking workdir to shm")
249
            self.create_dir(self.workdir, subdir=0)
250
        else:
251
            shm_workdir = self.create_dir(os.path.join(self.shm_path, self.symlink_name))
252
            self.logging.info("Linking workdir %s to %s" %(self.workdir, shm_workdir))  
253
            self.create_symlink(shm_workdir, self.workdir)
254
255
    def create_dir(self, dirname, subdir =1 ):
256
        """ Create a directory.  If subdir = 1,
257
            then the new dir should be a subdir of
258
            self.workdir.  Else, we just create dirname,
259
            which should really be dirpath in this case
260
261
        """
262
263
        if subdir:
264
            full_path = os.path.join(self.workdir, dirname)
265
        else:
266
            full_path = dirname
267
268
        if os.path.exists(full_path):
269
            if self.start_dirty:
270
                return full_path
271
            else:
272
                shutil.rmtree(full_path)
273
            if self.debug:
274
                 self.logging.debug("Creating directory: %s" %(dirname))   
275
        os.makedirs(full_path)
276
        return full_path
277
278
    def remove_dir(self, dirname, require_empty=0 ):
279
        """ Remove the directory in question.
280
            We assume we want to brute-force clean
281
            things.  If require_empty = 0, then
282
            the dir must be empty to remove it
283
284
        """
285
        if self.debug:
286
            self.logging.debug("Removing directory: %s" %(dirname))
287
        if os.path.islink(dirname):
288
            os.remove(dirname)
289
        elif require_empty:
290
            os.rmdir(dirname)
291
        else:
292
            shutil.rmtree(dirname)
293
294
    def copy_dir(self, srcdir, tgtdir, overwrite = 1):
295
        """ Copy the contents of srcdir to tgtdir.
296
            We overwrite (remove/recreate) tgtdir
297
            if overwrite == 1
298
299
        """
300
        if self.debug:
301
            self.logging.debug("Copying directory: %s to %s" %(srcdir, tgtdir))
302
        if os.path.exists(tgtdir):
303
            if overwrite:
304
                self.remove_dir(tgtdir)
305
            else:
306
                self.logging.error("Cannot overwrite existing directory: %s" %(tgtdir))
307
                sys.exit(1)
308
        shutil.copytree(srcdir, tgtdir, symlinks=True)
309
310
    def create_symlink(self, source, link_name):
311
        """ We create a symlink to source named link_name """
312
        if self.debug:
313
            self.logging.debug("Creating symlink from %s to %s" %(source, link_name))
2088.9.10 by patrick crews
Updates to filesystem_engine and transaction_log tests to allow dbqp + test-run.pl to live together and execute all tests
314
        if os.path.exists(link_name) or os.path.islink(link_name):
315
            os.remove(link_name)
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
316
        return os.symlink(source, link_name)
317
2088.9.10 by patrick crews
Updates to filesystem_engine and transaction_log tests to allow dbqp + test-run.pl to live together and execute all tests
318
    def create_symlinks(self, needed_symlinks):
319
        """ We created the symlinks in needed_symlinks 
320
            We expect it to be tuples in source, link_name format
321
322
        """
323
        
324
        for needed_symlink in needed_symlinks:
325
            source, link_name = needed_symlink
326
            self.create_symlink(source, link_name)
327
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
328
    def join_env_var_values(self, value_list):
329
        """ Utility to join multiple values into a nice string
330
            for setting an env var to
331
 
332
        """
333
334
        return self.env_var_delimiter.join(value_list)
335
336
    def set_env_var(self, var_name, var_value, quiet=0):
337
        """Set an environment variable.  We really just abstract
338
           voodoo on os.environ
339
340
        """
341
        if self.debug and not quiet:
342
            self.logging.debug("Setting env var: %s" %(var_name))
343
        try:
344
            os.environ[var_name]=var_value
345
        except Exception, e:
346
            self.logging.error("Issue setting environment variable %s to value %s" %(var_name, var_value))
347
            self.logging.error("%s" %(e))
348
            sys.exit(1)
349
2144.1.1 by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control
350
    def update_environment_vars(self, desired_vars, working_environment=None):
351
        """ We update the environment vars with desired_vars
352
            The expectation is that you know what you are asking for ; )
353
            If working_environment is provided, we will update that with
354
            desired_vars.  We operate directly on os.environ by default
355
            We return our updated environ dictionary
356
357
        """
358
359
        if not working_environment:
360
            working_environment = os.environ
361
        working_environment.update(desired_vars)
362
        return working_environment
363
364
    def create_working_environment(self, desired_vars):
365
        """ We return a copy of os.environ updated with desired_vars """
366
367
        working_copy = copy.deepcopy(os.environ)
368
        return self.update_environment_vars( desired_vars
369
                                    , working_environment = working_copy )
370
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
371
    def append_env_var(self, var_name, append_string, suffix=1, quiet=0):
372
        """ We add the values in var_values to the environment variable 
373
            var_name.  Depending on suffix value, we either append or prepend
2144.1.1 by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control
374
            we return a string suitable for os.putenv
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
375
376
        """
377
        new_var_value = ""
378
        if var_name in os.environ:
379
            cur_var_value = os.environ[var_name]
380
            if suffix: # We add new values to end of existing value
381
                new_var_values = [ cur_var_value, append_string ]
382
            else:
383
                new_var_values = [ append_string, cur_var_value ]
2088.9.24 by patrick crews
Added code to handle valgrind. Currently not working - getting bad error on malloc-fill=DEADBEEF : (, checking in anyway as it is largely there
384
            new_var_value = self.env_var_delimiter.join(new_var_values)
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
385
        else:
386
            # No existing variable value
387
            new_var_value = append_string
2144.1.1 by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control
388
        return new_var_value
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
389
390
    def find_path(self, paths, required=1):
391
        """We search for the files we need / want to be aware of
392
           such as the drizzled binary, the various client binaries, etc
393
           We use the required switch to determine if we die or not
394
           if we can't find the file.
395
396
           We expect paths to be a list of paths, ordered in terms
397
           of preference (ie we want to use something from search-path1
398
           before search-path2).
399
400
           We return None if no match found and this wasn't required
401
402
        """
403
404
        for test_path in paths:
405
            if self.debug:
406
                self.logging.debug("Searching for path: %s" %(test_path))
407
            if os.path.exists(test_path):
408
                return test_path
409
        if required:
410
            self.logging.error("Required file not found out of options: %s" %(" ,".join(paths)))
411
            sys.exit(1)
412
        else:
413
            return None
414
415
    def execute_cmd(self, cmd, must_pass = 1):
416
        """ Utility function to execute a command and
417
            return the output and retcode
418
419
        """
420
421
        if self.debug:
422
            self.logging.debug("Executing command: %s" %(cmd))
423
        (retcode, output)= commands.getstatusoutput(cmd)
424
        if not retcode == 0 and must_pass:
425
            self.logging.error("Command %s failed with retcode %d" %(cmd, retcode))
426
            self.logging.error("%s" %(output))
427
            sys.exit(1)
428
        return retcode, output
429
2088.9.24 by patrick crews
Added code to handle valgrind. Currently not working - getting bad error on malloc-fill=DEADBEEF : (, checking in anyway as it is largely there
430
    def libtool_check(self):
431
        """ We search for libtool """
432
        libtool_path = '../libtool'
433
        if os.path.exists(libtool_path) and os.access( libtool_path
434
                                                     , os.X_OK):
2121.6.1 by patrick crews
Updates to allow for reorder option. Also added placeholder for --fast (it is one in test-run.pl as well), and a minor fix of output regarding libtool
435
            if self.valgrind or self.gdb:
436
                self.logging.info("Using libtool when running valgrind or debugger")
2088.9.24 by patrick crews
Added code to handle valgrind. Currently not working - getting bad error on malloc-fill=DEADBEEF : (, checking in anyway as it is largely there
437
            return libtool_path
438
        else:
439
            return None
440
2151.8.4 by patrick crews
gdb! : ) We try to set things up to launch the native Mac Terminal if we are running on Darwin
441
    def handle_additional_reqs(self, variables):
442
        """ Do what we need to do to set things up for
443
            options like valgrind and gdb
444
445
        """
446
447
        # do we need to setup for valgrind?
448
        if self.valgrind:
449
            self.handle_valgrind_reqs(variables['valgrindarglist'])
450
451
    def handle_gdb_reqs(self, server, server_args):
452
        """ We generate the gdb init file and whatnot so we
453
            can run gdb properly
454
2158.2.1 by patrick crews
Added in --gdb and --manual-gdb option as well as --drizzled option
455
            if the user has specified manual-gdb, we provide
456
            them with a message about when to start and
457
            signal the server manager to simply wait
458
            for the server to be started by the user
459
2151.8.4 by patrick crews
gdb! : ) We try to set things up to launch the native Mac Terminal if we are running on Darwin
460
        """
461
        extra_args = ''
2158.2.1 by patrick crews
Added in --gdb and --manual-gdb option as well as --drizzled option
462
        gdb_term_cmd = "xterm -title %s.%s " %( server.owner
463
                                              , server.name
464
                                              )
2151.8.4 by patrick crews
gdb! : ) We try to set things up to launch the native Mac Terminal if we are running on Darwin
465
        gdb_file_name = "%s.gdbinit" %(server.name)
466
467
        if self.cur_os == 'Darwin': # Mac...ick ; P
468
            extra_args = [ "set env DYLD_INSERT_LIBRARIES /usr/lib/libgmalloc.dylib"
469
                         , "set env MallocStackLogging 1"
470
                         , "set env MallocScribble 1"
471
                         , "set env MallocPreScribble 1"
472
                         , "set env MallocStackLogging 1"
473
                         , "set env MallocStackLoggingNoCompact 1"
474
                         , "set env MallocGuardEdges 1"
475
                         ] 
476
477
        # produce our init file
478
        if extra_args:
479
            extra_args = "\n".join(extra_args)
480
        gdb_file_contents = [ "set args %s" %(" ".join(server_args))
481
                            , "%s" % (extra_args)
482
                            , "set breakpoint pending on"
483
	                          , "break drizzled::parse"
484
	                          , "commands 1"
485
                            , "disable 1"
486
	                          , "end"
487
                            , "set breakpoint pending off"
488
	                          , "run"
489
                            ]
490
        gdb_file_path = os.path.join(server.tmpdir, gdb_file_name)
491
        gdb_init_file = open(gdb_file_path,'w')
492
        gdb_init_file.write("\n".join(gdb_file_contents))
493
        gdb_init_file.close()
494
495
        # return our command line
496
        if self.libtool:
497
            libtool_string = "%s --mode=execute " %(self.libtool)
498
        else:
499
            libtool_string = ""
2158.2.1 by patrick crews
Added in --gdb and --manual-gdb option as well as --drizzled option
500
501
        if self.manual_gdb:
502
            self.logging.info("To start gdb, open another terminal and enter:")
503
            self.logging.info("%s/../libtool --mode=execute gdb -cd %s -x %s %s" %( self.code_tree.testdir
504
                                                                                  , self.code_tree.testdir
505
                                                                                  , gdb_file_path
506
                                                                                  , server.server_path
507
                                                                                  ) )
508
            return None
509
510
        else:
511
            return "%s -e %s gdb -x %s %s" %( gdb_term_cmd
512
                                            , libtool_string
513
                                            , gdb_file_path
514
                                            , server.server_path
515
                                            )
2151.8.4 by patrick crews
gdb! : ) We try to set things up to launch the native Mac Terminal if we are running on Darwin
516
2088.9.24 by patrick crews
Added code to handle valgrind. Currently not working - getting bad error on malloc-fill=DEADBEEF : (, checking in anyway as it is largely there
517
    def handle_valgrind_reqs(self, optional_args, mode='valgrind'):
518
        """ We do what voodoo we need to do to run valgrind """
519
        valgrind_args = [ "--show-reachable=yes"
2151.8.4 by patrick crews
gdb! : ) We try to set things up to launch the native Mac Terminal if we are running on Darwin
520
                        , "--malloc-fill=22"
521
                        , "--free-fill=22"
2088.9.24 by patrick crews
Added code to handle valgrind. Currently not working - getting bad error on malloc-fill=DEADBEEF : (, checking in anyway as it is largely there
522
                        # , "--trace-children=yes" this is for callgrind only
523
                        ]
524
        if optional_args:
525
        # we override the defaults with user-specified options
526
            valgrind_args = optional_args
527
        self.logging.info("Running valgrind with options: %s" %(" ".join(valgrind_args)))
528
529
        # set our environment variable
530
        self.set_env_var('VALGRIND_RUN', '1', quiet=0)
531
532
        # generate command prefix to call valgrind
533
        cmd_prefix = ''
534
        if self.libtool:
535
            cmd_prefix = "%s --mode=execute valgrind " %(self.libtool)
536
        if mode == 'valgrind':
537
            # default mode
538
539
            args = [ "--tool=memcheck"
540
                   , "--leak-check=yes"
541
                   , "--num-callers=16" 
542
                   ]
543
            # look for our suppressions file and add it to the mix if found
544
            suppress_file = os.path.join(self.code_tree.testdir,'valgrind.supp')
545
            if os.path.exists(suppress_file):
546
                args = args + [ "--suppressions=%s" %(suppress_file) ]
547
548
            cmd_prefix = cmd_prefix + " ".join(args + valgrind_args)
549
        self.cmd_prefix = cmd_prefix  
550
        
551
        # add debug libraries to ld_library_path
552
        debug_path = '/usr/lib/debug'
553
        if os.path.exists(debug_path):
554
            self.append_env_var("LD_LIBRARY_PATH", debug_path, suffix=1)
555
            self.append_env_var("DYLD_LIBRARY_PATH", debug_path, suffix=1)
2144.1.1 by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control
556
2194.2.1 by patrick crews
Integrated randgen with dbqp. We now have mode=randgen and a set of randgen test suites (very basic now). Output = same as dtr : ) We also have mode=cleanup to kill any servers we have started. Docs updates too. Gendata utility allows us to populate test servers
557
558
    def cleanup(self, exit=False):
559
        """ We try to kill any servers whose pid files
560
            we detect lurking about
561
562
        """
563
        
564
        self.pid_hunt_and_kill(exit)
565
566
    def pid_hunt_and_kill(self, exit):
567
        """ Crawl our workdir and look for server.pid files
568
            We read 'em and kill 'em if we find 'em
569
570
        """
571
572
        for root, dirs, files in os.walk(self.workdir):
573
            #print root, dirs, files
574
            for found_file in files:
575
                if found_file.endswith('.pid'):
576
                    file_path = os.path.join(root, found_file)
577
                    pid_file = open(file_path,'r')
578
                    pid = pid_file.readline().strip()
579
                    pid_file.close()
580
                    self.logging.info("Killing pid %s from %s" %( pid
581
                                                                , file_path
582
                                                                ))
2318.1.1 by patrick crews
Added code to monitor a server pid after shutdown cmd issued - after a limited timeout, we kill -9 it. Not a proper fix, but makes dbqp a good citizen again
583
                    self.kill_pid(pid)
2194.2.1 by patrick crews
Integrated randgen with dbqp. We now have mode=randgen and a set of randgen test suites (very basic now). Output = same as dtr : ) We also have mode=cleanup to kill any servers we have started. Docs updates too. Gendata utility allows us to populate test servers
584
        if exit:
585
            sys.exit(0)
586
2318.1.1 by patrick crews
Added code to monitor a server pid after shutdown cmd issued - after a limited timeout, we kill -9 it. Not a proper fix, but makes dbqp a good citizen again
587
    def find_pid(self, pid):
588
        """ Execute ps and see if we find the pid """
589
590
        cmd = "ps"
591
        retcode, output = self.execute_cmd(cmd)
592
        output = output.split('\n')
593
        for line in output:
594
            found_pid = line.strip().split(' ')
595
            if str(pid) == pid:
596
                return True
597
        return False
598
599
    def kill_pid(self, pid):
600
        """ We kill the specified pid """
601
        
602
        self.execute_cmd("kill -9 %s" %pid, must_pass=0)
603
2144.1.1 by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control
604
   
2088.9.24 by patrick crews
Added code to handle valgrind. Currently not working - getting bad error on malloc-fill=DEADBEEF : (, checking in anyway as it is largely there
605
    
606
607
2088.9.1 by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time
608
 
609
        
610
        
611