OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2013 The Chromium Authors. All rights reserved. | 2 # Copyright 2013 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Client tool to trigger tasks or retrieve results from a Swarming server.""" | 6 """Client tool to trigger tasks or retrieve results from a Swarming server.""" |
7 | 7 |
8 __version__ = '0.1' | 8 __version__ = '0.1' |
9 | 9 |
10 import hashlib | 10 import hashlib |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 start_time = time.time() | 126 start_time = time.time() |
127 self._isolate_item = isolateserver.BufferItem( | 127 self._isolate_item = isolateserver.BufferItem( |
128 self.bundle.zip_into_buffer(), self._algo, is_isolated=True) | 128 self.bundle.zip_into_buffer(), self._algo, is_isolated=True) |
129 print 'Zipping completed, time elapsed: %f' % (time.time() - start_time) | 129 print 'Zipping completed, time elapsed: %f' % (time.time() - start_time) |
130 | 130 |
131 try: | 131 try: |
132 start_time = time.time() | 132 start_time = time.time() |
133 uploaded = self.storage.upload_items([self._isolate_item]) | 133 uploaded = self.storage.upload_items([self._isolate_item]) |
134 elapsed = time.time() - start_time | 134 elapsed = time.time() - start_time |
135 except (IOError, OSError) as exc: | 135 except (IOError, OSError) as exc: |
136 print >> sys.stderr, 'Failed to upload the zip file: %s' % exc | 136 tools.report_error('Failed to upload the zip file: %s' % exc) |
137 return False | 137 return False |
138 | 138 |
139 if self._isolate_item in uploaded: | 139 if self._isolate_item in uploaded: |
140 print 'Upload complete, time elapsed: %f' % elapsed | 140 print 'Upload complete, time elapsed: %f' % elapsed |
141 else: | 141 else: |
142 print 'Zip file already on server, time elapsed: %f' % elapsed | 142 print 'Zip file already on server, time elapsed: %f' % elapsed |
143 | 143 |
144 return True | 144 return True |
145 | 145 |
146 def to_json(self): | 146 def to_json(self): |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
340 file_hash_or_isolated, test_name, shards, test_filter, slave_os, | 340 file_hash_or_isolated, test_name, shards, test_filter, slave_os, |
341 working_dir, isolate_server, swarming, verbose, profile, priority, algo): | 341 working_dir, isolate_server, swarming, verbose, profile, priority, algo): |
342 """Process the manifest file and send off the swarm test request. | 342 """Process the manifest file and send off the swarm test request. |
343 | 343 |
344 Optionally archives an .isolated file. | 344 Optionally archives an .isolated file. |
345 """ | 345 """ |
346 if file_hash_or_isolated.endswith('.isolated'): | 346 if file_hash_or_isolated.endswith('.isolated'): |
347 file_hash = archive( | 347 file_hash = archive( |
348 file_hash_or_isolated, isolate_server, slave_os, algo, verbose) | 348 file_hash_or_isolated, isolate_server, slave_os, algo, verbose) |
349 if not file_hash: | 349 if not file_hash: |
350 print >> sys.stderr, 'Archival failure %s' % file_hash_or_isolated | 350 tools.report_error('Archival failure %s' % file_hash_or_isolated) |
351 return 1 | 351 return 1 |
352 elif isolateserver.is_valid_hash(file_hash_or_isolated, algo): | 352 elif isolateserver.is_valid_hash(file_hash_or_isolated, algo): |
353 file_hash = file_hash_or_isolated | 353 file_hash = file_hash_or_isolated |
354 else: | 354 else: |
355 print >> sys.stderr, 'Invalid hash %s' % file_hash_or_isolated | 355 tools.report_error('Invalid hash %s' % file_hash_or_isolated) |
356 return 1 | 356 return 1 |
357 | 357 |
358 try: | 358 try: |
359 manifest = Manifest( | 359 manifest = Manifest( |
360 file_hash, | 360 file_hash, |
361 test_name, | 361 test_name, |
362 shards, | 362 shards, |
363 test_filter, | 363 test_filter, |
364 PLATFORM_MAPPING_SWARMING[slave_os], | 364 PLATFORM_MAPPING_SWARMING[slave_os], |
365 working_dir, | 365 working_dir, |
366 isolate_server, | 366 isolate_server, |
367 verbose, | 367 verbose, |
368 profile, | 368 profile, |
369 priority, | 369 priority, |
370 algo) | 370 algo) |
371 except ValueError as e: | 371 except ValueError as e: |
372 print >> sys.stderr, 'Unable to process %s: %s' % (test_name, e) | 372 tools.report_error('Unable to process %s: %s' % (test_name, e)) |
373 return 1 | 373 return 1 |
374 | 374 |
375 chromium_setup(manifest) | 375 chromium_setup(manifest) |
376 | 376 |
377 # Zip up relevant files. | 377 # Zip up relevant files. |
378 print('Zipping up files...') | 378 print('Zipping up files...') |
379 if not manifest.zip_and_upload(): | 379 if not manifest.zip_and_upload(): |
380 return 1 | 380 return 1 |
381 | 381 |
382 # Send test requests off to swarm. | 382 # Send test requests off to swarm. |
383 print('Sending test requests to swarm.') | 383 print('Sending test requests to swarm.') |
384 print('Server: %s' % swarming) | 384 print('Server: %s' % swarming) |
385 print('Job name: %s' % test_name) | 385 print('Job name: %s' % test_name) |
386 test_url = swarming + '/test' | 386 test_url = swarming + '/test' |
387 manifest_text = manifest.to_json() | 387 manifest_text = manifest.to_json() |
388 result = net.url_read(test_url, data={'request': manifest_text}) | 388 result = net.url_read(test_url, data={'request': manifest_text}) |
389 if not result: | 389 if not result: |
390 print >> sys.stderr, 'Failed to send test for %s\n%s' % ( | 390 tools.report_error( |
391 test_name, test_url) | 391 'Failed to send test for %s\n%s' % (test_name, test_url)) |
392 return 1 | 392 return 1 |
393 try: | 393 try: |
394 json.loads(result) | 394 json.loads(result) |
395 except (ValueError, TypeError) as e: | 395 except (ValueError, TypeError) as e: |
396 print >> sys.stderr, 'Failed to send test for %s' % test_name | 396 msg = '\n'.join(( |
397 print >> sys.stderr, 'Manifest: %s' % manifest_text | 397 'Failed to send test for %s' % test_name, |
398 print >> sys.stderr, 'Bad response: %s' % result | 398 'Manifest: %s' % manifest_text, |
399 print >> sys.stderr, str(e) | 399 'Bad response: %s' % result, |
| 400 str(e))) |
| 401 tools.report_error(msg) |
400 return 1 | 402 return 1 |
401 return 0 | 403 return 0 |
402 | 404 |
403 | 405 |
404 def trigger( | 406 def trigger( |
405 slave_os, | 407 slave_os, |
406 tasks, | 408 tasks, |
407 task_prefix, | 409 task_prefix, |
408 working_dir, | 410 working_dir, |
409 isolate_server, | 411 isolate_server, |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
531 add_collect_options(parser) | 533 add_collect_options(parser) |
532 (options, args) = parser.parse_args(args) | 534 (options, args) = parser.parse_args(args) |
533 if not args: | 535 if not args: |
534 parser.error('Must specify one test name.') | 536 parser.error('Must specify one test name.') |
535 elif len(args) > 1: | 537 elif len(args) > 1: |
536 parser.error('Must specify only one test name.') | 538 parser.error('Must specify only one test name.') |
537 | 539 |
538 try: | 540 try: |
539 return collect(options.swarming, args[0], options.timeout, options.decorate) | 541 return collect(options.swarming, args[0], options.timeout, options.decorate) |
540 except Failure as e: | 542 except Failure as e: |
541 parser.error(e.args[0]) | 543 tools.report_error(e) |
| 544 return 1 |
542 | 545 |
543 | 546 |
544 @subcommand.usage('[hash|isolated ...]') | 547 @subcommand.usage('[hash|isolated ...]') |
545 def CMDrun(parser, args): | 548 def CMDrun(parser, args): |
546 """Triggers a job and wait for the results. | 549 """Triggers a job and wait for the results. |
547 | 550 |
548 Basically, does everything to run command(s) remotely. | 551 Basically, does everything to run command(s) remotely. |
549 """ | 552 """ |
550 add_trigger_options(parser) | 553 add_trigger_options(parser) |
551 add_collect_options(parser) | 554 add_collect_options(parser) |
(...skipping 13 matching lines...) Expand all Loading... |
565 options.task_prefix, | 568 options.task_prefix, |
566 options.working_dir, | 569 options.working_dir, |
567 options.isolate_server, | 570 options.isolate_server, |
568 options.swarming, | 571 options.swarming, |
569 options.verbose, | 572 options.verbose, |
570 options.profile, | 573 options.profile, |
571 options.priority) | 574 options.priority) |
572 except Failure as e: | 575 except Failure as e: |
573 result = e.args[0] | 576 result = e.args[0] |
574 if result: | 577 if result: |
575 print >> sys.stderr, 'Failed to trigger %s: %s' % (arg, result) | 578 tools.report_error('Failed to trigger %s: %s' % (arg, result)) |
576 else: | 579 else: |
577 success.append(os.path.basename(arg)) | 580 success.append(os.path.basename(arg)) |
578 | 581 |
579 if not success: | 582 if not success: |
580 print >> sys.stderr, 'Failed to trigger any job.' | 583 tools.report_error('Failed to trigger any job.') |
581 return result | 584 return result |
582 | 585 |
583 code = 0 | 586 code = 0 |
584 for arg in success: | 587 for arg in success: |
585 logging.info('Collecting %s', arg) | 588 logging.info('Collecting %s', arg) |
586 try: | 589 try: |
587 new_code = collect( | 590 new_code = collect( |
588 options.swarming, | 591 options.swarming, |
589 options.task_prefix + arg, | 592 options.task_prefix + arg, |
590 options.timeout, | 593 options.timeout, |
591 options.decorate) | 594 options.decorate) |
592 code = max(code, new_code) | 595 code = max(code, new_code) |
593 except Failure as e: | 596 except Failure as e: |
594 code = max(code, 1) | 597 code = max(code, 1) |
595 print >> sys.stderr, e.args[0] | 598 tools.report_error(e) |
596 return code | 599 return code |
597 | 600 |
598 | 601 |
599 def CMDtrigger(parser, args): | 602 def CMDtrigger(parser, args): |
600 """Triggers Swarm request(s). | 603 """Triggers Swarm request(s). |
601 | 604 |
602 Accepts one or multiple --task requests, with either the hash (sha1) of a | 605 Accepts one or multiple --task requests, with either the hash (sha1) of a |
603 .isolated file already uploaded or the path to an .isolated file to archive, | 606 .isolated file already uploaded or the path to an .isolated file to archive, |
604 packages it if needed and sends a Swarm manifest file to the Swarm server. | 607 packages it if needed and sends a Swarm manifest file to the Swarm server. |
605 """ | 608 """ |
(...skipping 17 matching lines...) Expand all Loading... |
623 options.os, | 626 options.os, |
624 options.tasks, | 627 options.tasks, |
625 options.task_prefix, | 628 options.task_prefix, |
626 options.working_dir, | 629 options.working_dir, |
627 options.isolate_server, | 630 options.isolate_server, |
628 options.swarming, | 631 options.swarming, |
629 options.verbose, | 632 options.verbose, |
630 options.profile, | 633 options.profile, |
631 options.priority) | 634 options.priority) |
632 except Failure as e: | 635 except Failure as e: |
633 parser.error(e.args[0]) | 636 tools.report_error(e) |
| 637 return 1 |
634 | 638 |
635 | 639 |
636 class OptionParserSwarming(tools.OptionParserWithLogging): | 640 class OptionParserSwarming(tools.OptionParserWithLogging): |
637 def __init__(self, **kwargs): | 641 def __init__(self, **kwargs): |
638 tools.OptionParserWithLogging.__init__( | 642 tools.OptionParserWithLogging.__init__( |
639 self, prog='swarming.py', **kwargs) | 643 self, prog='swarming.py', **kwargs) |
640 self.add_option( | 644 self.add_option( |
641 '-S', '--swarming', | 645 '-S', '--swarming', |
642 metavar='URL', default='', | 646 metavar='URL', default='', |
643 help='Swarming server to use') | 647 help='Swarming server to use') |
644 | 648 |
645 def parse_args(self, *args, **kwargs): | 649 def parse_args(self, *args, **kwargs): |
646 options, args = tools.OptionParserWithLogging.parse_args( | 650 options, args = tools.OptionParserWithLogging.parse_args( |
647 self, *args, **kwargs) | 651 self, *args, **kwargs) |
648 options.swarming = options.swarming.rstrip('/') | 652 options.swarming = options.swarming.rstrip('/') |
649 if not options.swarming: | 653 if not options.swarming: |
650 self.error('--swarming is required.') | 654 self.error('--swarming is required.') |
651 return options, args | 655 return options, args |
652 | 656 |
653 | 657 |
654 def main(args): | 658 def main(args): |
655 dispatcher = subcommand.CommandDispatcher(__name__) | 659 dispatcher = subcommand.CommandDispatcher(__name__) |
656 try: | 660 try: |
657 return dispatcher.execute(OptionParserSwarming(version=__version__), args) | 661 return dispatcher.execute(OptionParserSwarming(version=__version__), args) |
658 except Failure as e: | 662 except Exception as e: |
659 sys.stderr.write('\nError: ') | 663 tools.report_error(e) |
660 sys.stderr.write(str(e)) | |
661 sys.stderr.write('\n') | |
662 return 1 | 664 return 1 |
663 | 665 |
664 | 666 |
665 if __name__ == '__main__': | 667 if __name__ == '__main__': |
666 fix_encoding.fix_encoding() | 668 fix_encoding.fix_encoding() |
667 tools.disable_buffering() | 669 tools.disable_buffering() |
668 colorama.init() | 670 colorama.init() |
669 sys.exit(main(sys.argv[1:])) | 671 sys.exit(main(sys.argv[1:])) |
OLD | NEW |