[dev-util] Add stage_debug,symbolicate_dump endpoints to dev server

Add an endpoint to the dev server that will synchronously download
and stage the debug symbols for a given build.
Add an endpoint to the dev server that will symbolicate a minidump.

BUG=chromium-os:29850,chromium-os:30399
TEST=unit
TEST=run dev server, use curl to make it download some artifacts; ensure
TEST=debug.tgz is _not_ downloaded immediately, but that the rest of the build is staged.

TEST=run dev server, use curl to make it download debug_symbols; check to
TEST=see that debug symbols are staged in static/archive
TEST=once symbols are staged, run the dev server in your
TEST=chroot and use curl with a minidump file like this:
TEST=  curl -F minidump=@/home/cmasone/chromeos/phooey/powerd.20120424.141235.1005.dmp http://localhost:8080/symbolicate_dump

Change-Id: Ie460526396d2b9999137142c723b87793bc23aaa
Reviewed-on: https://gerrit.chromium.org/gerrit/21696
Reviewed-by: Chris Sosa <sosa@chromium.org>
Commit-Ready: Chris Masone <cmasone@chromium.org>
Tested-by: Chris Masone <cmasone@chromium.org>
diff --git a/devserver_util.py b/devserver_util.py
index eb498f4..c2a659b 100644
--- a/devserver_util.py
+++ b/devserver_util.py
@@ -8,7 +8,9 @@
 import distutils.version
 import errno
 import os
+import random
 import shutil
+import time
 
 import downloadable_artifact
 import gsutil_util
@@ -61,8 +63,10 @@
   """Generates artifacts that we mean to download and install for autotest.
 
   This method generates the list of artifacts we will need for autotest. These
-  artifacts are instances of downloadable_artifact.DownloadableArtifact.Note,
-  these artifacts can be downloaded asynchronously iff !artifact.Synchronous().
+  artifacts are instances of downloadable_artifact.DownloadableArtifact.
+
+  Note, these artifacts can be downloaded asynchronously iff
+  !artifact.Synchronous().
   """
   cmd = 'gsutil ls %s/*.bin' % archive_url
   msg = 'Failed to get a list of payloads.'
@@ -104,6 +108,49 @@
   return artifacts
 
 
+def GatherSymbolArtifactDownloads(temp_download_dir, archive_url, staging_dir,
+                                  timeout=600, delay=10):
+  """Generates debug symbol artifacts that we mean to download and stage.
+
+  This method generates the list of artifacts we will need to
+  symbolicate crash dumps that occur during autotest runs.  These
+  artifacts are instances of downloadable_artifact.DownloadableArtifact.
+
+  This will poll google storage until the debug symbol artifact becomes
+  available, or until the 10 minute timeout is up.
+
+  @param temp_download_dir: the tempdir into which we're downloading artifacts
+                            prior to staging them.
+  @param archive_url: the google storage url of the bucket where the debug
+                      symbols for the desired build are stored.
+  @param staging_dir: the dir into which to stage the symbols
+
+  @return an iterable of one DebugTarball pointing to the right debug symbols.
+          This is an iterable so that it's similar to GatherArtifactDownloads.
+          Also, it's possible that someday we might have more than one.
+  """
+  symbol_url = archive_url + '/' + downloadable_artifact.DEBUG_SYMBOLS
+  cmd = 'gsutil ls %s' % symbol_url
+  msg = 'Debug symbols for %s not archived.' % archive_url
+
+  deadline = time.time() + timeout
+  while time.time() < deadline:
+    to_delay = delay + random.choice([-1, 1]) * random.random() * .5 * delay
+    try:
+      gsutil_util.GSUtilRun(cmd, msg)
+      break
+    except gsutil_util.GSUtilError as e:
+      cherrypy.log('%s, Retrying in %f seconds...' % (e, to_delay),
+                   'SYMBOL_DOWNLOAD')
+      time.sleep(to_delay)
+  else:
+    # On the last try, run and allow exceptions to escape.
+    gsutil_util.GSUtilRun(cmd, msg)
+
+  return [downloadable_artifact.DebugTarball(symbol_url, temp_download_dir,
+                                             staging_dir)]
+
+
 def PrepareBuildDirectory(build_dir):
   """Preliminary staging of installation directory for build.