Beispiel #1
0
 def test_spawn_zero_locusts(self):
     class MyTaskSet(TaskSet):
         @task
         def my_task(self):
             pass
         
     class MyTestLocust(Locust):
         task_set = MyTaskSet
         wait_time = constant(0.1)
     
     runner = LocalLocustRunner([MyTestLocust], self.options)
     
     timeout = gevent.Timeout(2.0)
     timeout.start()
     
     try:
         runner.start_hatching(0, 1, wait=True)
         runner.greenlet.join()
     except gevent.Timeout:
         self.fail("Got Timeout exception. A locust seems to have been spawned, even though 0 was specified.")
     finally:
         timeout.cancel()
Beispiel #2
0
    def test_cpu_warning(self):
        _monitor_interval = runners.CPU_MONITOR_INTERVAL
        runners.CPU_MONITOR_INTERVAL = 2.0
        try:

            class CpuLocust(Locust):
                wait_time = constant(0.001)

                @task
                def cpu_task(self):
                    for i in range(1000000):
                        _ = 3 / 2

            environment = Environment(options=mocked_options(), )
            runner = LocalLocustRunner(environment, [CpuLocust])
            self.assertFalse(runner.cpu_warning_emitted)
            runner.spawn_locusts(1, 1, wait=False)
            sleep(2.5)
            runner.quit()
            self.assertTrue(runner.cpu_warning_emitted)
        finally:
            runners.CPU_MONITOR_INTERVAL = _monitor_interval
Beispiel #3
0
    def test_stop_event_stop_and_quit(self):
        class User(Locust):
            wait_time = constant(1)

            @task
            def my_task(self):
                pass

        test_stop_run = [0]
        environment = Environment(options=mocked_options())

        def on_test_stop(*args, **kwargs):
            test_stop_run[0] += 1

        environment.events.test_stop.add_listener(on_test_stop)

        runner = LocalLocustRunner(environment, locust_classes=[User])
        runner.start(locust_count=3, hatch_rate=3, wait=False)
        self.assertEqual(0, test_stop_run[0])
        runner.stop()
        runner.quit()
        self.assertEqual(1, test_stop_run[0])
Beispiel #4
0
 def test_exception_is_catched(self):
     """ Test that exceptions are stored, and execution continues """
     
     class MyTaskSet(TaskSet):
         def __init__(self, *a, **kw):
             super(MyTaskSet, self).__init__(*a, **kw)
             self._task_queue = [
                 {"callable":self.will_error, "args":[], "kwargs":{}}, 
                 {"callable":self.will_stop, "args":[], "kwargs":{}},
             ]
         
         @task(1)
         def will_error(self):
             raise HeyAnException(":(")
         
         @task(1)
         def will_stop(self):
             raise StopLocust()
     
     class MyLocust(Locust):
         wait_time = constant(0.01)
         tasks = [MyTaskSet]
     
     runner = LocalLocustRunner(self.environment, [MyLocust])
     l = MyLocust(self.environment)
     
     # make sure HeyAnException isn't raised
     l.run()
     l.run()
     # make sure we got two entries in the error log
     self.assertEqual(2, len(self.mocked_log.error))
     
     # make sure exception was stored
     self.assertEqual(1, len(runner.exceptions))
     hash_key, exception = runner.exceptions.popitem()
     self.assertTrue("traceback" in exception)
     self.assertTrue("HeyAnException" in exception["traceback"])
     self.assertEqual(2, exception["count"])
Beispiel #5
0
 def test_exception_in_task(self):
     class HeyAnException(Exception):
         pass
     
     class MyLocust(Locust):
         class task_set(TaskSet):
             @task
             def will_error(self):
                 raise HeyAnException(":(")
     
     runner = LocalLocustRunner([MyLocust], self.options)
     
     l = MyLocust()
     l._catch_exceptions = False
     
     self.assertRaises(HeyAnException, l.run)
     self.assertRaises(HeyAnException, l.run)
     self.assertEqual(1, len(runner.exceptions))
     
     hash_key, exception = runner.exceptions.popitem()
     self.assertTrue("traceback" in exception)
     self.assertTrue("HeyAnException" in exception["traceback"])
     self.assertEqual(2, exception["count"])
Beispiel #6
0
 def test_spawn_zero_locusts(self):
     class MyTaskSet(TaskSet):
         @task
         def my_task(self):
             pass
         
     class MyTestLocust(Locust):
         tasks = [MyTaskSet]
         wait_time = constant(0.1)
     
     environment = Environment(options=mocked_options())
     runner = LocalLocustRunner(environment, [MyTestLocust])
     
     timeout = gevent.Timeout(2.0)
     timeout.start()
     
     try:
         runner.start(0, 1, wait=True)
         runner.hatching_greenlet.join()
     except gevent.Timeout:
         self.fail("Got Timeout exception. A locust seems to have been spawned, even though 0 was specified.")
     finally:
         timeout.cancel()
Beispiel #7
0
 def test_no_reset_stats(self):
     class User(Locust):
         wait_time = constant(0)
         @task
         class task_set(TaskSet):
             @task
             def my_task(self):
                 self.locust.environment.events.request_success.fire(
                     request_type="GET",
                     name="/test",
                     response_time=666,
                     response_length=1337,
                 )                    
                 sleep(2)
     
     environment = Environment(reset_stats=False, options=mocked_options())
     runner = LocalLocustRunner(environment, locust_classes=[User])
     runner.start(locust_count=6, hatch_rate=12, wait=False)
     sleep(0.25)
     self.assertGreaterEqual(runner.stats.get("/test", "GET").num_requests, 3)
     sleep(0.3)
     self.assertEqual(6, runner.stats.get("/test", "GET").num_requests)
     runner.quit()
Beispiel #8
0
    def test_start_event(self):
        class User(Locust):
            wait_time = constant(1)
            task_run_count = 0

            @task
            def my_task(self):
                User.task_run_count += 1

        test_start_run = [0]

        environment = Environment(locust_classes=[User])

        def on_test_start(*args, **kwargs):
            test_start_run[0] += 1

        environment.events.test_start.add_listener(on_test_start)

        runner = LocalLocustRunner(environment)
        runner.start(locust_count=3, hatch_rate=3, wait=False)
        runner.hatching_greenlet.get(timeout=3)

        self.assertEqual(1, test_start_run[0])
        self.assertEqual(3, User.task_run_count)
Beispiel #9
0
    wait_time = between(1, 3)
    host = "https://docs.locust.io"

    class task_set(TaskSet):
        @task
        def my_task(self):
            self.client.get("/")

        @task
        def task_404(self):
            self.client.get("/non-existing-path")


# setup Environment and Runner
env = Environment()
runner = LocalLocustRunner(environment=env, locust_classes=[User])
# start a WebUI instance
web_ui = WebUI(environment=env)
gevent.spawn(lambda: web_ui.start("127.0.0.1", 8089))

# TODO: fix
#def on_request_success(request_type, name, response_time, response_length, **kwargs):
#    report_to_grafana("%_%s" % (request_type, name), response_time)
#env.events.request_succes.add_listener(on_request_success)

# start a greenlet that periodically outputs the current stats
gevent.spawn(stats_printer(runner.stats))

# start the test
runner.start(1, hatch_rate=10)
# wait for the greenlets (indefinitely)
Beispiel #10
0
 def test_runner_reference_on_environment(self):
     env = Environment()
     runner = LocalLocustRunner(environment=env, locust_classes=[])
     self.assertEqual(env, runner.environment)
