Index: gsd_generate_index/gsd_generate_index.py |
=================================================================== |
--- gsd_generate_index/gsd_generate_index.py (revision 0) |
+++ gsd_generate_index/gsd_generate_index.py (revision 0) |
@@ -0,0 +1,179 @@ |
+#!/usr/bin/python |
+# Copyright (c) 2008-2010 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+"""Generate index.html files for a Google Storage for Developers directory. |
+ |
+Google Storage for Developers provides only a raw set of objects. |
+For some buckets we would like to be able to support browsing of the directory |
+tree. This utility will generate the needed index and upload/update it. |
+""" |
+ |
+import optparse |
+import posixpath |
+import re |
+import subprocess |
+import sys |
+import tempfile |
+ |
+ |
+GENERATED_INDEX = '_index.html' |
+ |
+ |
+def PathToLink(path): |
+ return path.replace('gs://', 'https://sandbox.google.com/storage/') |
+ |
+ |
+def FixupSize(sz): |
+ """Convert a size string in bytes to human readable form. |
+ |
+ Arguments: |
+ sz: a size string in bytes |
+ Returns: |
+ A human readable size in bytes/K/M/G. |
+ """ |
+ sz = int(sz) |
+ if sz < 1000: |
+ sz = str(sz) |
+ elif sz < 1000000: |
+ sz = str(int(sz / 100) / 10.0) + 'K' |
+ elif sz < 1000000000: |
+ sz = str(int(sz / 100000) / 10.0) + 'M' |
+ else: |
+ sz = str(int(sz / 100000000) / 10.0) + 'G' |
+ return sz |
+ |
+ |
+def GetPathInfo(path, options): |
+ """Collect size, date, md5 for a give gsd path.""" |
+ # Check current state. |
+ cmd = [options.gsutil, 'ls', '-l', path] |
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
+ p_stdout, _ = p.communicate() |
+ # Extract intersting fields. |
+ fields = {} |
+ fields['size'] = FixupSize(re.search('\tObject size:\t([0-9]+)\n', |
+ p_stdout).group(1)) |
+ fields['md5'] = re.search('\tMD5:\t([^\n]+)\n', p_stdout).group(1) |
+ fields['date'] = re.search('\tLast mod:\t([^\n]+)\n', p_stdout).group(1) |
+ return fields |
+ |
+ |
+def GenerateIndex(path, children, directories, options): |
+ """Generate index for a given path as needed.""" |
+ # Generate index content. |
+ index = '' |
+ index += '<html>' |
+ index += '<head>' |
+ index += '<title>Index of %s</title>' % path |
+ index += '</head>' |
+ index += '<body>' |
+ index += '<h1>Index of %s</h1>' % path |
+ index += '<table>' |
+ index += '<tr>' |
+ index += '<th align="left">Name</th>' |
+ index += '<th align="left">Last modified</th>' |
+ index += '<th align="left">Size</th>' |
+ index += '<th align="left">MD5</th>' |
+ index += '</tr>' |
+ index += '<tr><th colspan="4"><hr></th></tr>' |
+ parent = posixpath.dirname(path) |
+ if parent != 'gs:': |
+ index += '<tr>' |
+ index += '<td><a href="%s">Parent Directory</a></td>' % ( |
+ PathToLink(posixpath.join(parent, GENERATED_INDEX))) |
+ index += '<td> </td>' |
+ index += '<td> </td>' |
+ index += '<td> </td>' |
+ index += '</tr>' |
+ for child in children: |
+ index += '<tr>' |
+ if child in directories: |
+ index += '<td><a href="%s">%s</a></td>' % ( |
+ PathToLink(posixpath.join(child, GENERATED_INDEX)), |
+ posixpath.basename(child)) |
+ index += '<td> </td>' |
+ index += '<td> </td>' |
+ index += '<td> </td>' |
+ else: |
+ fields = GetPathInfo(child, options) |
+ index += '<td><a href="%s">%s</a></td>' % ( |
+ PathToLink(child), posixpath.basename(child)) |
+ index += '<td>%s</td>' % fields['date'] |
+ index += '<td><b>%s</b></td>' % fields['size'] |
+ index += '<td>%s</td>' % fields['md5'] |
+ index += '</tr>' |
+ index += '<tr><th colspan="4"><hr></th></tr>' |
+ index += '</table>' |
+ index += '</body>' |
+ index += '</html>' |
+ # Check current state. |
+ cmd = [options.gsutil, 'cat', posixpath.join(path, GENERATED_INDEX)] |
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE) |
+ p_stdout, _ = p.communicate() |
+ # Done if it's alrady right. |
+ if p_stdout == index and not options.force: |
+ print '%s -- skipping, up to date' % path |
+ return |
+ # Write to a file. |
+ f = tempfile.NamedTemporaryFile(suffix='.html') |
+ filename = f.name |
+ f.write(index) |
+ f.flush() |
+ # Upload index. |
+ cmd = [options.gsutil, 'cp'] |
+ if options.acl: |
+ cmd += ['-a', options.acl] |
+ cmd += [filename, posixpath.join(path, GENERATED_INDEX)] |
+ p = subprocess.Popen(cmd) |
+ p.communicate() |
+ print '%s -- updated index' % path |
+ |
+ |
+def GenerateIndexes(path, options): |
+ """Generate all relevant indexes for a given gsd path.""" |
+ # Get a list of objects under this prefix. |
+ p = subprocess.Popen([options.gsutil, 'ls', posixpath.join(path, '*')], |
+ stdout=subprocess.PIPE) |
+ p_stdout, _ = p.communicate() |
+ objects = str(p_stdout).splitlines() |
+ objects = [o for o in objects if posixpath.basename(o) != GENERATED_INDEX] |
+ # Find common prefixes. |
+ directories = set() |
+ for o in objects: |
+ part = posixpath.dirname(o) |
+ while part.startswith(path): |
+ directories.add(part) |
+ part = posixpath.dirname(part) |
+ objects += list(directories) |
+ # Generate index for each directory. |
+ for d in directories: |
+ # Skip directories not on the target path if any. |
+ if options.path and not options.path.startswith(d): |
+ continue |
+ # Find just this directories children. |
+ children = [o for o in objects if posixpath.dirname(o) == d] |
+ # Generate this directory's index if needed. |
+ GenerateIndex(d, children, directories, options) |
+ return 0 |
+ |
+ |
+def main(argv): |
+ parser = optparse.OptionParser(usage='usage: %prog [options] gs://base-dir') |
+ parser.add_option('-p', '--path', dest='path', |
+ help='only update indexes on a given path') |
+ parser.add_option('-a', dest='acl', help='acl to set on indexes') |
+ parser.add_option('-f', '--force', action='store_true', default=False, |
+ dest='force', help='upload all indexes even on match') |
+ parser.add_option('', '--gsutil', default='gsutil', |
+ dest='gsutil', help='path to gsutil') |
+ options, args = parser.parse_args(argv) |
+ if len(args) != 2 or not args[1].startswith('gs://'): |
+ parser.print_help() |
+ return 1 |
+ return GenerateIndexes(args[1], options) |
+ |
+ |
+if __name__ == '__main__': |
+ sys.exit(main(sys.argv)) |
Property changes on: gsd_generate_index/gsd_generate_index.py |
___________________________________________________________________ |
Added: svn:executable |
+ * |