1# Copyright 2015 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Provides a web interface for loading graph data from the production server. 6 7This is meant to be used on a dev server only. 8""" 9 10import base64 11import json 12import os 13import urllib 14 15from google.appengine.api import app_identity 16from google.appengine.api import urlfetch 17from google.appengine.ext import ndb 18from google.appengine.ext.ndb import model 19 20from dashboard import datastore_hooks 21from dashboard import request_handler 22from dashboard import update_test_suites 23 24_DEV_APP_ID = 'dev~' + app_identity.get_application_id() 25 26_PROD_DUMP_GRAPH_JSON_URL = 'https://chromeperf.appspot.com/dump_graph_json' 27 28 29class LoadFromProdHandler(request_handler.RequestHandler): 30 """Debugging handler to load data from the production instance.""" 31 32 def get(self): 33 if 'Development' not in os.environ['SERVER_SOFTWARE']: 34 self.RenderHtml('result.html', { 35 'errors': [ 36 'This should not be run in production, only on dev server.']}) 37 return 38 self.RenderHtml('load_from_prod.html', {}) 39 40 def post(self): 41 """Loads the requested data from the production server.""" 42 if 'Development' not in os.environ['SERVER_SOFTWARE']: 43 self.RenderHtml('result.html', { 44 'errors': [ 45 'This should not be run in production, only on dev server.']}) 46 return 47 48 sheriff = self.request.get('sheriff') 49 test_path = self.request.get('test_path') 50 if test_path: 51 num_points = self.request.get('num_points') 52 end_rev = self.request.get('end_rev') 53 url = ('%s?test_path=%s&num_points=%s' % 54 (_PROD_DUMP_GRAPH_JSON_URL, urllib.quote(test_path), num_points)) 55 if end_rev: 56 url += '&end_rev=%s' % end_rev 57 elif sheriff: 58 sheriff_name = self.request.get('sheriff') 59 num_alerts = self.request.get('num_alerts') 60 num_points = self.request.get('num_points') 61 url = ('%s?sheriff=%s&num_alerts=%s&num_points=%s' % 62 (_PROD_DUMP_GRAPH_JSON_URL, urllib.quote(sheriff_name), num_alerts, 63 num_points)) 64 else: 65 self.RenderHtml('result.html', { 66 'errors': ['Need to specify a test_path or sheriff.']}) 67 return 68 69 # This takes a while. 70 response = urlfetch.fetch(url, deadline=60) 71 if response.status_code != 200: 72 self.RenderHtml('result.html', {'errors': ['Could not fetch %s' % url]}) 73 return 74 protos = json.loads(response.content) 75 76 kinds = ['Master', 'Bot', 'TestMetadata', 'Row', 'Sheriff', 'Anomaly'] 77 entities = {k: [] for k in kinds} 78 for proto in protos: 79 pb = model.entity_pb.EntityProto(base64.b64decode(proto)) 80 # App ID is set in the key and all the ReferenceProperty keys to 81 # 's~chromeperf'. It won't be found in queries unless we use the 82 # devserver app ID. 83 key = pb.mutable_key() 84 key.set_app(_DEV_APP_ID) 85 for prop in pb.property_list(): 86 val = prop.mutable_value() 87 if val.has_referencevalue(): 88 ref = val.mutable_referencevalue() 89 ref.set_app(_DEV_APP_ID) 90 entity = ndb.ModelAdapter().pb_to_entity(pb) 91 entities[entity.key.kind()].append(entity) 92 93 for kind in kinds: 94 ndb.put_multi(entities[kind]) 95 96 update_test_suites.UpdateTestSuites(datastore_hooks.INTERNAL) 97 update_test_suites.UpdateTestSuites(datastore_hooks.EXTERNAL) 98 99 num_entities = sum(len(entities[kind]) for kind in kinds) 100 self.RenderHtml('result.html', { 101 'results': [ 102 { 103 'name': 'Added data', 104 'value': '%d entities' % num_entities 105 } 106 ] 107 }) 108