Beispiel #11
0
def main():
    parser, options, arguments = parse_options()

    # setup logging
    
    if options.show_version:
        print("Locust %s" % (version,))
        sys.exit(0)

    locustfile = find_locustfile(options.locustfile)
    if not locustfile:
        logger.error("Could not find any locustfile! Ensure file ends in '.py' and see --help for available options.")
        sys.exit(1)

    docstring, locusts = load_locustfile(locustfile)

    if options.list_commands:
        logger.info("Available Locusts:")
        for name in locusts:
            logger.info("    " + name)
        sys.exit(0)

    if not locusts:
        logger.error("No Locust class found!")
        sys.exit(1)

    # make sure specified Locust exists
    if arguments:
        missing = set(arguments) - set(locusts.keys())
        if missing:
            logger.error("Unknown Locust(s): %s\n" % (", ".join(missing)))
            sys.exit(1)
        else:
            names = set(arguments) & set(locusts.keys())
            locust_classes = [locusts[n] for n in names]
    else:
        locust_classes = locusts.values()
    
    if options.show_task_ratio:
        logger.info("\n Task ratio per locust class")
        logger.info( "-" * 80)
        print_task_ratio(locust_classes)
        logger.info("\n Total task ratio")
        logger.info("-" * 80)
        print_task_ratio(locust_classes, total=True)
        sys.exit(0)
    if options.show_task_ratio_json:
        from json import dumps
        task_data = {
            "per_class": get_task_ratio_dict(locust_classes), 
            "total": get_task_ratio_dict(locust_classes, total=True)
        }
        logger.info(dumps(task_data))
        sys.exit(0)
    
    # if --master is set, make sure --no-web isn't set
    if options.master and options.no_web:
        logger.error("Locust can not run distributed with the web interface disabled (do not use --no-web and --master together)")
        sys.exit(0)

    if not options.no_web and not options.slave:
        # spawn web greenlet
        logger.info("Starting web monitor at %s:%s" % (options.web_host or "*", options.port))
        main_greenlet = gevent.spawn(web.start, locust_classes, options)
    
    if not options.master and not options.slave:
        runners.locust_runner = LocalLocustRunner(locust_classes, options)
        # spawn client spawning/hatching greenlet
        if options.no_web:
            runners.locust_runner.start_hatching(wait=True)
            main_greenlet = runners.locust_runner.greenlet
    elif options.master:
        runners.locust_runner = MasterLocustRunner(locust_classes, options)
    elif options.slave:
        try:
            runners.locust_runner = SlaveLocustRunner(locust_classes, options)
            main_greenlet = runners.locust_runner.greenlet
        except socket.error as e:
            logger.error("Failed to connect to the Locust master: %s", e)
            sys.exit(-1)
    
    if not options.only_summary and (options.print_stats or (options.no_web and not options.slave)):
        # spawn stats printing greenlet
        gevent.spawn(stats_printer)
    
    def shutdown(code=0):
        """
        Shut down locust by firing quitting event, printing stats and exiting
        """
        logger.info("Shutting down (exit code %s), bye." % code)

        events.quitting.fire()
        print_stats(runners.locust_runner.request_stats)
        print_percentile_stats(runners.locust_runner.request_stats)

        print_error_report()
        sys.exit(code)
    
    # install SIGTERM handler
    def sig_term_handler():
        logger.info("Got SIGTERM signal")
        shutdown(0)
    gevent.signal(signal.SIGTERM, sig_term_handler)
    
    try:
        logger.info("Starting Locust %s" % version)
        main_greenlet.join()
        code = 0
        if len(runners.locust_runner.errors):
            code = 1
        shutdown(code=code)
    except KeyboardInterrupt as e:
        shutdown(0)
