Changeset 91
- Timestamp:
- 07/06/06 16:42:30 (3 years ago)
- Files:
-
- slavetools/trunk/lib/slavetools/slaveapi_posix.py (modified) (3 diffs)
- slavetools/trunk/lib/slavetools/slaveplug.py (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
slavetools/trunk/lib/slavetools/slaveapi_posix.py
r87 r91 1 1 #!/usr/bin/env python 2 import signal,errno, os, sys 2 import signal,errno, os, sys, time, logging 3 3 from subprocess import Popen 4 4 from optionsapi import COMMON_OPTS, build_parser … … 10 10 'daemonize_posix ' 11 11 'start_posix start_posix_noforward stop_posix main_posix_boilerplate ' 12 'launch_process ' 12 13 'minimal_daemon_example ' 13 14 'COMMON_POSIX_OPTS START_POSIX_OPTS STOP_POSIX_OPTS MAIN_POSIX_OPTS ' … … 288 289 289 290 291 def launch_process(options, argv, grace=2.5, prevpid=None, 292 time=time.time, sleep=time.sleep, log=None): 293 """launch a process, returning it's pid if we can. 294 295 There is a lot of unpythonic guess work going on here. However, given 296 the primary aim is to support developer testing, its not un reasonable 297 to assume the user has a degree of control over there environment. 298 Certainly enough to compensate for the bad guesses this function makes. 299 300 WARNING: If the process, or any of its children, implements the 301 (typical) idiom of read pid, kill if read, write pid then that child 302 becomes a suicide. (this abnormal termination will be noticed by this 303 function. It can be avoided in your own programs by using the 304 `kill_posix` method above - as it guards against this:: 305 306 if readpid and readpid != os.getpid(): 307 kill(readpid) 308 309 """ 310 311 readpidfile = False 312 pidfile = None 313 abspidfile = None 314 log = log or logging.getLogger(__name__) 315 options.directory = os.path.expanduser(options.directory) 316 slave_progname = argv[0] 317 cwd = os.getcwd() 318 try: 319 os.chdir(options.directory) 320 # deal with predecessor unless otherwise directed 321 if prevpid != -1: 322 log.info('*** attempting to kill of predecessor') 323 # we could possibly avoid the directory change if an explicit 324 # pid for the predecessor is provided . Not 325 # convinced this would be usefull. 326 # Note: if --pidfile was specified then that is *exactly* 327 # the filename we get back here. 328 pidfile=kill_posix(log, options, slave_progname, 329 # unless the user explicitly sets prevpid then prevpid will 330 # be None and ``pid`` is then ignored. 331 pid=prevpid) 332 if pidfile is None: # None if explicit prevpid is provided 333 pidfile = pidfile_name(options, slave_program) 334 if pidfile: # could still be None 335 abspidfile=os.path.abspath(pidfile) 336 # launch the process 337 p = Popen(argv) 338 tstart = time() # for benefit of `finalize` 339 sleep(grace) 340 rcode=p.poll() 341 if rcode is None: 342 log.info('*** [%s] is running, pid = [%s]' % 343 (argv[0], p.pid)) 344 pid=p.pid 345 return pid 346 if abspidfile: 347 log.info(( 348 '*** [%s] exited with [%s], ' % (p.pid, rcode))) 349 log.info('*** ATTEMPTING TO READ pid from file: %s' % abspidfile) 350 pid, readpidfile = read_pidfile(abspidfile, p.pid) 351 if not readpidfile: 352 log.warning( 353 '*** FAILED TO READ pid from file %s' % abspidfile) 354 else: 355 log.info("*** READ pid [%s] from [%s]" % (pid, abspidfile)) 356 else: 357 pid=p.pid 358 if readpidfile: 359 rcode = pollpid_posix(pid) 360 if rcode is not None: 361 log.warning( 362 '*** FAILED TO GET PID, exit code: %s' % rcode) 363 pid = -1 364 finally: 365 os.chdir(cwd) 366 return pid 367 290 368 COMMON_POSIX_OPTS=[ 291 369 ('--daemonize',dict(default=False,action='store_true', slavetools/trunk/lib/slavetools/slaveplug.py
r90 r91 68 68 """ 69 69 70 import os,textwrap,logging 70 import os,textwrap,logging,re,types 71 71 from time import time,sleep 72 72 from subprocess import Popen 73 73 from nose.plugins import Plugin 74 from nose.case import FunctionTestCase 74 75 from slavetools.optionsapi import add_options, prefix_longopts 75 #from slavetools.optionsapi import prune_shortopts, update_options76 #from slavetools.optionsapi import COMMON_OPTS77 #from slavetools.slaveapi_posix import COMMON_POSIX_OPTS, START_POSIX_OPTS78 76 from slavetools.slaveapi_posix import read_pidfile, pidfile_name 79 from slavetools.slaveapi_posix import kill_posix, pollpid_posix 77 from slavetools.slaveapi_posix import kill_posix, pollpid_posix, launch_process 80 78 81 79 PRUNE_SLAVE_LONGOPTS=['--dont-forward-opts'] 82 80 PLUGIN_OPTS = [ 81 ('--wingdbstub', dict(default=False, action='store_true')), 83 82 ('--slave-program', dict( 84 83 default=None, … … 150 149 'use this when --slave-program is just an indirect step.')), 151 150 ('--dont-forward-opts', dict(default=False, action='store_true', 152 help='For convenience, all options that are specified to the "start" ' 151 help= 152 'For convenience, all options that are specified to the "start" ' 153 153 ' command are copied into the *begining* of the argumets passed ' 154 154 ' to the child. Set this flag to disable this behaviour.')), … … 179 179 def configure(self, options, conf): 180 180 log = logging.getLogger(self.LOGCHAN) 181 self.log = log 182 if getattr(options,'wingdbstub'): 183 try: 184 import wingdbstub 185 except ImportError: 186 pass 181 187 super(SlaveService, self).configure(options, conf) 182 188 if getattr(options, ('%smore-help' % self.OPT_PREFIX).replace( … … 187 193 if not self.enabled: 188 194 return 195 196 self.match=re.compile(r'(?:^|[\b_\.%s-])[Ss]lave[Tt]est' % os.sep) 189 197 self.slave_options_xlate = [ 190 198 (self.sourced_longopts[iopt][0], … … 221 229 except ValueError: 222 230 self.slave_grace=2.5 223 if not self.slave_program:231 if False and not self.slave_program: 224 232 self.enabled=False 233 def loadTestsFromModule(self, module): 234 self.log.info("loadfrom: %s" % module.__name__) 235 modulefilepath = os.path.split(module.__file__)[0] 236 self.log.info("modulefilepath:%s" % modulefilepath) 237 for candidate in dir(module): 238 candidate = getattr(module, candidate) 239 if type(candidate) is not types.FunctionType: 240 continue 241 if (os.path.split(candidate.func_code.co_filename)[0] != 242 modulefilepath): 243 continue 244 if (None == self.match.search(candidate.__name__)): 245 continue 246 slave_program=getattr(candidate, 'slave_program') 247 if not slave_program: 248 continue 249 log = logging.getLogger(self.LOGCHAN) 250 options = self.slave_known_options 251 args=self.prepare_slave_program(slave_program) 252 pid=[-1] 253 def setup(): 254 pid[0]=launch_process(options, args, log=log) 255 def teardown(): 256 if pid[0] == -1: 257 return 258 kill_posix(log, None, args[0], pid=pid[0]) 259 yield FunctionTestCase(candidate,setUp=setup,tearDown=teardown) 260 261 def wantFunction(self, function): 262 if not self.match.search(function.__name__): 263 return 264 if not hasattr(function, 'slave_program'): 265 return 266 # somewhat counter intuitive, return False to prevent other plugins 267 # from seeing this method, then actualy select it in 268 # loadTestsFromModule above. 269 self.log.info("selecting:%s" % function.__name__) 270 return False 271 272 def prepareTest(self, test): 273 self.log.info('preparing test %s' % test) 274 return test 225 275 226 276 def _getprefixedattr(self, options, attr): … … 229 279 ) 230 280 231 def prepare_slave_program(self ):281 def prepare_slave_program(self, slave_program=None): 232 282 """If the heuristics for spliting the program don't suit 233 283 override this method.""" 284 if slave_program is None: 285 slave_program = self.slave_program 234 286 if self.slave_program_split is not None: 235 287 return [arg 236 for arg in s elf.slave_program.split(self.slave_program_split)288 for arg in slave_program.split(self.slave_program_split) 237 289 if arg] 238 return s elf.slave_program and [self.slave_program] or []290 return slave_program and [slave_program] or [] 239 291 240 292 def prepare_slave_args(self): … … 278 330 279 331 def begin(self): 332 if not self.slave_program: 333 return 334 return 280 335 log = logging.getLogger(self.LOGCHAN) 281 336 args = self.prepare_slave_program() … … 287 342 log.info(' '.join(args)) 288 343 log.info(str(args)) 289 self.launch_slave(args) 344 self.slave_progname=args[0] 345 self.pid=launch_process(self.slave_known_options, args) 346 #self.launch_slave(args) 290 347 291 348 def launch_slave(self, args): … … 367 424 368 425 def finalize(self, result): 426 return 427 if not self.slave_program: 428 return 369 429 log = logging.getLogger(self.LOGCHAN) 370 430 if self.pid == -1: