1# Copyright (C) 2010 Google Inc. All rights reserved. 2# 3# Redistribution and use in source and binary forms, with or without 4# modification, are permitted provided that the following conditions are 5# met: 6# 7# * Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above 10# copyright notice, this list of conditions and the following disclaimer 11# in the documentation and/or other materials provided with the 12# distribution. 13# * Neither the name of Google Inc. nor the names of its 14# contributors may be used to endorse or promote products derived from 15# this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29import logging 30import urllib 31 32from google.appengine.api import users 33from google.appengine.ext import webapp 34from google.appengine.ext.webapp import template 35 36from model.jsonresults import JsonResults 37from model.testfile import TestFile 38 39PARAM_MASTER = "master" 40PARAM_BUILDER = "builder" 41PARAM_DIR = "dir" 42PARAM_FILE = "file" 43PARAM_NAME = "name" 44PARAM_KEY = "key" 45PARAM_TEST_TYPE = "testtype" 46PARAM_INCREMENTAL = "incremental" 47PARAM_TEST_LIST_JSON = "testlistjson" 48 49 50class DeleteFile(webapp.RequestHandler): 51 """Delete test file for a given builder and name from datastore.""" 52 53 def get(self): 54 key = self.request.get(PARAM_KEY) 55 master = self.request.get(PARAM_MASTER) 56 builder = self.request.get(PARAM_BUILDER) 57 test_type = self.request.get(PARAM_TEST_TYPE) 58 name = self.request.get(PARAM_NAME) 59 60 logging.debug( 61 "Deleting File, master: %s, builder: %s, test_type: %s, name: %s, key: %s.", 62 master, builder, test_type, name, key) 63 64 TestFile.delete_file(key, master, builder, test_type, name, 100) 65 66 # Display file list after deleting the file. 67 self.redirect("/testfile?master=%s&builder=%s&testtype=%s&name=%s" 68 % (master, builder, test_type, name)) 69 70 71class GetFile(webapp.RequestHandler): 72 """Get file content or list of files for given builder and name.""" 73 74 def _get_file_list(self, master, builder, test_type, name): 75 """Get and display a list of files that matches builder and file name. 76 77 Args: 78 builder: builder name 79 test_type: type of the test 80 name: file name 81 """ 82 83 files = TestFile.get_files( 84 master, builder, test_type, name, load_data=False, limit=100) 85 if not files: 86 logging.info("File not found, master: %s, builder: %s, test_type: %s, name: %s.", 87 master, builder, test_type, name) 88 self.response.out.write("File not found") 89 return 90 91 template_values = { 92 "admin": users.is_current_user_admin(), 93 "master": master, 94 "builder": builder, 95 "test_type": test_type, 96 "name": name, 97 "files": files, 98 } 99 self.response.out.write(template.render("templates/showfilelist.html", 100 template_values)) 101 102 def _get_file_content(self, master, builder, test_type, name): 103 """Return content of the file that matches builder and file name. 104 105 Args: 106 builder: builder name 107 test_type: type of the test 108 name: file name 109 """ 110 111 files = TestFile.get_files( 112 master, builder, test_type, name, load_data=True, limit=1) 113 if not files: 114 logging.info("File not found, master %s, builder: %s, test_type: %s, name: %s.", 115 master, builder, test_type, name) 116 return None 117 118 return files[0].data 119 120 def _get_test_list_json(self, master, builder, test_type): 121 """Return json file with test name list only, do not include test 122 results and other non-test-data . 123 124 Args: 125 builder: builder name. 126 test_type: type of test results. 127 """ 128 129 json = self._get_file_content(master, builder, test_type, "results.json") 130 if not json: 131 return None 132 133 return JsonResults.get_test_list(builder, json) 134 135 def get(self): 136 master = self.request.get(PARAM_MASTER) 137 builder = self.request.get(PARAM_BUILDER) 138 test_type = self.request.get(PARAM_TEST_TYPE) 139 name = self.request.get(PARAM_NAME) 140 dir = self.request.get(PARAM_DIR) 141 test_list_json = self.request.get(PARAM_TEST_LIST_JSON) 142 143 logging.debug( 144 "Getting files, master %s, builder: %s, test_type: %s, name: %s.", 145 master, builder, test_type, name) 146 147 # If parameter "dir" is specified or there is no builder or filename 148 # specified in the request, return list of files, otherwise, return 149 # file content. 150 if dir or not builder or not name: 151 return self._get_file_list(master, builder, test_type, name) 152 153 if name == "results.json" and test_list_json: 154 json = self._get_test_list_json(master, builder, test_type) 155 else: 156 json = self._get_file_content(master, builder, test_type, name) 157 158 if json: 159 self.response.headers["Content-Type"] = "text/plain; charset=utf-8" 160 self.response.out.write(json) 161 else: 162 self.error(404) 163 164class Upload(webapp.RequestHandler): 165 """Upload test results file to datastore.""" 166 167 def post(self): 168 file_params = self.request.POST.getall(PARAM_FILE) 169 if not file_params: 170 self.response.out.write("FAIL: missing upload file field.") 171 return 172 173 builder = self.request.get(PARAM_BUILDER) 174 if not builder: 175 self.response.out.write("FAIL: missing builder parameter.") 176 return 177 178 master = self.request.get(PARAM_MASTER) 179 test_type = self.request.get(PARAM_TEST_TYPE) 180 incremental = self.request.get(PARAM_INCREMENTAL) 181 182 logging.debug( 183 "Processing upload request, master: %s, builder: %s, test_type: %s.", 184 master, builder, test_type) 185 186 # There are two possible types of each file_params in the request: 187 # one file item or a list of file items. 188 # Normalize file_params to a file item list. 189 files = [] 190 logging.debug("test: %s, type:%s", file_params, type(file_params)) 191 for item in file_params: 192 if not isinstance(item, list) and not isinstance(item, tuple): 193 item = [item] 194 files.extend(item) 195 196 errors = [] 197 for file in files: 198 filename = file.filename.lower() 199 if ((incremental and filename == "results.json") or 200 (filename == "incremental_results.json")): 201 # Merge incremental json results. 202 update_succeeded = JsonResults.update(master, builder, test_type, file.value) 203 else: 204 update_succeeded = TestFile.update( 205 master, builder, test_type, file.filename, file.value) 206 207 if not update_succeeded: 208 errors.append( 209 "Upload failed, master: %s, builder: %s, test_type: %s, name: %s." % 210 (master, builder, test_type, file.filename)) 211 212 if errors: 213 messages = "FAIL: " + "; ".join(errors) 214 logging.warning(messages) 215 self.response.set_status(500, messages) 216 self.response.out.write("FAIL") 217 else: 218 self.response.set_status(200) 219 self.response.out.write("OK") 220 221 222class UploadForm(webapp.RequestHandler): 223 """Show a form so user can upload a file.""" 224 225 def get(self): 226 template_values = { 227 "upload_url": "/testfile/upload", 228 } 229 self.response.out.write(template.render("templates/uploadform.html", 230 template_values)) 231