Index: swarm_client/tools/swarming_load_test_client.py |
=================================================================== |
--- swarm_client/tools/swarming_load_test_client.py (revision 235167) |
+++ swarm_client/tools/swarming_load_test_client.py (working copy) |
@@ -1,226 +0,0 @@ |
-#!/usr/bin/env python |
-# Copyright 2013 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
- |
-"""Triggers a ton of fake jobs to test its handling under high load. |
- |
-Generates an histogram with the latencies to process the tasks and number of |
-retries. |
-""" |
- |
-import json |
-import logging |
-import optparse |
-import os |
-import random |
-import string |
-import sys |
-import time |
- |
-ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
- |
-sys.path.insert(0, ROOT_DIR) |
- |
-from third_party import colorama |
- |
-import swarming |
- |
-from utils import graph |
-from utils import net |
-from utils import threading_utils |
- |
-import swarming_load_test_bot |
- |
- |
-def print_results(results, columns, buckets): |
- delays = [i for i in results if isinstance(i, float)] |
- failures = [i for i in results if not isinstance(i, float)] |
- |
- graph.print_histogram( |
- graph.generate_histogram(delays, buckets), columns, '%5.3f') |
- print('') |
- print('Total items : %d' % len(results)) |
- average = 0 |
- if delays: |
- average = sum(delays)/ len(delays) |
- print('Average delay: %s' % graph.to_units(average)) |
- #print('Average overhead: %s' % graph.to_units(total_size / len(sizes))) |
- print('') |
- if failures: |
- print('') |
- print('%sFAILURES%s:' % (colorama.Fore.RED, colorama.Fore.RESET)) |
- print('\n'.join(' %s' % i for i in failures)) |
- |
- |
-def trigger_task(swarming_url, progress, unique, timeout, index): |
- """Triggers a Swarming job and collects results. |
- |
- Returns the total amount of time to run a task remotely, including all the |
- overhead. |
- """ |
- name = 'load-test-%d-%s' % (index, unique) |
- start = time.time() |
- |
- logging.info('trigger') |
- manifest = swarming.Manifest( |
- None, name, 1, None, swarming_load_test_bot.OS_NAME, '', |
- 'http://localhost:1', False, False, 100, None) |
- data = {'request': manifest.to_json()} |
- response = net.url_open(swarming_url + '/test', data=data) |
- if not response: |
- # Failed to trigger. Return a failure. |
- return 'failed_trigger' |
- result = json.load(response) |
- test_key = result['test_keys'][0].pop('test_key') |
- assert test_key |
- expected = { |
- 'test_case_name': name, |
- 'test_keys': [ |
- { |
- 'config_name': swarming_load_test_bot.OS_NAME, |
- 'num_instances': 1, |
- 'instance_index': 0, |
- }, |
- ], |
- } |
- assert result == expected, result |
- progress.update_item('%5d' % index, processing=1) |
- try: |
- logging.info('collect') |
- test_keys = swarming.get_test_keys(swarming_url, name) |
- if not test_keys: |
- return 'no_test_keys' |
- assert test_keys == [test_key], test_keys |
- out = [ |
- output |
- for _index, output in swarming.yield_results( |
- swarming_url, test_keys, timeout, None) |
- ] |
- if not out: |
- return 'no_result' |
- out[0].pop('machine_tag') |
- out[0].pop('machine_id') |
- expected = [ |
- { |
- u'config_instance_index': 0, |
- u'exit_codes': u'0', |
- u'num_config_instances': 1, |
- u'output': swarming_load_test_bot.TASK_OUTPUT, |
- }, |
- ] |
- assert out == expected, '\n%s\n%s' % (out, expected) |
- return time.time() - start |
- finally: |
- progress.update_item('%5d - done' % index, processing=-1, processed=1) |
- |
- |
-def main(): |
- colorama.init() |
- parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) |
- parser.add_option( |
- '-S', '--swarming', |
- metavar='URL', default='', |
- help='Swarming server to use') |
- |
- group = optparse.OptionGroup(parser, 'Load generated') |
- group.add_option( |
- '-s', '--send-rate', type='float', default=16., metavar='RATE', |
- help='Rate (item/s) of sending requests as a float, default: %default') |
- group.add_option( |
- '-d', '--duration', type='float', default=60., metavar='N', |
- help='Duration (s) of the sending phase of the load test, ' |
- 'default: %default') |
- group.add_option( |
- '-m', '--concurrent', type='int', default=200, metavar='N', |
- help='Maximum concurrent on-going requests, default: %default') |
- group.add_option( |
- '-t', '--timeout', type='float', default=3600., metavar='N', |
- help='Timeout to get results, default: %default') |
- parser.add_option_group(group) |
- |
- group = optparse.OptionGroup(parser, 'Display options') |
- group.add_option( |
- '--columns', type='int', default=graph.get_console_width(), metavar='N', |
- help='For histogram display, default:%default') |
- group.add_option( |
- '--buckets', type='int', default=20, metavar='N', |
- help='Number of buckets for histogram display, default:%default') |
- parser.add_option_group(group) |
- |
- parser.add_option( |
- '--dump', metavar='FOO.JSON', help='Dumps to json file') |
- parser.add_option( |
- '-v', '--verbose', action='store_true', help='Enables logging') |
- |
- options, args = parser.parse_args() |
- logging.basicConfig(level=logging.INFO if options.verbose else logging.FATAL) |
- if args: |
- parser.error('Unsupported args: %s' % args) |
- options.swarming = options.swarming.rstrip('/') |
- if not options.swarming: |
- parser.error('--swarming is required.') |
- if options.duration <= 0: |
- parser.error('Needs --duration > 0. 0.01 is a valid value.') |
- |
- total = options.send_rate * options.duration |
- print( |
- 'Sending %.1f i/s for %ds with max %d parallel requests; timeout %.1fs; ' |
- 'total %d' % |
- (options.send_rate, options.duration, options.concurrent, |
- options.timeout, total)) |
- print('[processing/processed/todo]') |
- |
- # This is used so there's no clash between runs and actual real usage. |
- unique = ''.join(random.choice(string.ascii_letters) for _ in range(8)) |
- columns = [('processing', 0), ('processed', 0), ('todo', 0)] |
- progress = threading_utils.Progress(columns) |
- index = 0 |
- with threading_utils.ThreadPoolWithProgress( |
- progress, 1, options.concurrent, 0) as pool: |
- try: |
- start = time.time() |
- while True: |
- duration = time.time() - start |
- if duration > options.duration: |
- break |
- should_have_triggered_so_far = int(duration * options.send_rate) |
- while index < should_have_triggered_so_far: |
- pool.add_task( |
- 0, |
- trigger_task, |
- options.swarming, |
- progress, |
- unique, |
- options.timeout, |
- index) |
- progress.update_item('', todo=1) |
- index += 1 |
- progress.print_update() |
- time.sleep(0.01) |
- except KeyboardInterrupt: |
- aborted = pool.abort() |
- progress.update_item( |
- 'Got Ctrl-C. Aborted %d unsent tasks.' % aborted, |
- raw=True, |
- todo=-aborted) |
- progress.print_update() |
- finally: |
- # TODO(maruel): We could give up on collecting results for the on-going |
- # tasks but that would need to be optional. |
- progress.update_item('Getting results for on-going tasks.', raw=True) |
- results = sorted(pool.join()) |
- progress.print_update() |
- # At this point, progress is not used anymore. |
- print('') |
- print(' - Took %.1fs.' % (time.time() - start)) |
- print('') |
- print_results(results, options.columns, options.buckets) |
- if options.dump: |
- with open(options.dump, 'w') as f: |
- json.dump(results, f, separators=(',',':')) |
- return 0 |
- |
- |
-if __name__ == '__main__': |
- sys.exit(main()) |