Changeset 27

Show
Ignore:
Timestamp:
25/11/07 20:50:23 (4 years ago)
Author:
daedalus
Message:

* Added 'authoritarian' mode, which allows you permit/deny each and every command

that will be issued to a remote device. Designed for testing new change scripts.

* Added a 'command_timeout' parameter to Command Provisioners, so you can override

the default command timeout of 300 seconds by setting an attribute in the config
file

* Added proper handling of the 'onfail: continue' idea. You can now specify that

if a change fails, and it is a pre-requisite for some other change, processing
will continue despite the failure.

* Added the onfail: retry feature. If a change fails, you can specify that it should

be retried. You can also set a max number of retries. The default is 3.

Location:
trunk
Files:
9 modified

Legend:

Unmodified
Added
Removed
  • trunk/change.py

    r25 r27  
    6161import debug 
    6262 
    63 log = logging.getLogger('configulator') 
     63log = logging.getLogger('modipy') 
    6464 
    6565from zope.interface import Interface, implements 
     
    7474    'backout_ok': 4, 
    7575    'backout_failed': 5, 
     76    'retry': 6, 
    7677    } 
    7778 
     
    8687    """ 
    8788 
    88     def __init__(self, name, devices=[], serial_mode=True, backout_all=False, continue_on_fail=False, **kwargs): 
     89    def __init__(self, name, devices=[], serial_mode=True, backout_all=False, on_fail_continue=False, **kwargs): 
    8990        """ 
    9091        @param devices: a list of devices that this change should be applied to. 
     
    9596        L{Change} to a device fails, the L{Change} will be backed out for all other 
    9697        devices that have so far succeeded. This makes a L{Change} atomic. 
    97         @param continue_on_fail: Boolean. If set to True, if application of a 
     98        @param on_fail_continue: Boolean. If set to True, if application of a 
    9899        L{Change} to a device fails, the change will be backed out from that device, 
    99100        but the system will continue attempting to apply the change to the rest 
     
    175176    implements( IChange, ) 
    176177 
    177     def __init__(self, name, devices=[], serial_mode=True, backout_all=False, continue_on_fail=False, 
     178    def __init__(self, name, devices=[], serial_mode=True, backout_all=False, 
     179                 on_fail_continue=False, 
     180                 on_fail_retry=False, 
     181                 max_retries=3, 
    178182                 pre_impl=None, 
    179183                 impl=None, 
     
    191195        self.serial_mode = serial_mode 
    192196        self.backout_all = backout_all 
    193         self.continue_on_fail = continue_on_fail 
    194  
     197        self.on_fail_continue = on_fail_continue 
     198        self.on_fail_retry = on_fail_retry 
     199        self.max_retries = max_retries 
     200        self.retries = 0 
    195201        self.pre_impl = pre_impl 
    196202        self.impl = impl 
     
    206212        pass 
    207213 
    208     def __str__(self): 
     214    def __repr__(self): 
    209215        return '<%s: %s>' % ( self.__class__, self.name ) 
    210216 
     
    427433 
    428434        namespace = self.namespace 
     435 
     436    def can_retry(self): 
     437        """ 
     438        Check to see if this change is allowed to retry after failing. 
     439        """ 
     440        if self.on_fail_retry: 
     441            log.debug("Onfail-retry is enabled") 
     442            if self.retries < self.max_retries: 
     443                log.debug("Retries: %d is less than retry max: %s. Will retry change.", self.retries, self.max_retries) 
     444                self.retries += 1 
     445                return True 
     446            else: 
     447                log.debug("Too many retries for change. Will not retry again.") 
     448            pass 
     449        return False 
    429450         
    430451if __name__ == '__main__': 
  • trunk/confloader.py

    r26 r27  
    2222import debug 
    2323 
    24 log = logging.getLogger('configulator') 
     24log = logging.getLogger('modipy') 
    2525 
    2626xinclude_re = re.compile(r'.*<xi:include href=[\'\"](?P<uri>.*)[\'\"].*') 
     
    7878 
    7979    def update(self, dict): 
    80         log.debug("Updating a namespace!") 
     80        for key in dict: 
     81            self.namespace[key] = dict[key] 
    8182 
    8283    def __iter__(self): 
     
    102103                return self.namespace.next() 
    103104            except AttributeError: 
    104                 log.debug("i have no parent!") 
    105105                raise StopIteration 
    106106             
     
    114114        return items 
    115115 
    116  
    117116class ConfigLoader: 
    118117    """ 
     
    121120    """ 
    122121 
    123     def __init__(self, configfile=None, devices=[]): 
     122    def __init__(self, options=None, devices=[]): 
    124123 
    125124        self.doc = None 
     125 
     126        self.options = options 
    126127 
    127128        self.provisioners = {} 
     
    138139        log.debug("created ConfigLoader") 
    139140 
    140         if configfile: 
    141             self.parse(configfile) 
     141        if options.configfile: 
     142            self.parse(options.configfile) 
    142143            pass 
    143144 
     
    196197##             self.dependency_tree = element 
    197198             
    198     def parse(self, configfile=None): 
     199    def parse(self, configfile): 
    199200        """ 
    200201        Parse my configuration file. 
    201202        """ 
    202         if configfile is None: 
    203             configfile = self.configfile 
    204             pass 
    205          
    206203        self.tree = etree.parse(configfile) 
    207204 
     
    291288        prov_klass = node.attrib['type'] 
    292289        prov_name = node.attrib['name'] 
     290 
     291        # Copy the attribs into a dictionary for use as kwargs 
     292        kwargs = {} 
     293        for key in node.attrib: 
     294            if key not in ['type', 'name']: 
     295                kwargs[key] = node.attrib[key] 
     296 
    293297        log.debug("Attempting to create a '%s' provisioner", prov_klass) 
    294298        prov_module = __import__('provisioner') 
    295299        klass = getattr( prov_module, prov_klass ) 
    296         provisioner = klass() 
     300        try: 
     301            provisioner = klass(prov_name, authoritarian=self.options.authoritarian, **kwargs) 
     302        except TypeError, e: 
     303            log.error("Incorrect parameter supplied for provisioner of type '%s'", prov_klass) 
     304            raise e 
    297305 
    298306        log.debug("created provisioner '%s': %s", prov_name, provisioner) 
     
    334342        change_klass = node.attrib['type'] 
    335343        change_name = node.attrib['name'] 
     344 
    336345        log.debug("Attempting to create a '%s' change", change_klass) 
    337346 
     
    341350            log.info("change module not yet imported. Importing...") 
    342351            change_module = __import__('change') 
    343              
    344         klass = getattr( change_module, change_klass ) 
    345  
     352            pass 
     353 
     354        klass = getattr(change_module, change_klass) 
    346355        change = klass(change_name) 
    347356 
     
    451460                log.error("Iterator '%s' is not defined", itername) 
    452461                raise 
    453          
     462 
     463        # Add an optional onfail mode 
     464        log.debug("Checking for 'onfail' attribute...") 
     465        try: 
     466            onfail = node.attrib['onfail'] 
     467            log.debug("onfail node found.") 
     468            if onfail == 'continue': 
     469                change.on_fail_continue = True 
     470                log.debug("Processing will continue if this change fails") 
     471 
     472            elif onfail == 'retry': 
     473                change.on_fail_retry = True 
     474                log.debug("This change will retry on failure") 
     475                try: 
     476                    max_retries = int(node.attrib['max_retries']) 
     477                    change.max_retries = max_retries 
     478                except KeyError: 
     479                    pass 
     480                log.debug("  This change will retry at most %d times", change.max_retries) 
     481        except KeyError: 
     482            pass 
     483 
    454484        # If the change doesn't have a provisioner, use the first one 
    455485        # in our config by default. 
     
    497527        log.debug("pending changes: %s", self.pending_changes) 
    498528        for change in self.pending_changes: 
     529            log.debug("testing change %s", change) 
    499530            if len(change.pre_requisites) == 0: 
    500531                changelist.append(change) 
    501                 log.debug("change '%s' has no pre-reqs. Adding.", change) 
     532                log.debug("change '%s' has no pre-reqs. Adding to execution queue.", change) 
    502533                continue 
     534 
    503535            else: 
    504536                all_prereqs = True 
    505                 log.debug("checking change pre-requsisites: %s", change.pre_requisites) 
    506537                for prereq in change.pre_requisites: 
    507538                    log.debug("change '%s' has a pre-req of '%s'", change, prereq) 
    508539                    if prereq not in self.change_success: 
    509                         log.debug("pre-req '%s' not complete yet. skipping", prereq) 
     540 
     541                        # If a prereq has failed, doesn't need to retry, and is 
     542                        # marked as 'onfail:continue', then we treat it as if 
     543                        # this prereq has been met. 
     544                        if prereq.state not in [ CHANGE_STATE['pending'], 
     545                                                 CHANGE_STATE['retry'], 
     546                                                 ] and prereq.on_fail_continue: 
     547                            log.debug("prereq failed, but is marked onfail:continue.") 
     548                            continue 
     549 
     550                        log.debug("pre-req '%s' not complete yet.", prereq) 
    510551                        all_prereqs = False 
    511552                        break 
    512553                    pass 
    513554 
    514                 log.debug("All pre-reqs for '%s' have completed. Adding.", change) 
    515555                if all_prereqs: 
     556                    log.debug("All pre-reqs for '%s' have completed. Adding to execution queue.", change) 
    516557                    changelist.append(change) 
    517558                else: 
     
    547588            self.backout_failure.append(change) 
    548589            self.pending_changes.remove(change) 
    549              
     590 
     591        elif change.state == CHANGE_STATE['retry']: 
     592            # Change will be retried 
     593            pass 
     594         
    550595        else: 
    551596            log.error("Unknown/unhandled change state '%s'", change.state) 
     
    647692            d = change.provisioner.perform_change(None, change, self.cfgldr.global_namespace) 
    648693            d.addCallback(self.change_complete, change) 
    649             d.addErrback(self.change_complete, change) 
     694            d.addErrback(self.change_failure, change) 
    650695            dlist.append(d) 
    651696            pass 
     
    662707        self.cfgldr.change_complete(change) 
    663708 
    664     def change_failure(self, failure): 
     709    def change_failure(self, failure, change): 
    665710        log.error("Major change failure!") 
    666711        tlog.err(failure) 
     712        self.change_complete(failure, change) 
    667713 
    668714    def print_stats(self, ignored): 
  • trunk/debug.py

    r14 r27  
    3232    handler = logging.handlers.RotatingFileHandler(filename=filename, maxBytes=10e6, backupCount=10) 
    3333    handler.setFormatter(formatter) 
    34     log = logging.getLogger('configulator') 
     34    log = logging.getLogger('modipy') 
    3535    log.addHandler(handler) 
    3636     
  • trunk/etc/netapp-provision-demo.xml

    r24 r27  
    1515<provisioner 
    1616  name='netapp_provisioner' 
    17   type='MultiConnectingProvisioner'> 
     17  type='MultiConnectingProvisioner' 
     18  command_timeout='5'> 
    1819 
    1920  <command>ssh -i /home/daedalus/.ssh/configulator -o BatchMode=yes -o ServerAliveInterval=0 root@%(device.ipaddress)s "%(command.send)s"</command> 
     
    6061</iterator> 
    6162 
    62 <change name="create_root_volume_primary" type="CommandChange"> 
     63<change name="create_root_volume_primary" type="CommandChange" onfail='continue'> 
    6364 
    6465  <target>%(primary_filer)s</target> 
     
    99100</change> 
    100101 
    101 <change name="create_project_volume_primary" type="CommandChange" iterator="project_volumes"> 
     102<change name="create_project_volume_primary" type="CommandChange" iterator="project_volumes" 
     103  onfail='retry' max_retries='2'> 
    102104 
    103105  <prereq>create_root_volume_primary</prereq> 
  • trunk/modipy.py

    r26 r27  
    1111 
    1212import logging 
    13 log = logging.getLogger('configulator') 
     13log = logging.getLogger('modipy') 
    1414 
    1515optparser = ChangeOptions() 
     
    1717 
    1818try: 
    19     cfgldr = ConfigLoader(optparser.options.configfile, optparser.args) 
     19    cfgldr = ConfigLoader(optparser.options, optparser.args) 
    2020except Exception, e: 
    2121    log.error("Cannot load configuration: %s", e) 
  • trunk/options.py

    r20 r27  
    1818from twisted.python import log as tlog 
    1919 
    20 log = logging.getLogger('configulator') 
     20log = logging.getLogger('modipy') 
    2121 
    2222class BaseOptions(optparse.OptionParser): 
     
    3232        help_license = 'Display the license agreement and exit.' 
    3333        help_debug = "Set the output debug level to: debug, info, warn, error, or critical." 
    34         help_logfile = "Log to specified file instead of default logfile" 
    35         help_no_logfile = "Disable logging to logfile" 
     34#        help_logfile = "Log to specified file instead of default logfile" 
     35#        help_no_logfile = "Disable logging to logfile" 
    3636 
     37         
    3738        self.add_option('', '--license',       dest='license', action='store_true', help=help_license)     
    3839        self.add_option('', '--debug',         dest='debug', type='choice', choices=('debug', 'info', 'warn', 'error', 'critical'), metavar='LEVEL', default='info', help=help_debug) 
    39         self.add_option('', '--logfile',       dest='logfile', type='string', help=help_logfile) 
    40         self.add_option('', '--no-logfile',       dest='no-logfile', action='store_true', default=False, help=help_no_logfile) 
    41          
     40#        self.add_option('', '--logfile',       dest='logfile', type='string', help=help_logfile) 
     41#        self.add_option('', '--no-logfile',       dest='no-logfile', action='store_true', default=False, help=help_no_logfile) 
     42 
    4243        self.addOptions() 
    4344 
     
    7576 
    7677    def addOptions(self): 
     78        help_authoritarian = "Authoritarian mode. Confirm every change command." 
    7779        help_configfile = "The configuration file to load" 
    78         #help_loadonly = "Load the configuration file and exit. Used to test parsing." 
     80        help_loadonly = "Load the configuration file and exit. Used to test parsing." 
    7981         
     82 
     83        self.add_option('-a', '--authoritarian',  dest='authoritarian', action='store_true', default=False, help=help_authoritarian)         
    8084        self.add_option('-c', '--configfile', dest='configfile', type='string', help=help_configfile) 
    81         #self.add_option('', '--loadonly', dest='loadonly', action='store_true', default=False, help=help_configfile) 
     85        self.add_option('', '--loadonly', dest='loadonly', action='store_true', default=False, help=help_loadonly) 
    8286 
    8387    def check_values(self, options, args): 
  • trunk/provisioner.py

    r26 r27  
    1717from twisted.internet import protocol 
    1818from twisted.python import log as tlog 
    19 from twisted.internet.error import ProcessDone, ProcessTerminated 
     19from twisted.internet.error import ProcessDone, ProcessTerminated, AlreadyCancelled, AlreadyCalled 
    2020 
    2121from change import ChangeFailed, ChangeConditionFailure, CHANGE_STATE 
     
    2626 
    2727import debug 
    28 log = logging.getLogger('configulator') 
     28log = logging.getLogger('modipy') 
    2929 
    3030from twisted.internet.base import DelayedCall 
     
    6363    implements( IProvisioner, ) 
    6464 
    65     def __init__(self, name='', namespace={}): 
     65    def __init__(self, name='', namespace={}, authoritarian=False, **kwargs): 
    6666        self.name = name 
    6767        self.connectedDevice = None 
    6868        self.namespace = namespace 
     69        self.authoritarian = authoritarian 
     70 
     71    def parse_config_node(self, node): 
     72        pass 
    6973 
    7074    def perform_change(self, ignored, change, namespace={}): 
     
    109113 
    110114                log.debug("applying change with namespace: %s", namespace) 
    111                  
    112 ##                 for key in self.namespace: 
    113 ##                     if namespace.has_key(key): 
    114 ##                         log.warning("Conflicting namespace keys: %s from %s conflicts with %s from global namespace" % (key, self, key) ) 
    115 ##                         pass 
    116 ##                     namespace[key] = self.namespace[key] 
    117  
    118 ##                 # Then the change namespace overrides 
    119 ##                 for key in change.namespace: 
    120 ##                     if namespace.has_key(key): 
    121 ##                         log.warning("Conflicting namespace keys: %s from %s conflicts with %s from global namespace" % (key, change, key) ) 
    122 ##                         pass 
    123 ##                     namespace[key] = change.namespace[key] 
    124                      
    125 ##                 for key in device.namespace: 
    126 ##                     if namespace.has_key[key]: 
    127 ##                         log.warning("Conflicting namespace keys: %s from %s conflicts with %s from global namespace" % (key, device, key) ) 
    128 ##                         pass 
    129 ##                     namespace[key] = device.namespace[key] 
    130                          
    131                 d.addCallback( self.apply_change, device, change, namespace) 
     115                d.addCallback(self.apply_change, device, change, namespace) 
    132116 
    133117                d.addCallback(self.change_complete_success, change, namespace) 
     
    175159        d = self.backout_change(None, device, change, namespace) 
    176160 
    177         if change.continue_on_fail: 
     161        if change.on_fail_continue: 
    178162            log.info("Attempting to continue, despite failure...") 
    179163            change.set_state('partial_failure') 
     
    191175    def change_failure(self, failure, change, namespace): 
    192176        log.debug("Change failure for %s: %s", change.name, failure.value) 
    193         if change.state == CHANGE_STATE['pending']: 
    194             change.state = CHANGE_STATE['total_failure'] 
     177        if change.state in [ CHANGE_STATE['pending'], CHANGE_STATE['retry'] ]: 
     178            # If the change is marked as 'on_fail: retry', let it retry 
     179            if change.can_retry(): 
     180                change.state = CHANGE_STATE['retry'] 
     181            else: 
     182                change.state = CHANGE_STATE['total_failure'] 
    195183        else: 
    196184            change.state = CHANGE_STATE['backout_failed'] 
     
    199187 
    200188    def change_complete_success(self, result, change, namespace): 
    201         if change.state == CHANGE_STATE['pending']: 
     189        if change.state in [ CHANGE_STATE['pending'], CHANGE_STATE['retry'] ]: 
    202190            change.state = CHANGE_STATE['success'] 
    203191            log.info("Change '%s' was a success!", change.name) 
     
    258246    command_re = re.compile(r'^(?P<start>.*)"(?P<quoted>.*)"(?P<end>.*)$') 
    259247 
     248    def parse_config_node(self, node): 
     249        Provisioner.parse_config_node(self, node) 
     250        pass 
     251 
     252    def __init__(self, name='', namespace={}, authoritarian=False, command_timeout=300): 
     253        Provisioner.__init__(self, name, namespace, authoritarian) 
     254        self.command_timeout = int(command_timeout) 
     255 
    260256    def split_command(self, cmdstring): 
    261257        """ 
     
    298294    device via STDIN, and receives the output from STDOUT and STDERR. 
    299295    """ 
     296    def parse_config_node(self, node): 
     297        CommandProvisioner.parse_config_node(self, node) 
    300298 
    301299    def connect(self, device): 
     
    327325        log.error("Connection to child timed out!") 
    328326        self.ep.transport.loseConnection() 
    329         self.childConnectingDefer.errback( ValueError("Connection timed out") ) 
     327        self.childConnectingDefer.errback( Exception("Connection timed out") ) 
    330328 
    331329    def childConnectSuccess(self): 
     
    454452        # check the expr to see if we should process it 
    455453        if expr is None: 
    456             log.debug("No expression to wait for. Running command '%s' immediately." % cmdstring) 
    457454            if cmdstring is not None: 
    458                 self.transport.writeToChild(0, '%s\n' % cmdstring) 
    459                 self.data_wait_timeout = reactor.callLater(10, self.data_wait_too_long) 
     455                log.debug("No expression to wait for. Running command '%s' immediately." % cmdstring) 
     456                self.issue_command(cmdstring) 
     457            else: 
     458                log.debug("cmdstring is 'None', so nothing to do") 
    460459 
    461460        else: 
     
    477476                 
    478477                if cmdstring is not None: 
    479                     log.debug("running command: %s" % cmdstring) 
    480                     self.transport.writeToChild(0, '%s\n' % cmdstring) 
    481                     self.data_wait_timeout = reactor.callLater(10, self.data_wait_too_long) 
     478                    self.issue_command(cmdstring) 
    482479                    pass 
    483480                else: 
     
    494491            pass 
    495492 
     493    def issue_command(self, cmdstring): 
     494        """ 
     495        Write the command to the remote device. 
     496        """ 
     497        # If we're in authoritarian mode, wait for confirmation 
     498        # that we should execute the command. 
     499        if self.parent.authoritarian: 
     500            log.debug("Authoritarian mode. Waiting for ok to proceed...") 
     501            isok = raw_input("Issue command (y/n)[n]?> ") 
     502            if isok.startswith('y'): 
     503                log.debug("Ok! Let's continue!") 
     504            else: 
     505                log.info(" Bailing out at your command.") 
     506                self.waitingForCommand.errback("User requested manual bailout.") 
     507                return 
     508            pass 
     509        log.debug("running command: %s" % cmdstring)                     
     510        self.transport.writeToChild(0, '%s\n' % cmdstring) 
     511        self.data_wait_timeout = reactor.callLater(10, self.data_wait_too_long) 
    496512 
    497513    def data_wait_too_long(self): 
     
    516532        Deals with additional configuration that I expect to be provided with. 
    517533        """ 
     534        ConnectingProvisioner.parse_config_node(self, node) 
    518535        log.debug("Parsing config node: %s", node) 
    519536        # parse command definition 
     
    572589                cmd = self.split_command(command) 
    573590                log.debug("command is: %s", cmd) 
    574                 d.addCallback(self.spawn_command, cmd) 
     591                d.addCallback(self.spawn_command, cmd, self.command_timeout) 
    575592                d.addCallbacks(self.command_completed, self.command_failed) 
    576593                pass 
     
    586603        will fire based on the results. 
    587604        """ 
     605        log.debug("spawning command...") 
     606        self.waitingForCommand = defer.Deferred() 
     607        # If we're in authoritarian mode, wait for confirmation 
     608        # that we should execute the command. 
     609        log.debug("Checking for authoritarian mode...") 
     610        if self.authoritarian: 
     611            log.debug("Authoritarian mode. Waiting for ok to proceed...") 
     612            isok = raw_input("Issue command: %s\n(y/n)[n]?> " % ' '.join(cmd) ) 
     613            if isok.startswith('y'): 
     614                log.debug("Ok! Let's continue!") 
     615            else: 
     616                log.info("Bailing out at your command.") 
     617                self.exitcode = -1 
     618                self.cmdoutput = 'User requested manual bailout' 
     619                self.waitingForCommand.errback( Exception("User requested manual bailout.") ) 
     620                return self.waitingForCommand 
     621            pass 
     622 
    588623        log.debug("running command remotely: %s" % cmd) 
    589         self.waitingForCommand = defer.Deferred() 
    590624        self.ep = SingleCommandProtocol(self, timeout) 
    591625        self.p = reactor.spawnProcess(self.ep, cmd[0], cmd) 
     626        log.debug("Spawned process: %s", self.p) 
     627 
    592628        return self.waitingForCommand 
    593629 
     
    602638        self.cmdoutput += result[1] 
    603639        log.debug("current results: exit '%d', output: %s", self.exitcode, self.cmdoutput) 
    604  
     640        log.error("Process: %s", self.p) 
     641         
    605642    def command_failed(self, failure): 
    606643        errorstr = "%s: %s" % (self.exitcode, self.cmdoutput) 
    607644        log.error("Command failed: %s", errorstr) 
    608         self.all_commands_defer.errback( Exception(errorstr) ) 
     645        log.error("Process: %s", self.p) 
     646        self.exitcode = result[0] 
     647        self.cmdoutput += result[1] 
     648        #self.all_commands_defer.errback( Exception(errorstr) ) 
    609649 
    610650    def all_commands_done(self, result): 
     
    650690 
    651691    def processEnded(self, status_object): 
    652         #log.debug("process ended: %s" % status_object) 
     692        log.debug("process ended: %s" % status_object) 
    653693        try: 
    654694            if isinstance(status_object.value, ProcessDone): 
     
    659699             
    660700            elif isinstance(status_object.value, ProcessTerminated): 
    661                 log.debug("process did not exit ok: %s: %s" % (status_object.value.exitCode, self.databuf)) 
     701                log.debug("process did not exit ok: %s: [%s: %s] %s" % (status_object.value.exitCode, status_object.value.signal, status_object.value.status, self.databuf)) 
    662702                self.exitCode = status_object.value.exitCode 
    663703                self.timeout.cancel() 
    664704                self.parent.waitingForCommand.callback( (self.exitCode, self.databuf) ) 
     705                pass 
    665706                 
    666707        except Exception, e: 
    667             log.error("erk: not good") 
    668             traceback.print_exc(e) 
    669             self.timeout.cancel() 
    670             self.parent.waitingForCommand.errback( e ) 
     708            log.error("Exception raised during end of process processing. That's quite bad.") 
     709            raise 
     710##             traceback.print_exc(e) 
     711##             self.timeout.cancel() 
     712##             self.parent.waitingForCommand.errback( e ) 
    671713 
    672714    def timedOut(self): 
  • trunk/test_ssh.py

    r8 r27  
    1111 
    1212import logging 
    13 log = logging.getLogger('configulator') 
     13log = logging.getLogger('modipy') 
    1414log.setLevel(logging.DEBUG) 
    1515 
  • trunk/util.py

    r18 r27  
    33# 
    44import logging 
    5 log = logging.getLogger('configulator') 
     5log = logging.getLogger('modipy') 
    66 
    77def substituteVariables(str, namespace={}):