Anonymous avatar Anonymous committed bceca38

Add expanded range support, fix DEBUG check

Added support for expanding hostname ranges (in args and files). So
we can now use ww[1-20,23,40-90].foo.com and similar expressions to
expand into whole ranges of hosts.

Also fixed DEBUG check such that environ['DEBUG'] must point to argv[0]
to activate debugging output.

Comments (0)

Files changed (2)

    timeouts.
 '''
 
-__author__='''Jim Dennis <answrguy@gmail.com>'''
-__url__   ='''http://bitbucket.org/jimd/classh/'''
-__license___='''BSD'''
+__author__  = '''Jim Dennis <answrguy@gmail.com>'''
+__url__     = '''http://bitbucket.org/jimd/classh/'''
+__version__ = 0.03
+__license___= '''PSF (Python Software Foundation)'''
 
 
 from subprocess import Popen, PIPE
 from time import sleep, time
-import sys, os, string, signal
-## from signal import alarm
+import sys, os, string, signal, re
 
 
 def debug(str):   # TODO: use logging package?
-    if 'DEBUG' in os.environ:
+    if 'DEBUG' in os.environ and os.environ['DEBUG']==sys.argv[0]:
         print >> sys.stderr, str
 
 class SSHJobMan(object):
         return self.started and not len(self.pool.keys())
 
 