Beispiel #12
0
def main():
    parser, options = parse_options()

    # setup logging
    if not options.skip_log_setup:
        setup_logging(options.loglevel, options.logfile)

    logger = logging.getLogger(__name__)

    locust_path = get_locust_path()

    if options.web:
        web_port = options.web_port
        logger.info('Running easy-locust web: 0.0.0.0:{}'.format(web_port))
        init_app(port=web_port)
        sys.exit(0)

    if options.demo:
        if not locust_path:
            logger.error(
                '''Cannot locate Python path, make sure it is in right place. If windows add it to sys PATH,
            if linux make sure python is installed in /usr/local/lib/''')
            sys.exit(1)
        pt_demo_path = os.path.join(locust_path, 'demo',
                                    'demo_pressuretest.xls')
        pt_demo_path_json = os.path.join(locust_path, 'demo',
                                         'demo_locustfile.json')
        pt_new_demo = os.path.join(os.getcwd(), 'PtDemo.xls')
        pt_new_demo_json = os.path.join(os.getcwd(), 'demo.json')
        shutil.copyfile(pt_demo_path, pt_new_demo)
        shutil.copyfile(pt_demo_path_json, pt_new_demo_json)
        sys.exit(0)

    if options.xlsfile:
        pt_file = options.xlsfile
        if not (pt_file.endswith('.xls') or pt_file.endswith('.json')):
            logger.error(
                "PressureTest file must be end with '.xls' or '.json' and see --help for available options."
            )
            sys.exit(1)
        if not os.path.isfile(pt_file):
            logger.error('PressureTest file is not exist, please check it.')
            sys.exit(1)
        _status = generate_locust_file(pt_file)
        if not _status:
            sys.exit(1)
        sys.exit(0)

    locustfile = find_locustfile(options.locustfile)

    if not locustfile:
        logger.error(
            "Could not find any locustfile! Ensure file ends in '.py' and see --help for available options."
        )
        sys.exit(1)

    if locustfile == "locust.py" or locustfile == "locust.xls" or locustfile == "locust.json":
        logger.error(
            "The locustfile must not be named `locust.py` or `locust.xls` or `locust.json`. "
            "Please rename the file and try again.")
        sys.exit(1)

    docstring, locusts = load_locustfile(locustfile)

    if options.list_commands:
        console_logger.info("Available Locusts:")
        for name in locusts:
            console_logger.info("    " + name)
        sys.exit(0)

    if not locusts:
        logger.error("No Locust class found!")
        sys.exit(1)

    # make sure specified Locust exists
    if options.locust_classes:
        missing = set(options.locust_classes) - set(locusts.keys())
        if missing:
            logger.error("Unknown Locust(s): %s\n" % (", ".join(missing)))
            sys.exit(1)
        else:
            names = set(options.locust_classes) & set(locusts.keys())
            locust_classes = [locusts[n] for n in names]
    else:
        # list() call is needed to consume the dict_view object in Python 3
        locust_classes = list(locusts.values())

    if options.show_task_ratio:
        console_logger.info("\n Task ratio per locust class")
        console_logger.info("-" * 80)
        print_task_ratio(locust_classes)
        console_logger.info("\n Total task ratio")
        console_logger.info("-" * 80)
        print_task_ratio(locust_classes, total=True)
        sys.exit(0)
    if options.show_task_ratio_json:
        from json import dumps
        task_data = {
            "per_class": get_task_ratio_dict(locust_classes),
            "total": get_task_ratio_dict(locust_classes, total=True)
        }
        console_logger.info(dumps(task_data))
        sys.exit(0)

    if options.run_time:
        if not options.no_web:
            logger.error(
                "The --run-time argument can only be used together with --no-web"
            )
            sys.exit(1)
        try:
            options.run_time = parse_timespan(options.run_time)
        except ValueError:
            logger.error(
                "Valid --run-time formats are: 20, 20s, 3m, 2h, 1h20m, 3h30m10s, etc."
            )
            sys.exit(1)

        def spawn_run_time_limit_greenlet():
            logger.info("Run time limit set to %s seconds" % options.run_time)

            def timelimit_stop():
                logger.info("Time limit reached. Stopping Locust.")
                runners.locust_runner.quit()

            gevent.spawn_later(options.run_time, timelimit_stop)

    if options.step_time:
        if not options.step_load:
            logger.error(
                "The --step-time argument can only be used together with --step-load"
            )
            sys.exit(1)
        try:
            options.step_time = parse_timespan(options.step_time)
        except ValueError:
            logger.error(
                "Valid --step-time formats are: 20, 20s, 3m, 2h, 1h20m, 3h30m10s, etc."
            )
            sys.exit(1)

    if options.master:
        # Add -d for automatically run slaves
        if options.distribute:
            ptpy = locustfile
            if options.locustfile.endswith('.xls'):
                _type = 'xls'
                pt_s = PtExcel(options.locustfile)
                master_ip, pt_slave_info = pt_s.pt_slave()
            else:
                _type = 'dict'
                with open(options.locustfile, 'r') as f:
                    _d = json.load(f, encoding='utf-8')
                master_ip = _d.get('master_ip')
                pt_slave_info = _d.get('slaves')
            if master_ip == '':
                logger.error(
                    'master IP cannot be None if you use --distribute')
                sys.exit(1)
            if options.boomer:
                locust_cli_slave = 'nohup ./client_v1 --web --master-host={masteIP} > /dev/null 2>&1 &'.format(
                    masteIP=master_ip)
                targets_dict, file_list = gen_boomer_client_json(
                    options.locustfile)
                boomer_client_file = os.path.join(locust_path, 'boomer_client',
                                                  'client_v1')
                file_list.append(boomer_client_file)
                thread_pool = []
                try:
                    for slave in pt_slave_info:
                        if _type == 'xls':
                            slave_ip, slave_username, slave_password = slave
                        else:
                            slave_ip, slave_username, slave_password = slave[
                                'ip'], slave['username'], slave['password']
                        _t = Thread(target=pt_slave_boomer,
                                    args=(slave_ip, slave_username,
                                          slave_password, file_list,
                                          locust_cli_slave, targets_dict))
                        logger.info('Prepare slave {}'.format(slave_ip))
                        thread_pool.append(_t)
                        _t.start()
                    for each_t in thread_pool:
                        each_t.join()
                    file_list.pop()
                    for each in file_list:
                        os.remove(each)
                except KeyboardInterrupt:
                    pass
                except Exception as e:
                    logger.error(
                        'Something happened, collect Exceptions here: {}'.
                        format(e))
            else:
                try:
                    locust_cli_slave = 'nohup locust -f /root/locust_client.py --slave --master-host={masteIP} > /dev/null 2>&1 &'\
                        .format(masteIP=master_ip)
                    thread_pool = []
                    for slave in pt_slave_info:
                        if _type == 'xls':
                            slave_ip, slave_username, slave_password = slave
                        else:
                            slave_ip, slave_username, slave_password = slave[
                                'ip'], slave['username'], slave['password']
                        _t = Thread(target=pt_slave,
                                    args=(slave_ip, slave_username,
                                          slave_password, ptpy,
                                          locust_cli_slave))
                        logger.info('Prepare slave {}'.format(slave_ip))
                        thread_pool.append(_t)
                        _t.start()
                    for each_t in thread_pool:
                        each_t.join()
                except KeyboardInterrupt:
                    pass
                except Exception as e:
                    logger.error(
                        'Something happened, collect Exceptions here: {}'.
                        format(e))

        runners.locust_runner = MasterLocustRunner(locust_classes, options)
    else:
        runners.locust_runner = LocalLocustRunner(locust_classes, options)
    # main_greenlet is pointing to runners.locust_runner.greenlet by default, it will point the web greenlet later if in web mode
    main_greenlet = runners.locust_runner.greenlet

    if options.no_web:
        if options.master:
            while len(runners.locust_runner.clients.ready
                      ) < options.expect_slaves:
                logging.info(
                    "Waiting for slaves to be ready, %s of %s connected",
                    len(runners.locust_runner.clients.ready),
                    options.expect_slaves)
                time.sleep(1)
        if options.step_time:
            runners.locust_runner.start_stepload(options.num_clients,
                                                 options.hatch_rate,
                                                 options.step_clients,
                                                 options.step_time)
        else:
            runners.locust_runner.start_hatching(options.num_clients,
                                                 options.hatch_rate)
            # make locusts are spawned
            time.sleep(1)
    else:
        # spawn web greenlet
        logger.info("Starting web monitor at http://%s:%s" %
                    (options.web_host or "*", options.port))
        main_greenlet = gevent.spawn(web.start, locust_classes, options)

    if options.run_time:
        spawn_run_time_limit_greenlet()

    stats_printer_greenlet = None
    if not options.only_summary and (options.print_stats or options.no_web):
        # spawn stats printing greenlet
        stats_printer_greenlet = gevent.spawn(stats_printer)

    if options.csvfilebase:
        gevent.spawn(stats_writer, options.csvfilebase,
                     options.stats_history_enabled)

    def shutdown(code=0):
        """
        Shut down locust by firing quitting event, printing/writing stats and exiting
        """
        logger.info("Shutting down (exit code %s), bye." % code)
        if stats_printer_greenlet is not None:
            stats_printer_greenlet.kill(block=False)
        logger.info("Cleaning up runner...")
        if runners.locust_runner is not None:
            runners.locust_runner.quit()
        logger.info("Running teardowns...")
        events.quitting.fire(reverse=True)
        print_stats(runners.locust_runner.stats, current=False)
        print_percentile_stats(runners.locust_runner.stats)
        if options.csvfilebase:
            write_stat_csvs(options.csvfilebase, options.stats_history_enabled)
        print_error_report()
        sys.exit(code)

    # install SIGTERM handler
    def sig_term_handler():
        logger.info("Got SIGTERM signal")
        shutdown(0)

    gevent.signal(signal.SIGTERM, sig_term_handler)

    try:
        logger.info("Starting Locust %s" % version)
        main_greenlet.join()
        code = 0
        if len(runners.locust_runner.errors) or len(
                runners.locust_runner.exceptions):
            code = options.exit_code_on_error
        shutdown(code=code)
    except KeyboardInterrupt:
        shutdown(0)
