Changeset 91

Show
Ignore:
Timestamp:
07/06/06 16:42:30 (3 years ago)
Author:
robin
Message:

support for per test slaveservices

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • slavetools/trunk/lib/slavetools/slaveapi_posix.py

    r87 r91  
    11#!/usr/bin/env python 
    2 import signal,errno, os, sys 
     2import signal,errno, os, sys, time, logging 
    33from subprocess import Popen 
    44from optionsapi import COMMON_OPTS, build_parser 
     
    1010        'daemonize_posix ' 
    1111        'start_posix start_posix_noforward stop_posix main_posix_boilerplate ' 
     12        'launch_process ' 
    1213        'minimal_daemon_example ' 
    1314        'COMMON_POSIX_OPTS START_POSIX_OPTS STOP_POSIX_OPTS MAIN_POSIX_OPTS ' 
     
    288289 
    289290 
     291def 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     
    290368COMMON_POSIX_OPTS=[ 
    291369    ('--daemonize',dict(default=False,action='store_true', 
  • slavetools/trunk/lib/slavetools/slaveplug.py

    r90 r91  
    6868""" 
    6969 
    70 import os,textwrap,logging 
     70import os,textwrap,logging,re,types 
    7171from time import time,sleep 
    7272from subprocess import Popen 
    7373from nose.plugins import Plugin 
     74from nose.case import FunctionTestCase 
    7475from slavetools.optionsapi import add_options, prefix_longopts 
    75 #from slavetools.optionsapi import prune_shortopts, update_options 
    76 #from slavetools.optionsapi import COMMON_OPTS 
    77 #from slavetools.slaveapi_posix import COMMON_POSIX_OPTS, START_POSIX_OPTS 
    7876from slavetools.slaveapi_posix import read_pidfile, pidfile_name 
    79 from slavetools.slaveapi_posix import kill_posix, pollpid_posix 
     77from slavetools.slaveapi_posix import kill_posix, pollpid_posix, launch_process 
    8078 
    8179PRUNE_SLAVE_LONGOPTS=['--dont-forward-opts'] 
    8280PLUGIN_OPTS = [ 
     81    ('--wingdbstub', dict(default=False, action='store_true')), 
    8382    ('--slave-program', dict( 
    8483        default=None, 
     
    150149            'use this when --slave-program is just an indirect step.')), 
    151150        ('--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" ' 
    153153            ' command are copied into the *begining* of the argumets passed ' 
    154154            ' to the child. Set this flag to disable this behaviour.')), 
     
    179179    def configure(self, options, conf): 
    180180        log = logging.getLogger(self.LOGCHAN) 
     181        self.log = log 
     182        if getattr(options,'wingdbstub'): 
     183            try: 
     184                import wingdbstub 
     185            except ImportError: 
     186                pass 
    181187        super(SlaveService, self).configure(options, conf) 
    182188        if getattr(options, ('%smore-help' % self.OPT_PREFIX).replace( 
     
    187193        if not self.enabled: 
    188194            return 
     195 
     196        self.match=re.compile(r'(?:^|[\b_\.%s-])[Ss]lave[Tt]est' % os.sep) 
    189197        self.slave_options_xlate = [ 
    190198            (self.sourced_longopts[iopt][0],  
     
    221229        except ValueError: 
    222230            self.slave_grace=2.5 
    223         if not self.slave_program: 
     231        if False and not self.slave_program: 
    224232            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 
    225275 
    226276    def _getprefixedattr(self, options, attr): 
     
    229279            ) 
    230280  
    231     def prepare_slave_program(self): 
     281    def prepare_slave_program(self, slave_program=None): 
    232282        """If the heuristics for spliting the program don't suit  
    233283        override this method.""" 
     284        if slave_program is None: 
     285            slave_program = self.slave_program 
    234286        if self.slave_program_split is not None: 
    235287            return [arg  
    236                 for arg in self.slave_program.split(self.slave_program_split) 
     288                for arg in slave_program.split(self.slave_program_split) 
    237289                if arg] 
    238         return self.slave_program and [self.slave_program] or [] 
     290        return slave_program and [slave_program] or [] 
    239291 
    240292    def prepare_slave_args(self): 
     
    278330     
    279331    def begin(self): 
     332        if not self.slave_program: 
     333            return 
     334        return 
    280335        log = logging.getLogger(self.LOGCHAN) 
    281336        args = self.prepare_slave_program() 
     
    287342        log.info(' '.join(args)) 
    288343        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) 
    290347 
    291348    def launch_slave(self, args): 
     
    367424 
    368425    def finalize(self, result): 
     426        return 
     427        if not self.slave_program: 
     428            return 
    369429        log = logging.getLogger(self.LOGCHAN) 
    370430        if self.pid == -1: