| 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 |