Beispiel #13
0
def main(user_params=None, common_param=None):
    parser, options, arguments = parse_options()

    # setup logging
    setup_logging(options.loglevel, options.logfile)
    logger = logging.getLogger(__name__)

    if options.show_version:
        print("Locust %s" % (version, ))
        sys.exit(0)

    locustfile = find_locustfile(options.locustfile)

    if not locustfile:
        logger.error(
            "Could not find any locustfile! Ensure file ends in '.py' and see --help for available options."
        )
        sys.exit(1)

    if locustfile == "locust.py":
        logger.error(
            "The locustfile must not be named `locust.py`. Please rename the file and try again."
        )
        sys.exit(1)

    docstring, locusts = load_locustfile(locustfile)

    if options.list_commands:
        console_logger.info("Available Locusts:")
        for name in locusts:
            console_logger.info("    " + name)
        sys.exit(0)

    if not locusts:
        logger.error("No Locust class found!")
        sys.exit(1)

    # make sure specified Locust exists
    if arguments:
        missing = set(arguments) - set(locusts.keys())
        if missing:
            logger.error("Unknown Locust(s): %s\n" % (", ".join(missing)))
            sys.exit(1)
        else:
            names = set(arguments) & set(locusts.keys())
            locust_classes = [locusts[n] for n in names]
    else:
        # list() call is needed to consume the dict_view object in Python 3
        locust_classes = list(locusts.values())

    if options.show_task_ratio:
        console_logger.info("\n Task ratio per locust class")
        console_logger.info("-" * 80)
        print_task_ratio(locust_classes)
        console_logger.info("\n Total task ratio")
        console_logger.info("-" * 80)
        print_task_ratio(locust_classes, total=True)
        sys.exit(0)
    if options.show_task_ratio_json:
        from json import dumps
        task_data = {
            "per_class": get_task_ratio_dict(locust_classes),
            "total": get_task_ratio_dict(locust_classes, total=True)
        }
        console_logger.info(dumps(task_data))
        sys.exit(0)

    if not options.no_web and not options.slave:
        # spawn web greenlet
        logger.info("Starting web monitor at %s:%s" %
                    (options.web_host or "*", options.port))
        main_greenlet = gevent.spawn(web.start, locust_classes, options)

    if not options.master and not options.slave:
        if user_params:
            runners.locust_runner = ParameterizableLocustRunner(
                locust_classes, options, user_params, common_param)
        else:
            runners.locust_runner = LocalLocustRunner(locust_classes, options)
        # spawn client spawning/hatching greenlet
        if options.no_web:
            runners.locust_runner.start_hatching(wait=True)
            main_greenlet = runners.locust_runner.greenlet
    elif options.master:
        runners.locust_runner = MasterLocustRunner(locust_classes, options)
        if options.no_web:
            while len(runners.locust_runner.clients.ready
                      ) < options.expect_slaves:
                logging.info(
                    "Waiting for slaves to be ready, %s of %s connected",
                    len(runners.locust_runner.clients.ready),
                    options.expect_slaves)
                time.sleep(1)

            runners.locust_runner.start_hatching(options.num_clients,
                                                 options.hatch_rate)
            main_greenlet = runners.locust_runner.greenlet
    elif options.slave:
        try:
            runners.locust_runner = SlaveLocustRunner(locust_classes, options)
            main_greenlet = runners.locust_runner.greenlet
        except socket.error as e:
            logger.error("Failed to connect to the Locust master: %s", e)
            sys.exit(-1)

    if not options.only_summary and (options.print_stats or
                                     (options.no_web and not options.slave)):
        # spawn stats printing greenlet
        gevent.spawn(stats_printer)

    if options.csvfilebase:
        gevent.spawn(stats_writer, options.csvfilebase)

    def shutdown(code=0):
        """
        Shut down locust by firing quitting event, printing/writing stats and exiting
        """
        logger.info("Shutting down (exit code %s), bye." % code)

        events.quitting.fire()
        print_stats(runners.locust_runner.request_stats)
        print_percentile_stats(runners.locust_runner.request_stats)
        if options.csvfilebase:
            write_stat_csvs(options.csvfilebase)
        print_error_report()
        sys.exit(code)

    # install SIGTERM handler
    def sig_term_handler():
        logger.info("Got SIGTERM signal")
        shutdown(0)

    gevent.signal(gevent.signal.SIGTERM, sig_term_handler)

    try:
        logger.info("Starting Locust %s" % version)
        main_greenlet.join()
        code = 0
        if len(runners.locust_runner.errors):
            code = 1
        shutdown(code=code)
    except KeyboardInterrupt as e:
        shutdown(0)
