Anonymous avatar Anonymous committed f621ede

Added --sshargs --sshpath switches and search_path(): find ssh on PATH

Improved docstrings
Back off on OSError in SSHJobMan.add_jobs() uses self.sleeptime
Commend on why self.start() called from .poll() if necessary

Comments (0)

Files changed (3)

 __author__  = '''Jim Dennis <answrguy@gmail.com>'''
 __url__     = '''http://bitbucket.org/jimd/classh/'''
 __license___= '''PSF (Python Software Foundation)'''
-__version__ = 0.09
+__version__ = 0.091
 
 
 import sys, os, string, signal, re
     '''
 
     def __init__(self, hostlist=None, cmd=None, **opts):
-        '''
+        '''Prepare job with a list of targets and a command.
+           
+           Optional settings:
+               ssh (path to the default ssh binary):        (found on PATH)
+               ssh_args (default ssh arguments):            -oBatchMode=yes
+               poolsize (number of concurrent jobs):        100
+               jobtimeout (after which a job is killed):    300 seconds
+               sleeptime                                    0.2 seconds
+                 (sleeptime is called by wait() and for back off if
+                  Popen() raises an OSError).
         '''
         self.started = False
         self.poolsize = 100
         self.mergeout = dict()
         self.mergeerr = dict()
         
-        self.ssh = [ '/usr/bin/ssh' ]        #TODO: dynamically find this
+        self.ssh = [ search_path('ssh') ]
         self.ssh_args = [ '-oBatchMode=yes'] #TODO: add other batch options?
 
         self.targets = hostlist
         
 
     def start(self):
-        '''Set instance started flag
-           Prime the pool with new jobs
+        '''Set instance started flag and prime the pool with new jobs
 
-           This is deferred from initialization to
-           allow the caller to hook self.poll() into
-           any signal handling (SIGCHILD, SIGALARM), etc.
+           This is deferred from initialization to allow the caller 
+           to hook self.poll() into any signal handling (SIGCHILD, SIGALARM), 
+           etc.
+ 
+           This will be called by poll() if necessary
         '''
 
         debug ('Starting')
                 ## Sleep briefly and retry
                 debug('Popen failure %s' % e.errno)
                 self.targets.insert(0, host)
-                sleep(0.1)
+                self.sleeptime
                 return 
 
             self.results[key] = 'Placeholder'
 
     def poll(self):
         '''Scan pool for completed jobs, 
-           call gather() for any of those
-           remove completed jobs from pool
-           call add_jobs() to top off pool
-           return list of completed job keys
+           call gather() for any of those remove completed jobs from pool
+           call add_jobs() to top off pool return list of completed job keys
         '''
         
         debug ('polling')
-        if not self.started:
+        if not self.started:  # It was too each for forget to call this
             self.start()
         reaped = list() 
         for key, job in self.pool.items():
 
 
     def wait(self, maxwait=None, snooze=None):
-        '''
+        '''For for the job to complete, optionally for a maxwait time 
+
+           snooze over-rides the default sleeptime granularity.
         '''
         debug ('wait called called with: %s %s ' % (maxwait,snooze))
         return_after=None
     return results
 
 usage = '''
-usage %%prog [switches] cmd [hostname|./file] [host]./file ...]
+%%prog [switches] cmd [hostname|./file] [host]./file ...]
 version %s
 ''' % __version__
  
        '--mergemsgs', dest='mergemsgs', 
        action='store_true', default=False,
        help="Merge identical output/error messages")
+
+    parser.add_option(
+       '--sshargs', dest='ssh_args', 
+       action='store', type='string', default='-oBatchMode=yes',
+       help='Arguments to pass to the ssh for each jobs')
+
+    parser.add_option(
+       '--sshpath', dest='ssh_path', 
+       action='store', type='string', default='',
+       help='Arguments to pass to the ssh for each jobs')
+       
        
     opts, args = parser.parse_args()
     return (parser, opts, args)
 
+def search_path(executable):
+    '''Find ssh on the PATH
+    '''
+    for i in os.environ.get('PATH',os.defpath).split(os.pathsep): 
+        t = os.path.join(i, executable)
+        if os.access(t,os.X_OK): 
+            return t
+    # Else:
+    return None
+
 if __name__ == "__main__":
     '''First argument is command, rest are hosts.
        Dispatch jobs to hosts and incrementally print results
         handle_completed = lambda x,y : None
     if options.progressbar:
         handle_completed = show_progress
+    if not options.ssh_path:
+        options.ssh_path = search_path('ssh')
+    
     
     for arg in args[1:]:
         if '/' in arg:
 
     job = SSHJobMan(hosts, cmd, 
         poolsize=options.numjobs,
-        jobtimeout=options.timeout)
+        jobtimeout=options.timeout,
+        ssh_args=options.ssh_args.split(),
+        ssh=[options.ssh_path])
 
     job.start()
     
-    completed = None
     while not job.done():
         completed = job.poll()
         sleep(0.2)
 __author__  = '''Jim Dennis <answrguy@gmail.com>'''
 __url__     = '''http://bitbucket.org/jimd/classh/'''
 __license___= '''PSF (Python Software Foundation)'''