+
+# Expand ranges: 
+expr = re.compile(r'\[[0-9][-0-9,[]*\]')
+def range2list(s):
+    '''Given [x-y,a,b-c] return: range(x,y) + [a] + range(b,c)
+       Handle decrements and zero-filling if necessary!
+    '''
+    assert s.startswith('[') and s.endswith(']') 
+    if len(s) < 2:
+        return [s]
+    results = []
+    r = s[1:-1]  # extract from enclosing brackets
+    for i in r.split(','):  # each p
+        if '-' not in i:
+            results.append(i)
+            continue
+        # Else: (it's a range so...)
+        t = i.split('-')
+        if len(t) != 2:   # punt on degenerate expressions
+            results.append(i) 
+            continue
+        # Else:
+        if len(t[0]) > 1 and t[0].startswith('0'):
+            fmt = "%%0%sd" % len(t[0])  ## Handle zero fill
+        else:
+            fmt = "%s"
+        try:
+            l, u = int(t[0]), int(t[1])
+        except ValueError:  # punt on stuff that can't be converted
+            results.append(i) # remember i? There's a song about i.
+            continue 
+        if l > u:
+            step=-1
+            u -= 1          # Make range INCLUSIVE
+        else:
+            step=1
+            u += 1          # Make range INCLUSIVE
+        results.extend([fmt % x for x in range(l,u,step)])
+    return results 
+        
+
+def expand_range(s, r=None):
+    '''Return a list of strings with expressions like [n-m,p,t] expanded.
+
+       Examples:
+       >>> expand_range("foo")
+       ['foo']
+
+       >>> expand_range("foo[1,3,9]")
+       ['foo1', 'foo3', 'foo9']
+
+       >>> expand_range("bar[1-3]foo")
+       ['bar1foo', 'bar2foo', 'bar3foo']
+    '''
+
+    debug("\ter(%s,%s)" % (s,r))
+    results = [] 
+    rx = expr.search(s)  # range expression
+    if not rx:
+        return [s]  # other base case, wrap it in a list
+    b,e = rx.span()
+    fmt = s[:b] + "%s" + s[e:]  # foo[...]bar -> foo%sbar
+    m  = s[b:e]                 # first [...] from 
+    for i in range2list(m):
+        results.extend(expand_range(fmt % i))
+    return results 
+
+        
 # Use in print results to munge non-printable chars
 filter = string.maketrans(
            string.translate(string.maketrans('',''),
     for line in f:
         if line.endswith('\n'):
             line = line[:-1].strip()
-        results.append(line)
+        results.extend(expand_range(line))
     return results
 
 if __name__ == "__main__":
             hosts.extend(readfile(arg))
         else:
             ## TODO: expand host range expressions
-            hosts.append(arg)
+            hosts.extend(expand_range(arg))
 
     print >> sys.stderr, "About to run '%s' on %s hosts...\n\n" \
         % (cmd, len(hosts))
         
     summarize_results(job.results)
     print "Completed in %s seconds" % (time() - start)
-       
+
+
    timeouts.
 '''
 
-__author__='''Jim Dennis <answrguy@gmail.com>'''
-__url__   ='''http://bitbucket.org/jimd/classh/'''
-__license___='''BSD'''
+__author__  = '''Jim Dennis <answrguy@gmail.com>'''
+__url__     = '''http://bitbucket.org/jimd/classh/'''
+__version__ = 0.03
+__license___= '''PSF (Python Software Foundation)'''
 
 
 from subprocess import Popen, PIPE
 from time import sleep, time
-import sys, os, string, signal
-## from signal import alarm
+import sys, os, string, signal, re
 
 
 def debug(str):   # TODO: use logging package?
-    if 'DEBUG' in os.environ:
+    if 'DEBUG' in os.environ and os.environ['DEBUG']==sys.argv[0]:
         print >> sys.stderr, str
 
 class SSHJobMan(object):
         return self.started and not len(self.pool.keys())
 
 
+
+# Expand ranges: 
+expr = re.compile(r'\[[0-9][-0-9,[]*\]')
+def range2list(s):
+    '''Given [x-y,a,b-c] return: range(x,y) + [a] + range(b,c)
+       Handle decrements and zero-filling if necessary!
+    '''
+    assert s.startswith('[') and s.endswith(']') 
+    if len(s) < 2:
+        return [s]
+    results = []
+    r = s[1:-1]  # extract from enclosing brackets
+    for i in r.split(','):  # each p
+        if '-' not in i:
+            results.append(i)
+            continue
+        # Else: (it's a range so...)
+        t = i.split('-')
+        if len(t) != 2:   # punt on degenerate expressions
+            results.append(i) 
+            continue
+        # Else:
+        if len(t[0]) > 1 and t[0].startswith('0'):
+            fmt = "%%0%sd" % len(t[0])  ## Handle zero fill
+        else:
+            fmt = "%s"
+        try:
+            l, u = int(t[0]), int(t[1])
+        except ValueError:  # punt on stuff that can't be converted
+            results.append(i) # remember i? There's a song about i.
+            continue 
+        if l > u:
+            step=-1
+            u -= 1          # Make range INCLUSIVE
+        else:
+            step=1
+            u += 1          # Make range INCLUSIVE
+        results.extend([fmt % x for x in range(l,u,step)])
+    return results 
+        
+
+def expand_range(s, r=None):
+    '''Return a list of strings with expressions like [n-m,p,t] expanded.
+
+       Examples:
+       >>> expand_range("foo")
+       ['foo']
+
+       >>> expand_range("foo[1,3,9]")
+       ['foo1', 'foo3', 'foo9']
+
+       >>> expand_range("bar[1-3]foo")
+       ['bar1foo', 'bar2foo', 'bar3foo']
+    '''
+
+    debug("\ter(%s,%s)" % (s,r))
+    results = [] 
+    rx = expr.search(s)  # range expression
+    if not rx:
+        return [s]  # other base case, wrap it in a list
+    b,e = rx.span()
+    fmt = s[:b] + "%s" + s[e:]  # foo[...]bar -> foo%sbar
+    m  = s[b:e]                 # first [...] from 
+    for i in range2list(m):
+        results.extend(expand_range(fmt % i))
+    return results 
+
+        
 # Use in print results to munge non-printable chars
 filter = string.maketrans(
            string.translate(string.maketrans('',''),
     for line in f:
         if line.endswith('\n'):
             line = line[:-1].strip()
-        results.append(line)
+        results.extend(expand_range(line))
     return results
 
 if __name__ == "__main__":
             hosts.extend(readfile(arg))
         else:
             ## TODO: expand host range expressions
-            hosts.append(arg)
+            hosts.extend(expand_range(arg))
 
     print >> sys.stderr, "About to run '%s' on %s hosts...\n\n" \
         % (cmd, len(hosts))
         
     summarize_results(job.results)
     print "Completed in %s seconds" % (time() - start)
-       
+
+
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.