Exemple #1
0
    def test_heartbeat(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.slave.Process", mocked_process()):
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                server.mocked_send(
                    'all', Message("worker_ready", None, "fake_client0"))
                self.slave.start_hatching(1, 1)
                sleep(runners.slave.HEARTBEAT_INTERVAL)

                self.assertEqual(1, len(server.outbox_all))
                self.assertEqual(
                    'ping',
                    Message.unserialize(server.outbox_all[0]).type)
                server.mocked_send('all', Message("pong", None,
                                                  "fake_client0"))
                sleep(runners.slave.HEARTBEAT_INTERVAL)
                self.assertEqual(1, self.slave.worker_count)
                sleep(runners.slave.HEARTBEAT_INTERVAL)
                self.assertEqual(0, self.slave.worker_count)
Exemple #2
0
    def test_worker_stats_report_median(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.slave.Process", mocked_process()):
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                server.mocked_send(
                    'all', Message("worker_ready", None, "fake_client"))
                sleep(0)
                self.slave.start_hatching(1, 1)

                self.slave.stats.get("Task", "/", "GET").log(100, 23455)
                self.slave.stats.get("Task", "/", "GET").log(800, 23455)
                self.slave.stats.get("Task", "/", "GET").log(700, 23455)

                data = {"user_count": 1}
                events.report_to_master.fire(node_id="fake_client", data=data)
                self.slave.stats.clear_all()

                server.mocked_send("all", Message("stats", data,
                                                  "fake_client"))
                sleep(0)
                s = self.slave.stats.get("Task", "/", "GET")
                self.assertEqual(700, s.median_response_time)
Exemple #3
0
    def test_worker_amount_spawn(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.slave.Process",
                            mocked_process()) as processes:
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())

                timeout = gevent.Timeout(3.0)
                timeout.start()

                try:
                    for i in range(5):
                        server.mocked_send(
                            "all",
                            Message("worker_ready", None, "fake_client%i" % i))
                    self.slave.start_hatching(42, 2)
                except gevent.Timeout:
                    self.fail("Got Timeout exception")
                finally:
                    timeout.cancel()

                self.assertEqual(5, len(processes.started))
                self.assertEqual(5, self.slave.worker_count)
Exemple #4
0
    def test_worker_connect(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.slave.Process", mocked_process()):
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                server.mocked_send(
                    'all', Message("worker_ready", None, "zeh_fake_client1"))
                sleep(0)
                self.slave.start_hatching(1, 1)
                self.assertEqual(1, self.slave.worker_count)
                self.assertTrue(
                    "zeh_fake_client1" in self.slave.workers,
                    "Could not find fake client in master instance's clients dict"
                )
                server.mocked_send(
                    "all", Message("worker_ready", None, "zeh_fake_client2"))
                server.mocked_send(
                    "all", Message("worker_ready", None, "zeh_fake_client3"))
                server.mocked_send(
                    "all", Message("worker_ready", None, "zeh_fake_client4"))
                sleep(0)
                self.assertEqual(4, self.slave.worker_count)

                server.mocked_send("all",
                                   Message("quit", None, "zeh_fake_client3"))
                sleep(0)
                self.assertEqual(3, self.slave.worker_count)
Exemple #5
0
 def test_change_user_count_during_hatching(self):
     class User(Locust):
         wait_time = constant(1)
         class task_set(TaskSet):
             @task
             def my_task(self):
                 pass
     
     with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
         options = mocked_options()
         options.stop_timeout = None
         slave = SlaveLocustRunner([User], options)
         
         client.mocked_send(Message("hatch", {
             "hatch_rate": 5,
             "num_clients": 10,
             "host": "",
             "stop_timeout": None,
         }, "dummy_client_id"))
         sleep(0.6)
         self.assertEqual(STATE_HATCHING, slave.state)
         client.mocked_send(Message("hatch", {
             "hatch_rate": 5,
             "num_clients": 9,
             "host": "",
             "stop_timeout": None,
         }, "dummy_client_id"))
         sleep(0)
         slave.hatching_greenlet.join()
         self.assertEqual(9, len(slave.locusts))
         slave.quit()
Exemple #6
0
 def get_runner(self, environment=None, locust_classes=[]):
     if environment is None:
         environment = self.environment
     return SlaveLocustRunner(environment,
                              locust_classes,
                              master_host="localhost",
                              master_port=5557)
Exemple #7
0
 def test_slave_stop_timeout(self):
     class MyTestLocust(Locust):
         _test_state = 0
         class task_set(TaskSet):
             wait_time = constant(0)
             @task
             def the_task(self):
                 MyTestLocust._test_state = 1
                 gevent.sleep(0.2)
                 MyTestLocust._test_state = 2
     
     with mock.patch("locust.rpc.rpc.Client", mocked_rpc()) as client:
         options = mocked_options()
         slave = SlaveLocustRunner([MyTestLocust], options)
         self.assertEqual(1, len(client.outbox))
         self.assertEqual("client_ready", client.outbox[0].type)
         client.mocked_send(Message("hatch", {
             "hatch_rate": 1,
             "num_clients": 1,
             "host": "",
             "stop_timeout": 1,
         }, "dummy_client_id"))
         #print("outbox:", client.outbox)
         # wait for slave to hatch locusts
         self.assertIn("hatching", [m.type for m in client.outbox])
         slave.hatching_greenlet.join()
         self.assertEqual(1, len(slave.locusts))
         # check that locust has started running
         gevent.sleep(0.01)
         self.assertEqual(1, MyTestLocust._test_state)
         # send stop message
         client.mocked_send(Message("stop", None, "dummy_client_id"))
         slave.locusts.join()
         # check that locust user got to finish
         self.assertEqual(2, MyTestLocust._test_state)
Exemple #8
0
    def test_on_ping(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveClient",
                        mocked_rpc_server()) as client:
            with mock.patch("locust.runners.slave.Process",
                            mocked_process()) as processes:
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                sleep(0)
                self.assertEqual(2, len(client.outbox_all))
                client.mocked_send('all', Message("ping", None, "master"))
                sleep(0)
                self.assertEqual(3, len(client.outbox_all))
Exemple #9
0
    def test_on_hatch(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.rpc.rpc.SlaveClient",
                            mocked_rpc_server()) as client:
                with mock.patch("locust.runners.slave.Process",
                                mocked_process()) as processes:
                    self.slave = SlaveLocustRunner(MyTestLocust,
                                                   config.locust_config())
                    for i in range(5):
                        server.mocked_send(
                            "all",
                            Message("worker_ready", None, "fake_client%i" % i))

                    timeout = gevent.Timeout(2.0)
                    timeout.start()

                    try:
                        data = {
                            "hatch_rate": 10,
                            "num_clients": 43,
                            "num_requests": None,
                            "host": 'host',
                            "stop_timeout": None
                        }
                        client.mocked_send('all',
                                           Message("hatch", data, "master"))
                    except gevent.Timeout:
                        self.fail(
                            "Got Timeout exception. A locust seems to have been spawned, even though 0 was specified."
                        )
                    finally:
                        timeout.cancel()

                    sleep(0)
                    self.assertEqual(5, len(processes.started))
Exemple #10
0
    def test_stats_reporting(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.rpc.rpc.SlaveClient",
                            mocked_rpc_server()) as client:
                with mock.patch("locust.runners.slave.Process",
                                mocked_process()) as processes:
                    self.slave = SlaveLocustRunner(MyTestLocust,
                                                   config.locust_config())
                    server.mocked_send(
                        'all', Message("worker_ready", None, "fake_client0"))
                    self.slave.start_hatching(1, 1)

                    self.slave.stats.get("Task", "/", "GET").log(100, 23455)
                    self.slave.stats.get("Task", "/", "GET").log(800, 23455)
                    self.slave.stats.get("Task", "/", "GET").log(700, 23455)

                    sleep(runners.slave.SLAVE_STATS_INTERVAL + 0.1)
                    messages = [
                        Message.unserialize(msg) for msg in client.outbox_all
                    ]
                    data = filter(
                        lambda m: m.type == 'stats' and m.data['stats'],
                        messages)[0].data

                    self.assertEqual({
                        800: 1,
                        100: 1,
                        700: 1
                    }, data['stats'][0]['response_times'])
                    self.assertEqual(3, data['stats'][0]['num_requests'])
                    self.assertEqual(1600,
                                     data['stats'][0]['total_response_time'])
                    self.assertEqual(1, data['worker_count'])
Exemple #11
0
    def test_spawn_uneven_locusts(self):
        """
        Tests that we can accurately spawn a certain number of locusts, even if it's not an
        even number of the connected slaves
        """
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.slave.Process", mocked_process()):
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                server.mocked_send(
                    'all', Message("worker_ready", None, "fake_client0"))
                sleep(0)
                self.slave.start_hatching(1, 1)
                for i in range(1, 5):
                    server.mocked_send(
                        "all",
                        Message("worker_ready", None, "fake_client%i" % i))
                    sleep(0)

                del server.outbox_direct[:]
                self.slave.start_hatching(42, 7)

                self.assertEqual(5, len(server.outbox_direct))

                num_clients = 0
                for msg in server.outbox_direct:
                    num_clients += Message.unserialize(
                        msg[1]).data["num_clients"]
                self.assertEqual(
                    42, num_clients,
                    "Total number of locusts that would have been spawned is not 42"
                )
Exemple #12
0
    def test_worker_receive_propagated_config(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveClient",
                        mocked_rpc_server()) as client:
            with mock.patch("locust.runners.slave.Process",
                            mocked_process()) as processes:
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                sleep(0)
                self.assertEqual(2, len(client.outbox_all))
                msg = {
                    'host': 'custom_host.com',
                    'master_host': 'new_master_host.com'
                }
                client.mocked_send('all', Message("new_config", msg, "master"))
                sleep(0)
                self.assertEqual(self.slave.options.host,
                                 'http://custom_host.com')
                self.assertEqual(self.slave.options.master_host, '127.0.0.1')
Exemple #13
0
    def worker_relaunch(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.rpc.rpc.SlaveClient",
                            mocked_rpc_server()) as client:
                with mock.patch("locust.runners.slave.Process",
                                mocked_process()) as processes:
                    self.slave = SlaveLocustRunner(MyTestLocust,
                                                   config.locust_config())
                    server.mocked_send(
                        'all', Message("worker_ready", None, "fake_client0"))
                    self.slave.start_hatching(1, 1)
                    self.assertEqual(1, len(processes.started))
                    sleep(2 * runners.slave.HEARTBEAT_INTERVAL)
                    self.assertEqual(0, self.slave.worker_count)
                    client.mocked_send('all', Message("ping", None, "master"))
                    sleep(runners.slave.HEARTBEAT_INTERVAL)
                    self.assertEqual(2, len(processes.started))
Exemple #14
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)
Exemple #15
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.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_new_demo = os.path.join(os.getcwd(), 'PtDemo.xls')
        shutil.copyfile(pt_demo_path, pt_new_demo)
        sys.exit(0)

    if options.xlsfile:
        pt_file = options.xlsfile
        if not pt_file.endswith('.xls'):
            logger.error(
                "PressureTest file must be end with '.xls' 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)
        make_locustfile(pt_file)
        logger.info('Transform XLS to locustfile finish.')
        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":
        logger.error(
            "The locustfile must not be named `locust.py` or `locust.xls`. 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)
        if options.slave:
            logger.error(
                "--run-time should be specified on the master node, and not on slave nodes"
            )
            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)
        if options.slave:
            logger.error(
                "--step-time should be specified on the master node, and not on slave nodes"
            )
            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
            pt_s = PtExcel(options.locustfile)
            master_ip, pt_slave_info = pt_s.pt_slave()
            if master_ip == '':
                logger.error(
                    'master IP cannot be None if you use --distribute')
                sys.exit(1)
            try:
                locust_cli_slave = 'nohup locust -f /root/{locustfile} --slave --master-host={masteIP} > /dev/null 2>&1 &'.format(
                    locustfile=ptpy, masteIP=master_ip)
                thread_pool = []
                for slave in pt_slave_info:
                    slave_ip, slave_username, slave_password = slave
                    _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(
                    'Must something happened, collect Exceptions here: {}'.
                    format(e))

        runners.locust_runner = MasterLocustRunner(locust_classes, options)
    elif options.slave:
        try:
            runners.locust_runner = SlaveLocustRunner(locust_classes, options)
        except socket.error as e:
            logger.error("Failed to connect to the Locust master: %s", e)
            sys.exit(-1)
    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)
        elif not options.slave:
            runners.locust_runner.start_hatching(options.num_clients,
                                                 options.hatch_rate)
            # make locusts are spawned
            time.sleep(1)
    elif not options.slave:
        # 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 and not options.slave)):
        # 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 as e:
        shutdown(0)
Exemple #16
0
class TestSlaveRunner(LocustTestCase):
    def setUp(self):
        global_stats.reset_all()
        self._slave_report_event_handlers = [
            h for h in events.node_report._handlers
        ]

    def tearDown(self):
        events.node_report._handlers = self._slave_report_event_handlers
        self.slave.quit()

    def test_worker_connect(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.slave.Process", mocked_process()):
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                server.mocked_send(
                    'all', Message("worker_ready", None, "zeh_fake_client1"))
                sleep(0)
                self.slave.start_hatching(1, 1)
                self.assertEqual(1, self.slave.worker_count)
                self.assertTrue(
                    "zeh_fake_client1" in self.slave.workers,
                    "Could not find fake client in master instance's clients dict"
                )
                server.mocked_send(
                    "all", Message("worker_ready", None, "zeh_fake_client2"))
                server.mocked_send(
                    "all", Message("worker_ready", None, "zeh_fake_client3"))
                server.mocked_send(
                    "all", Message("worker_ready", None, "zeh_fake_client4"))
                sleep(0)
                self.assertEqual(4, self.slave.worker_count)

                server.mocked_send("all",
                                   Message("quit", None, "zeh_fake_client3"))
                sleep(0)
                self.assertEqual(3, self.slave.worker_count)

    def test_worker_receive_propagated_config(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveClient",
                        mocked_rpc_server()) as client:
            with mock.patch("locust.runners.slave.Process",
                            mocked_process()) as processes:
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                sleep(0)
                self.assertEqual(2, len(client.outbox_all))
                msg = {
                    'host': 'custom_host.com',
                    'master_host': 'new_master_host.com'
                }
                client.mocked_send('all', Message("new_config", msg, "master"))
                sleep(0)
                self.assertEqual(self.slave.options.host,
                                 'http://custom_host.com')
                self.assertEqual(self.slave.options.master_host, '127.0.0.1')

    def test_worker_stats_report_median(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.slave.Process", mocked_process()):
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                server.mocked_send(
                    'all', Message("worker_ready", None, "fake_client"))
                sleep(0)
                self.slave.start_hatching(1, 1)

                self.slave.stats.get("Task", "/", "GET").log(100, 23455)
                self.slave.stats.get("Task", "/", "GET").log(800, 23455)
                self.slave.stats.get("Task", "/", "GET").log(700, 23455)

                data = {"user_count": 1}
                events.report_to_master.fire(node_id="fake_client", data=data)
                self.slave.stats.clear_all()

                server.mocked_send("all", Message("stats", data,
                                                  "fake_client"))
                sleep(0)
                s = self.slave.stats.get("Task", "/", "GET")
                self.assertEqual(700, s.median_response_time)

    def test_spawn_uneven_locusts(self):
        """
        Tests that we can accurately spawn a certain number of locusts, even if it's not an
        even number of the connected slaves
        """
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.slave.Process", mocked_process()):
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                server.mocked_send(
                    'all', Message("worker_ready", None, "fake_client0"))
                sleep(0)
                self.slave.start_hatching(1, 1)
                for i in range(1, 5):
                    server.mocked_send(
                        "all",
                        Message("worker_ready", None, "fake_client%i" % i))
                    sleep(0)

                del server.outbox_direct[:]
                self.slave.start_hatching(42, 7)

                self.assertEqual(5, len(server.outbox_direct))

                num_clients = 0
                for msg in server.outbox_direct:
                    num_clients += Message.unserialize(
                        msg[1]).data["num_clients"]
                self.assertEqual(
                    42, num_clients,
                    "Total number of locusts that would have been spawned is not 42"
                )

    def test_worker_amount_spawn(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.slave.Process",
                            mocked_process()) as processes:
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())

                timeout = gevent.Timeout(3.0)
                timeout.start()

                try:
                    for i in range(5):
                        server.mocked_send(
                            "all",
                            Message("worker_ready", None, "fake_client%i" % i))
                    self.slave.start_hatching(42, 2)
                except gevent.Timeout:
                    self.fail("Got Timeout exception")
                finally:
                    timeout.cancel()

                self.assertEqual(5, len(processes.started))
                self.assertEqual(5, self.slave.worker_count)

    def test_heartbeat(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.slave.Process", mocked_process()):
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                server.mocked_send(
                    'all', Message("worker_ready", None, "fake_client0"))
                self.slave.start_hatching(1, 1)
                sleep(runners.slave.HEARTBEAT_INTERVAL)

                self.assertEqual(1, len(server.outbox_all))
                self.assertEqual(
                    'ping',
                    Message.unserialize(server.outbox_all[0]).type)
                server.mocked_send('all', Message("pong", None,
                                                  "fake_client0"))
                sleep(runners.slave.HEARTBEAT_INTERVAL)
                self.assertEqual(1, self.slave.worker_count)
                sleep(runners.slave.HEARTBEAT_INTERVAL)
                self.assertEqual(0, self.slave.worker_count)

    def worker_relaunch(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.rpc.rpc.SlaveClient",
                            mocked_rpc_server()) as client:
                with mock.patch("locust.runners.slave.Process",
                                mocked_process()) as processes:
                    self.slave = SlaveLocustRunner(MyTestLocust,
                                                   config.locust_config())
                    server.mocked_send(
                        'all', Message("worker_ready", None, "fake_client0"))
                    self.slave.start_hatching(1, 1)
                    self.assertEqual(1, len(processes.started))
                    sleep(2 * runners.slave.HEARTBEAT_INTERVAL)
                    self.assertEqual(0, self.slave.worker_count)
                    client.mocked_send('all', Message("ping", None, "master"))
                    sleep(runners.slave.HEARTBEAT_INTERVAL)
                    self.assertEqual(2, len(processes.started))

    def test_on_ping(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveClient",
                        mocked_rpc_server()) as client:
            with mock.patch("locust.runners.slave.Process",
                            mocked_process()) as processes:
                self.slave = SlaveLocustRunner(MyTestLocust,
                                               config.locust_config())
                sleep(0)
                self.assertEqual(2, len(client.outbox_all))
                client.mocked_send('all', Message("ping", None, "master"))
                sleep(0)
                self.assertEqual(3, len(client.outbox_all))

    def test_on_hatch(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.rpc.rpc.SlaveClient",
                            mocked_rpc_server()) as client:
                with mock.patch("locust.runners.slave.Process",
                                mocked_process()) as processes:
                    self.slave = SlaveLocustRunner(MyTestLocust,
                                                   config.locust_config())
                    for i in range(5):
                        server.mocked_send(
                            "all",
                            Message("worker_ready", None, "fake_client%i" % i))

                    timeout = gevent.Timeout(2.0)
                    timeout.start()

                    try:
                        data = {
                            "hatch_rate": 10,
                            "num_clients": 43,
                            "num_requests": None,
                            "host": 'host',
                            "stop_timeout": None
                        }
                        client.mocked_send('all',
                                           Message("hatch", data, "master"))
                    except gevent.Timeout:
                        self.fail(
                            "Got Timeout exception. A locust seems to have been spawned, even though 0 was specified."
                        )
                    finally:
                        timeout.cancel()

                    sleep(0)
                    self.assertEqual(5, len(processes.started))

    def test_stats_reporting(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.SlaveServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.rpc.rpc.SlaveClient",
                            mocked_rpc_server()) as client:
                with mock.patch("locust.runners.slave.Process",
                                mocked_process()) as processes:
                    self.slave = SlaveLocustRunner(MyTestLocust,
                                                   config.locust_config())
                    server.mocked_send(
                        'all', Message("worker_ready", None, "fake_client0"))
                    self.slave.start_hatching(1, 1)

                    self.slave.stats.get("Task", "/", "GET").log(100, 23455)
                    self.slave.stats.get("Task", "/", "GET").log(800, 23455)
                    self.slave.stats.get("Task", "/", "GET").log(700, 23455)

                    sleep(runners.slave.SLAVE_STATS_INTERVAL + 0.1)
                    messages = [
                        Message.unserialize(msg) for msg in client.outbox_all
                    ]
                    data = filter(
                        lambda m: m.type == 'stats' and m.data['stats'],
                        messages)[0].data

                    self.assertEqual({
                        800: 1,
                        100: 1,
                        700: 1
                    }, data['stats'][0]['response_times'])
                    self.assertEqual(3, data['stats'][0]['num_requests'])
                    self.assertEqual(1600,
                                     data['stats'][0]['total_response_time'])
                    self.assertEqual(1, data['worker_count'])
Exemple #17
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)