-__version__ = 0.09
+__version__ = 0.091
 
 
 import sys, os, string, signal, re
     '''
 
     def __init__(self, hostlist=None, cmd=None, **opts):
-        '''
+        '''Prepare job with a list of targets and a command.
+           
+           Optional settings:
+               ssh (path to the default ssh binary):        (found on PATH)
+               ssh_args (default ssh arguments):            -oBatchMode=yes
+               poolsize (number of concurrent jobs):        100
+               jobtimeout (after which a job is killed):    300 seconds
+               sleeptime                                    0.2 seconds
+                 (sleeptime is called by wait() and for back off if
+                  Popen() raises an OSError).
         '''
         self.started = False
         self.poolsize = 100
         self.mergeout = dict()
         self.mergeerr = dict()
         
-        self.ssh = [ '/usr/bin/ssh' ]        #TODO: dynamically find this
+        self.ssh = [ search_path('ssh') ]
         self.ssh_args = [ '-oBatchMode=yes'] #TODO: add other batch options?
 
         self.targets = hostlist
         
 
     def start(self):
-        '''Set instance started flag
-           Prime the pool with new jobs
+        '''Set instance started flag and prime the pool with new jobs
 
-           This is deferred from initialization to
-           allow the caller to hook self.poll() into
-           any signal handling (SIGCHILD, SIGALARM), etc.
+           This is deferred from initialization to allow the caller 
+           to hook self.poll() into any signal handling (SIGCHILD, SIGALARM), 
+           etc.
+ 
+           This will be called by poll() if necessary
         '''
 
         debug ('Starting')
                 ## Sleep briefly and retry
                 debug('Popen failure %s' % e.errno)
                 self.targets.insert(0, host)
-                sleep(0.1)
+                self.sleeptime
                 return 
 
             self.results[key] = 'Placeholder'
 
     def poll(self):
         '''Scan pool for completed jobs, 
-           call gather() for any of those
-           remove completed jobs from pool
-           call add_jobs() to top off pool
-           return list of completed job keys
+           call gather() for any of those remove completed jobs from pool
+           call add_jobs() to top off pool return list of completed job keys
         '''
         
         debug ('polling')
-        if not self.started:
+        if not self.started:  # It was too each for forget to call this
             self.start()
         reaped = list() 
         for key, job in self.pool.items():
 
 
     def wait(self, maxwait=None, snooze=None):
-        '''
+        '''For for the job to complete, optionally for a maxwait time 
+
+           snooze over-rides the default sleeptime granularity.
         '''
         debug ('wait called called with: %s %s ' % (maxwait,snooze))
         return_after=None
     return results
 
 usage = '''
-usage %%prog [switches] cmd [hostname|./file] [host]./file ...]
+%%prog [switches] cmd [hostname|./file] [host]./file ...]
 version %s
 ''' % __version__
  
        '--mergemsgs', dest='mergemsgs', 
        action='store_true', default=False,
        help="Merge identical output/error messages")
+
+    parser.add_option(
+       '--sshargs', dest='ssh_args', 
+       action='store', type='string', default='-oBatchMode=yes',
+       help='Arguments to pass to the ssh for each jobs')
+
+    parser.add_option(
+       '--sshpath', dest='ssh_path', 
+       action='store', type='string', default='',
+       help='Arguments to pass to the ssh for each jobs')
+       
        
     opts, args = parser.parse_args()
     return (parser, opts, args)
 
+def search_path(executable):
+    '''Find ssh on the PATH
+    '''
+    for i in os.environ.get('PATH',os.defpath).split(os.pathsep): 
+        t = os.path.join(i, executable)
+        if os.access(t,os.X_OK): 
+            return t
+    # Else:
+    return None
+
 if __name__ == "__main__":
     '''First argument is command, rest are hosts.
        Dispatch jobs to hosts and incrementally print results
         handle_completed = lambda x,y : None
     if options.progressbar:
         handle_completed = show_progress
+    if not options.ssh_path:
+        options.ssh_path = search_path('ssh')
+    
     
     for arg in args[1:]:
         if '/' in arg:
 
     job = SSHJobMan(hosts, cmd, 
         poolsize=options.numjobs,
-        jobtimeout=options.timeout)
+        jobtimeout=options.timeout,
+        ssh_args=options.ssh_args.split(),
+        ssh=[options.ssh_path])
 
     job.start()
     
-    completed = None
     while not job.done():
         completed = job.poll()
         sleep(0.2)
 #!/usr/bin/env python
+from classh import __version__
 from distutils.core import setup
 
+import sys
+print __version__
+sys.exit(1)
+
 setup(
     name='classh',
-    version='0.09',
+    version=__version__,
     author='James T. Dennis',
     author_email='answrguy@gmail.com',
     license='PSF',
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.