Beispiel #14
0
    def start_test(self):

        # install SIGTERM handler
        def sig_term_handler():
            logger.info("##### Locust plugin: Got SIGTERM signal")
            self.shutdown(0)
            gevent.signal(signal.SIGTERM, sig_term_handler)

        def spawn_local_slaves(count):
            """
            Spawn *local* locust slaves : data aggregation will NOT work with *remote* slaves
            """
            try:
                args = ['locust']
                args.append('--locustfile={}'.format(str(self.locustfile)))
                args.append('--slave')
                args.append('--master-host={}'.format(self.master_host))
                args.append('--master-port={}'.format(self.master_port))
                args.append('--resplogfile={}'.format(self.locustlog_file))
                logger.info("##### Locust plugin: slave args = {}".format(args))

                # Spawning the slaves in shell processes (security warning with the use of 'shell=True')
                self._locustslaves = [subprocess.Popen(' '.join(args), shell=True, stdin=None,
                            stdout=open('{}/locust-slave-{}.log'.format(self.core.artifacts_dir, i), 'w'),
                            stderr=subprocess.STDOUT) for i in range(count)]
                #slaves = [SlaveLocustRunner(self._locustclasses, self._options) for _ in range(count)] # <-- WRONG: This will spawn slave running on the same CPU core as master
                time.sleep(1)

                logger.info("##### Locust plugin: Started {} new locust slave(s)".format(len(self._locustslaves)))
                logger.info("##### Locust plugin: locust slave(s) PID = {}".format(self._locustslaves))
            except socket.error as e:
                logger.error("##### Locust plugin: Failed to connect to the Locust master: %s", e)
                sys.exit(-1)
            except Exception as e:
                logger.error("##### Locust plugin: Failed to spawn locust slaves: %s", e)
                sys.exit(-1)

        try:
            logger.info("##### Locust plugin: Starting Locust %s" % version)

            # run the locust 

            ### FIXME
            #if self.csvfilebase:
            #    gevent.spawn(stats_writer, self.csvfilebase)
            ### /FIXME

            if self.run_time:
                if not self.no_web:
                    logger.error("##### Locust plugin: The --run-time argument can only be used together with --no-web")
                    sys.exit(1)
                try:
                    self.run_time = parse_timespan(self.run_time)
                except ValueError:
                    logger.error("##### Locust plugin: Valid --time-limit formats are: 20, 20s, 3m, 2h, 1h20m, 3h30m10s, etc.")
                    sys.exit(1)
                def spawn_run_time_limit_greenlet():
                    logger.info("##### Locust plugin: Run time limit set to %s seconds" % self.run_time)
                    def timelimit_stop():
                        logger.info("##### Locust plugin: Time limit reached. Stopping Locust.")
                        self._locustrunner.quit()
                        logger.debug("######## DEBUG: timelimit_stop()/self._locustrunner.quit() passed")
                    def on_greenlet_completion():
                        logger.debug("######## DEBUG: Locust plugin: on_greenlet_completion()")

                    #gevent.spawn_later(self.run_time, timelimit_stop)
                    gl = gevent.spawn_later(self.run_time, timelimit_stop)
                    # linking timelimit greenlet to main greenlet and get a feedback of its execution
                    #gl.link(on_greenlet_completion)



            # locust runner : web monitor
            if not self.no_web and not self.slave and self._locustrunner is None:
                # spawn web greenlet
                logger.info("##### Locust plugin: Starting web monitor at %s:%s" % (self.web_host or "*", self.port))
                main_greenlet = gevent.spawn(web.start, self._locustclasses, self._options)


            # locust runner : standalone
            if not self.master and not self.slave and self._locustrunner is None:
                logger.info("##### Locust plugin: LocalLocustRunner about to be launched")
                self._locustrunner = LocalLocustRunner(self._locustclasses, self._options)
                # spawn client spawning/hatching greenlet
                if self.no_web:
                    logger.info("##### Locust plugin: LocalLocustRunner.start_hatching()")
                    self._locustrunner.start_hatching(wait=True)
                    main_greenlet = self._locustrunner.greenlet
                if self.run_time:
                    logger.info("##### Locust plugin: spawn_run_time_limit_greenlet()")
                    spawn_run_time_limit_greenlet()
                    logger.info("##### Locust plugin: spawn_run_time_limit_greenlet() passed")

            # locust runner : master/slave mode (master here)
            elif self.master and self._locustrunner is None:
                self._locustrunner = MasterLocustRunner(self._locustclasses, self._options)
                logger.info("##### Locust plugin: MasterLocustRunner started")
                time.sleep(1)
                if self.no_web:
                    gevent.spawn(spawn_local_slaves(self.expect_slaves))
                    while len(self._locustrunner.clients.ready) < self.expect_slaves:
                        logger.info("##### Locust plugin: Waiting for slaves to be ready, %s of %s connected",
                                     len(self._locustrunner.clients.ready), self.expect_slaves)
                        time.sleep(1)
                    self._locustrunner.start_hatching(self.num_clients, self.hatch_rate)
                    logger.debug("######## DEBUG: MasterLocustRunner/start_hatching()")
                    main_greenlet = self._locustrunner.greenlet
                if self.run_time:
                    try:
                        spawn_run_time_limit_greenlet()
                    except Exception as e:
                        logger.error("##### Locust plugin: exception raised in spawn_run_time_limit_greenlet() = {}".format(e))

            # locust runner : master/slave mode (slave here)
            #elif self.slave and self._locustrunner is None:
            #    if self.run_time:
            #        logger.error("##### Locust plugin: --run-time should be specified on the master node, and not on slave nodes")
            #        sys.exit(1)
            #    try:
            #        self._locustrunner = SlaveLocustRunner(self._locustclasses, self._options)
            #        main_greenlet = self._locustrunner.greenlet
            #    except socket.error as e:
            #        logger.error("##### Locust plugin: Failed to connect to the Locust master: %s", e)
            #        sys.exit(-1)
            return self._locustrunner

            self._locustrunner.greenlet.join()
            code = 0
            if len(self._locustrunner.errors):
                code = 1
                self.shutdown(code=code)
        except KeyboardInterrupt as e:
            self.shutdown(0)