Home | History | Annotate | Download | only in interpreter
      1 # Copyright 2012 Google Inc. All Rights Reserved.
      2 # Author: mrdmnd@ (Matt Redmond)
      3 """A client to pull data from Bartlett.
      4 
      5 Inspired by //depot/google3/experimental/mobile_gwp/database/app_engine_pull.py
      6 
      7 The server houses perf.data.gz, board, chrome version for each upload.
      8 This script first authenticates with a proper @google.com account, then
      9 downloads a sample (if it's not already cached) and unzips perf.data
     10 
     11   Authenticate(): Gets login info and returns an auth token
     12   DownloadSamples(): Download and unzip samples.
     13   _GetServePage(): Pulls /serve page from the app engine server
     14   _DownloadSampleFromServer(): Downloads a local compressed copy of a sample
     15   _UncompressSample(): Decompresses a sample, deleting the compressed version.
     16 """
     17 import cookielib
     18 import getpass
     19 import gzip
     20 import optparse
     21 import os
     22 import urllib
     23 import urllib2
     24 
     25 SERVER_NAME = 'http://chromeoswideprofiling.appspot.com'
     26 APP_NAME = 'chromeoswideprofiling'
     27 DELIMITER = '~'
     28 
     29 
     30 def Authenticate(server_name):
     31   """Gets credentials from user and attempts to retrieve auth token.
     32      TODO: Accept OAuth2 instead of password.
     33   Args:
     34     server_name: (string) URL that the app engine code is living on.
     35   Returns:
     36     authtoken: (string) The authorization token that can be used
     37                         to grab other pages.
     38   """
     39 
     40   if server_name.endswith('/'):
     41     server_name = server_name.rstrip('/')
     42   # Grab username and password from user through stdin.
     43   username = raw_input('Email (must be @google.com account): ')
     44   password = getpass.getpass('Password: ')
     45   # Use a cookie to authenticate with GAE.
     46   cookiejar = cookielib.LWPCookieJar()
     47   opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
     48   urllib2.install_opener(opener)
     49   # Get an AuthToken from Google accounts service.
     50   auth_uri = 'https://www.google.com/accounts/ClientLogin'
     51   authreq_data = urllib.urlencode({'Email': username,
     52                                    'Passwd': password,
     53                                    'service': 'ah',
     54                                    'source': APP_NAME,
     55                                    'accountType': 'HOSTED_OR_GOOGLE'})
     56   auth_req = urllib2.Request(auth_uri, data=authreq_data)
     57   try:
     58     auth_resp = urllib2.urlopen(auth_req)
     59   except urllib2.URLError:
     60     print 'Error logging in to Google accounts service.'
     61     return None
     62   body = auth_resp.read()
     63   # Auth response contains several fields.
     64   # We care about the part after Auth=
     65   auth_resp_dict = dict(x.split('=') for x in body.split('\n') if x)
     66   authtoken = auth_resp_dict['Auth']
     67   return authtoken
     68 
     69 
     70 def DownloadSamples(server_name, authtoken, output_dir, start, stop):
     71   """Download every sample and write unzipped version
     72      to output directory.
     73   Args:
     74     server_name: (string) URL that the app engine code is living on.
     75     authtoken:   (string) Authorization token.
     76     output_dir   (string) Filepath to write output to.
     77     start:       (int)    Index to start downloading from, starting at top.
     78     stop:        (int)    Index to stop downloading, non-inclusive. -1 for end.
     79   Returns:
     80     None
     81   """
     82 
     83   if server_name.endswith('/'):
     84     server_name = server_name.rstrip('/')
     85 
     86   serve_page_string = _GetServePage(server_name, authtoken)
     87   if serve_page_string is None:
     88     print 'Error getting /serve page.'
     89     return
     90 
     91   sample_list = serve_page_string.split('</br>')
     92   print 'Will download:'
     93   sample_list_subset = sample_list[start:stop]
     94   for sample in sample_list_subset:
     95     print sample
     96   for sample in sample_list_subset:
     97     assert sample, 'Sample should be valid.'
     98     sample_info = [s.strip() for s in sample.split(DELIMITER)]
     99     key = sample_info[0]
    100     time = sample_info[1]
    101     time = time.replace(' ', '_')  # No space between date and time.
    102     # sample_md5 = sample_info[2]
    103     board = sample_info[3]
    104     version = sample_info[4]
    105 
    106     # Put a compressed copy of the samples in output directory.
    107     _DownloadSampleFromServer(server_name, authtoken, key, time, board, version,
    108                               output_dir)
    109     _UncompressSample(key, time, board, version, output_dir)
    110 
    111 
    112 def _BuildFilenameFromParams(key, time, board, version):
    113   """Return the filename for our sample.
    114   Args:
    115     key:  (string) Key indexing our sample in the datastore.
    116     time: (string) Date that the sample was uploaded.
    117     board: (string) Board that the sample was taken on.
    118     version: (string) Version string from /etc/lsb-release
    119   Returns:
    120     filename (string)
    121   """
    122   filename = DELIMITER.join([key, time, board, version])
    123   return filename
    124 
    125 
    126 def _DownloadSampleFromServer(server_name, authtoken, key, time, board, version,
    127                               output_dir):
    128   """Downloads sample_$(samplekey).gz to current dir.
    129   Args:
    130     server_name: (string) URL that the app engine code is living on.
    131     authtoken:   (string) Authorization token.
    132     key:  (string) Key indexing our sample in the datastore
    133     time: (string) Date that the sample was uploaded.
    134     board: (string) Board that the sample was taken on.
    135     version: (string) Version string from /etc/lsb-release
    136     output_dir:  (string) Filepath to write to output to.
    137   Returns:
    138     None
    139   """
    140   filename = _BuildFilenameFromParams(key, time, board, version)
    141   compressed_filename = filename + '.gz'
    142 
    143   if os.path.exists(os.path.join(output_dir, filename)):
    144     print 'Already downloaded %s, skipping.' % filename
    145     return
    146 
    147   serv_uri = server_name + '/serve/' + key
    148   serv_args = {'continue': serv_uri, 'auth': authtoken}
    149   full_serv_uri = server_name + '/_ah/login?%s' % urllib.urlencode(serv_args)
    150   serv_req = urllib2.Request(full_serv_uri)
    151   serv_resp = urllib2.urlopen(serv_req)
    152   f = open(os.path.join(output_dir, compressed_filename), 'w+')
    153   f.write(serv_resp.read())
    154   f.close()
    155 
    156 
    157 def _UncompressSample(key, time, board, version, output_dir):
    158   """Uncompresses a given sample.gz file and deletes the compressed version.
    159   Args:
    160     key: (string) Sample key to uncompress.
    161     time: (string) Date that the sample was uploaded.
    162     board: (string) Board that the sample was taken on.
    163     version: (string) Version string from /etc/lsb-release
    164     output_dir: (string) Filepath to find sample key in.
    165   Returns:
    166     None
    167   """
    168   filename = _BuildFilenameFromParams(key, time, board, version)
    169   compressed_filename = filename + '.gz'
    170 
    171   if os.path.exists(os.path.join(output_dir, filename)):
    172     print 'Already decompressed %s, skipping.' % filename
    173     return
    174 
    175   out_file = open(os.path.join(output_dir, filename), 'wb')
    176   in_file = gzip.open(os.path.join(output_dir, compressed_filename), 'rb')
    177   out_file.write(in_file.read())
    178   in_file.close()
    179   out_file.close()
    180   os.remove(os.path.join(output_dir, compressed_filename))
    181 
    182 
    183 def _DeleteSampleFromServer(server_name, authtoken, key):
    184   """Opens the /delete page with the specified key
    185      to delete the sample off the datastore.
    186     Args:
    187       server_name: (string) URL that the app engine code is living on.
    188       authtoken:   (string) Authorization token.
    189       key:  (string) Key to delete.
    190     Returns:
    191       None
    192   """
    193 
    194   serv_uri = server_name + '/del/' + key
    195   serv_args = {'continue': serv_uri, 'auth': authtoken}
    196   full_serv_uri = server_name + '/_ah/login?%s' % urllib.urlencode(serv_args)
    197   serv_req = urllib2.Request(full_serv_uri)
    198   urllib2.urlopen(serv_req)
    199 
    200 
    201 def _GetServePage(server_name, authtoken):
    202   """Opens the /serve page and lists all keys.
    203   Args:
    204     server_name: (string) URL the app engine code is living on.
    205     authtoken:   (string) Authorization token.
    206   Returns:
    207     The text of the /serve page (including HTML tags)
    208   """
    209 
    210   serv_uri = server_name + '/serve'
    211   serv_args = {'continue': serv_uri, 'auth': authtoken}
    212   full_serv_uri = server_name + '/_ah/login?%s' % urllib.urlencode(serv_args)
    213   serv_req = urllib2.Request(full_serv_uri)
    214   serv_resp = urllib2.urlopen(serv_req)
    215   return serv_resp.read()
    216 
    217 
    218 def main():
    219   parser = optparse.OptionParser()
    220   parser.add_option('--output_dir',
    221                     dest='output_dir',
    222                     action='store',
    223                     help='Path to output perf data files.')
    224   parser.add_option('--start',
    225                     dest='start_ind',
    226                     action='store',
    227                     default=0,
    228                     help='Start index.')
    229   parser.add_option('--stop',
    230                     dest='stop_ind',
    231                     action='store',
    232                     default=-1,
    233                     help='Stop index.')
    234   options = parser.parse_args()[0]
    235   if not options.output_dir:
    236     print 'Must specify --output_dir.'
    237     return 1
    238   if not os.path.exists(options.output_dir):
    239     print 'Specified output_dir does not exist.'
    240     return 1
    241 
    242   authtoken = Authenticate(SERVER_NAME)
    243   if not authtoken:
    244     print 'Could not obtain authtoken, exiting.'
    245     return 1
    246   DownloadSamples(SERVER_NAME, authtoken, options.output_dir, options.start_ind,
    247                   options.stop_ind)
    248   print 'Downloaded samples.'
    249   return 0
    250 
    251 
    252 if __name__ == '__main__':
    253   exit(main())
    254