| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 """ | 3 """ |
| 4 Copyright 2013 Google Inc. | 4 Copyright 2013 Google Inc. |
| 5 | 5 |
| 6 Use of this source code is governed by a BSD-style license that can be | 6 Use of this source code is governed by a BSD-style license that can be |
| 7 found in the LICENSE file. | 7 found in the LICENSE file. |
| 8 | 8 |
| 9 HTTP server for our HTML rebaseline viewer. | 9 HTTP server for our HTML rebaseline viewer. |
| 10 """ | 10 """ |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 _SKP_PLATFORMS[0], _SKP_PLATFORMS[1])) | 253 _SKP_PLATFORMS[0], _SKP_PLATFORMS[1])) |
| 254 file_handle.write('</li>') | 254 file_handle.write('</li>') |
| 255 | 255 |
| 256 file_handle.write('\n</ul></body></html>') | 256 file_handle.write('\n</ul></body></html>') |
| 257 | 257 |
| 258 | 258 |
| 259 class Server(object): | 259 class Server(object): |
| 260 """ HTTP server for our HTML rebaseline viewer. """ | 260 """ HTTP server for our HTML rebaseline viewer. """ |
| 261 | 261 |
| 262 def __init__(self, | 262 def __init__(self, |
| 263 actuals_source, |
| 263 actuals_dir=DEFAULT_ACTUALS_DIR, | 264 actuals_dir=DEFAULT_ACTUALS_DIR, |
| 264 json_filename=DEFAULT_JSON_FILENAME, | 265 json_filename=DEFAULT_JSON_FILENAME, |
| 265 gm_summaries_bucket=DEFAULT_GM_SUMMARIES_BUCKET, | |
| 266 port=DEFAULT_PORT, export=False, editable=True, | 266 port=DEFAULT_PORT, export=False, editable=True, |
| 267 reload_seconds=0, config_pairs=None, builder_regex_list=None, | 267 reload_seconds=0, config_pairs=None, builder_regex_list=None, |
| 268 boto_file_path=None, | 268 boto_file_path=None, |
| 269 imagediffdb_threads=imagediffdb.DEFAULT_NUM_WORKER_THREADS): | 269 imagediffdb_threads=imagediffdb.DEFAULT_NUM_WORKER_THREADS): |
| 270 """ | 270 """ |
| 271 Args: | 271 Args: |
| 272 actuals_source: actuals_source.get_builders() -> |
| 273 {builder:string -> [ bucket:string, path:string, generation:string ]} |
| 274 If None, don't fetch new actual-results files |
| 275 at all, just compare to whatever files are already in actuals_dir |
| 272 actuals_dir: directory under which we will check out the latest actual | 276 actuals_dir: directory under which we will check out the latest actual |
| 273 GM results | 277 GM results |
| 274 json_filename: basename of the JSON summary file to load for each builder | 278 json_filename: basename of the JSON summary file to load for each builder |
| 275 gm_summaries_bucket: Google Storage bucket to download json_filename | |
| 276 files from; if None or '', don't fetch new actual-results files | |
| 277 at all, just compare to whatever files are already in actuals_dir | |
| 278 port: which TCP port to listen on for HTTP requests | 279 port: which TCP port to listen on for HTTP requests |
| 279 export: whether to allow HTTP clients on other hosts to access this server | 280 export: whether to allow HTTP clients on other hosts to access this server |
| 280 editable: whether HTTP clients are allowed to submit new GM baselines | 281 editable: whether HTTP clients are allowed to submit new GM baselines |
| 281 (SKP baseline modifications are performed using an entirely different | 282 (SKP baseline modifications are performed using an entirely different |
| 282 mechanism, not affected by this parameter) | 283 mechanism, not affected by this parameter) |
| 283 reload_seconds: polling interval with which to check for new results; | 284 reload_seconds: polling interval with which to check for new results; |
| 284 if 0, don't check for new results at all | 285 if 0, don't check for new results at all |
| 285 config_pairs: List of (string, string) tuples; for each tuple, compare | 286 config_pairs: List of (string, string) tuples; for each tuple, compare |
| 286 actual results of these two configs. If None or empty, | 287 actual results of these two configs. If None or empty, |
| 287 don't compare configs at all. | 288 don't compare configs at all. |
| 288 builder_regex_list: List of regular expressions specifying which builders | 289 builder_regex_list: List of regular expressions specifying which builders |
| 289 we will process. If None, process all builders. | 290 we will process. If None, process all builders. |
| 290 boto_file_path: Path to .boto file giving us credentials to access | 291 boto_file_path: Path to .boto file giving us credentials to access |
| 291 Google Storage buckets; if None, we will only be able to access | 292 Google Storage buckets; if None, we will only be able to access |
| 292 public GS buckets. | 293 public GS buckets. |
| 293 imagediffdb_threads: How many threads to spin up within imagediffdb. | 294 imagediffdb_threads: How many threads to spin up within imagediffdb. |
| 294 """ | 295 """ |
| 296 self._actuals_source = actuals_source |
| 295 self._actuals_dir = actuals_dir | 297 self._actuals_dir = actuals_dir |
| 296 self._json_filename = json_filename | 298 self._json_filename = json_filename |
| 297 self._gm_summaries_bucket = gm_summaries_bucket | |
| 298 self._port = port | 299 self._port = port |
| 299 self._export = export | 300 self._export = export |
| 300 self._editable = editable | 301 self._editable = editable |
| 301 self._reload_seconds = reload_seconds | 302 self._reload_seconds = reload_seconds |
| 302 self._config_pairs = config_pairs or [] | 303 self._config_pairs = config_pairs or [] |
| 303 self._builder_regex_list = builder_regex_list | 304 self._builder_regex_list = builder_regex_list |
| 304 self.truncate_results = False | 305 self.truncate_results = False |
| 305 | 306 |
| 306 if boto_file_path: | 307 if boto_file_path: |
| 307 self._gs = gs_utils.GSUtils(boto_file_path=boto_file_path) | 308 self._gs = gs_utils.GSUtils(boto_file_path=boto_file_path) |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 378 the same time. | 379 the same time. |
| 379 | 380 |
| 380 Args: | 381 Args: |
| 381 invalidate: if True, invalidate self._results immediately upon entry; | 382 invalidate: if True, invalidate self._results immediately upon entry; |
| 382 otherwise, we will let readers see those results until we | 383 otherwise, we will let readers see those results until we |
| 383 replace them | 384 replace them |
| 384 """ | 385 """ |
| 385 with self.results_rlock: | 386 with self.results_rlock: |
| 386 if invalidate: | 387 if invalidate: |
| 387 self._results = None | 388 self._results = None |
| 388 if self._gm_summaries_bucket: | 389 |
| 390 if self._actuals_source: |
| 389 logging.info( | 391 logging.info( |
| 390 'Updating GM result summaries in %s from gm_summaries_bucket %s ...' | 392 'Updating GM result summaries in %s from %s ...' |
| 391 % (self._actuals_dir, self._gm_summaries_bucket)) | 393 % (self._actuals_dir, self._actuals_source.description())) |
| 392 | 394 |
| 393 # Clean out actuals_dir first, in case some builders have gone away | 395 # Clean out actuals_dir first, in case some builders have gone away |
| 394 # since we last ran. | 396 # since we last ran. |
| 395 if os.path.isdir(self._actuals_dir): | 397 if os.path.isdir(self._actuals_dir): |
| 396 shutil.rmtree(self._actuals_dir) | 398 shutil.rmtree(self._actuals_dir) |
| 397 | 399 |
| 398 # Get the list of builders we care about. | 400 # Get the list of actuals we care about. |
| 399 all_builders = download_actuals.get_builders_list( | 401 all_actuals = self._actuals_source.get_builders() |
| 400 summaries_bucket=self._gm_summaries_bucket) | 402 |
| 401 if self._builder_regex_list: | 403 if self._builder_regex_list: |
| 402 matching_builders = [] | 404 matching_builders = [] |
| 403 for builder in all_builders: | 405 for builder in all_actuals: |
| 404 for regex in self._builder_regex_list: | 406 for regex in self._builder_regex_list: |
| 405 if re.match(regex, builder): | 407 if re.match(regex, builder): |
| 406 matching_builders.append(builder) | 408 matching_builders.append(builder) |
| 407 break # go on to the next builder, no need to try more regexes | 409 break # go on to the next builder, no need to try more regexes |
| 408 else: | 410 else: |
| 409 matching_builders = all_builders | 411 matching_builders = all_actuals.keys() |
| 410 | 412 |
| 411 # Download the JSON file for each builder we care about. | 413 # Download the JSON file for each builder we care about. |
| 412 # | 414 # |
| 413 # TODO(epoger): When this is a large number of builders, we would be | 415 # TODO(epoger): When this is a large number of builders, we would be |
| 414 # better off downloading them in parallel! | 416 # better off downloading them in parallel! |
| 415 for builder in matching_builders: | 417 for builder in matching_builders: |
| 416 self._gs.download_file( | 418 self._gs.download_file( |
| 417 source_bucket=self._gm_summaries_bucket, | 419 source_bucket=all_actuals[builder].bucket, |
| 418 source_path=posixpath.join(builder, self._json_filename), | 420 source_path=all_actuals[builder].path, |
| 421 source_generation=all_actuals[builder].generation, |
| 419 dest_path=os.path.join(self._actuals_dir, builder, | 422 dest_path=os.path.join(self._actuals_dir, builder, |
| 420 self._json_filename), | 423 self._json_filename), |
| 421 create_subdirs_if_needed=True) | 424 create_subdirs_if_needed=True) |
| 422 | 425 |
| 423 # We only update the expectations dir if the server was run with a | 426 # We only update the expectations dir if the server was run with a |
| 424 # nonzero --reload argument; otherwise, we expect the user to maintain | 427 # nonzero --reload argument; otherwise, we expect the user to maintain |
| 425 # her own expectations as she sees fit. | 428 # her own expectations as she sees fit. |
| 426 # | 429 # |
| 427 # Because the Skia repo is hosted using git, and git does not | 430 # Because the Skia repo is hosted using git, and git does not |
| 428 # support updating a single directory tree, we have to update the entire | 431 # support updating a single directory tree, we have to update the entire |
| (...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 892 parser.add_argument('--editable', action='store_true', | 895 parser.add_argument('--editable', action='store_true', |
| 893 help=('Allow HTTP clients to submit new GM baselines; ' | 896 help=('Allow HTTP clients to submit new GM baselines; ' |
| 894 'SKP baselines can be edited regardless of this ' | 897 'SKP baselines can be edited regardless of this ' |
| 895 'setting.')) | 898 'setting.')) |
| 896 parser.add_argument('--export', action='store_true', | 899 parser.add_argument('--export', action='store_true', |
| 897 help=('Instead of only allowing access from HTTP clients ' | 900 help=('Instead of only allowing access from HTTP clients ' |
| 898 'on localhost, allow HTTP clients on other hosts ' | 901 'on localhost, allow HTTP clients on other hosts ' |
| 899 'to access this server. WARNING: doing so will ' | 902 'to access this server. WARNING: doing so will ' |
| 900 'allow users on other hosts to modify your ' | 903 'allow users on other hosts to modify your ' |
| 901 'GM expectations, if combined with --editable.')) | 904 'GM expectations, if combined with --editable.')) |
| 905 parser.add_argument('--rietveld-issue', |
| 906 help=('Download json_filename files from latest trybot' |
| 907 'runs on this codereview.chromium.org issue.' |
| 908 'Overrides --gm-summaries-bucket.')) |
| 902 parser.add_argument('--gm-summaries-bucket', | 909 parser.add_argument('--gm-summaries-bucket', |
| 903 help=('Google Cloud Storage bucket to download ' | 910 help=('Google Cloud Storage bucket to download ' |
| 904 'JSON_FILENAME files from. ' | 911 'JSON_FILENAME files from. ' |
| 905 'Defaults to %(default)s ; if set to ' | 912 'Defaults to %(default)s ; if set to ' |
| 906 'empty string, just compare to actual-results ' | 913 'empty string, just compare to actual-results ' |
| 907 'already found in ACTUALS_DIR.'), | 914 'already found in ACTUALS_DIR.'), |
| 908 default=DEFAULT_GM_SUMMARIES_BUCKET) | 915 default=DEFAULT_GM_SUMMARIES_BUCKET) |
| 909 parser.add_argument('--json-filename', | 916 parser.add_argument('--json-filename', |
| 910 help=('JSON summary filename to read for each builder; ' | 917 help=('JSON summary filename to read for each builder; ' |
| 911 'defaults to %(default)s.'), | 918 'defaults to %(default)s.'), |
| (...skipping 17 matching lines...) Expand all Loading... |
| 929 default=imagediffdb.DEFAULT_NUM_WORKER_THREADS) | 936 default=imagediffdb.DEFAULT_NUM_WORKER_THREADS) |
| 930 parser.add_argument('--truncate', action='store_true', | 937 parser.add_argument('--truncate', action='store_true', |
| 931 help=('FOR TESTING ONLY: truncate the set of images we ' | 938 help=('FOR TESTING ONLY: truncate the set of images we ' |
| 932 'process, to speed up testing.')) | 939 'process, to speed up testing.')) |
| 933 args = parser.parse_args() | 940 args = parser.parse_args() |
| 934 if args.compare_configs: | 941 if args.compare_configs: |
| 935 config_pairs = CONFIG_PAIRS_TO_COMPARE | 942 config_pairs = CONFIG_PAIRS_TO_COMPARE |
| 936 else: | 943 else: |
| 937 config_pairs = None | 944 config_pairs = None |
| 938 | 945 |
| 946 if args.rietveld_issue: |
| 947 actuals_source = download_actuals.RietveldIssueActuals(args.rietveld_issue, |
| 948 args.json_filename) |
| 949 else: |
| 950 actuals_source = download_actuals.TipOfTreeActuals(args.gm_summaries_bucket, |
| 951 args.json_filename) |
| 952 |
| 939 global _SERVER | 953 global _SERVER |
| 940 _SERVER = Server(actuals_dir=args.actuals_dir, | 954 _SERVER = Server(actuals_source, |
| 955 actuals_dir=args.actuals_dir, |
| 941 json_filename=args.json_filename, | 956 json_filename=args.json_filename, |
| 942 gm_summaries_bucket=args.gm_summaries_bucket, | |
| 943 port=args.port, export=args.export, editable=args.editable, | 957 port=args.port, export=args.export, editable=args.editable, |
| 944 reload_seconds=args.reload, config_pairs=config_pairs, | 958 reload_seconds=args.reload, config_pairs=config_pairs, |
| 945 builder_regex_list=args.builders, boto_file_path=args.boto, | 959 builder_regex_list=args.builders, boto_file_path=args.boto, |
| 946 imagediffdb_threads=args.threads) | 960 imagediffdb_threads=args.threads) |
| 947 if args.truncate: | 961 if args.truncate: |
| 948 _SERVER.truncate_results = True | 962 _SERVER.truncate_results = True |
| 949 _SERVER.run() | 963 _SERVER.run() |
| 950 | 964 |
| 951 | 965 |
| 952 if __name__ == '__main__': | 966 if __name__ == '__main__': |
| 953 main() | 967 main() |
| OLD | NEW |