示例#1
0
    def test_spawn_locusts_in_stepload_mode(self):
        class MyTestLocust(Locust):
            pass
        
        with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
            master = MasterLocustRunner([MyTestLocust], self.options)
            for i in range(5):
                server.mocked_send(Message("client_ready", None, "fake_client%i" % i))

            # start a new swarming in Step Load mode: total locust count of 10, hatch rate of 2, step locust count of 5, step duration of 5s
            master.start_stepload(10, 2, 5, 5)

            # make sure the first step run is started
            sleep(1)
            self.assertEqual(5, len(server.outbox))

            num_clients = 0
            end_of_last_step = len(server.outbox)
            for _, msg in server.outbox:
                num_clients += msg.data["num_clients"]
            
            self.assertEqual(5, num_clients, "Total number of locusts that would have been spawned for first step is not 5")

            # make sure the first step run is complete
            sleep(5)
            num_clients = 0
            idx = end_of_last_step
            while idx < len(server.outbox):
                msg = server.outbox[idx][1]
                num_clients += msg.data["num_clients"]
                idx += 1
            self.assertEqual(10, num_clients, "Total number of locusts that would have been spawned for second step is not 10")
示例#2
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
        """
        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.Server",
                        mocked_rpc_server()) as server:
            master = MasterLocustRunner(MyTestLocust, self.options)
            for i in range(5):
                server.mocked_send(
                    Message("client_ready", None, "fake_client%i" % i))

            master.start_hatching(7, 7)
            self.assertEqual(5, len(server.outbox))

            num_clients = 0
            for _, msg in server.outbox:
                num_clients += Message.unserialize(msg).data["num_clients"]

            self.assertEqual(
                7, num_clients,
                "Total number of locusts that would have been spawned is not 7"
            )
示例#3
0
    def test_spawn_fewer_locusts_than_slaves(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.Server",
                        mocked_rpc_server()) as server:
            master = MasterLocustRunner(MyTestLocust, self.options)
            for i in range(5):
                server.mocked_send(
                    Message("client_ready", None, "fake_client%i" % i))
                sleep(0)

            master.start_hatching(2, 2)
            self.assertEqual(5, len(server.outbox))

            num_clients = 0
            for msg in server.outbox:
                num_clients += Message.unserialize(msg).data["num_clients"]

            self.assertEqual(
                2, num_clients,
                "Total number of locusts that would have been spawned is not 2"
            )
示例#4
0
    def test_heartbeat(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.MasterServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.master.Process", mocked_process()):
                server.mocked_send(
                    'all', Message("slave_ready", None, "fake_client0"))
                self.master = MasterLocustRunner(MyTestLocust,
                                                 config.locust_config())

                sleep(0)
                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.master.HEARTBEAT_INTERVAL)
                self.assertEqual(1, self.master.slave_count)
                sleep(runners.master.HEARTBEAT_INTERVAL)
                self.assertEqual(0, self.master.slave_count)
示例#5
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.MasterServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.master.Process", mocked_process()):
                server.mocked_send(
                    'all', Message("slave_ready", None, "fake_client0"))
                self.master = MasterLocustRunner(MyTestLocust,
                                                 config.locust_config())
                for i in range(1, 5):
                    server.mocked_send(
                        "all", Message("slave_ready", None,
                                       "fake_client%i" % i))
                    sleep(0)

                server.outbox_direct = []
                self.master.start_hatching(7, 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(
                    7, num_clients,
                    "Total number of locusts that would have been spawned is not 7"
                )
示例#6
0
    def test_spawn_fewer_locusts_than_slaves(self):
        import mock

        class MyTestLocust(Locust):
            pass

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

                server.outbox_direct = []
                self.master.start_hatching(2, 2)
                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(
                    2, num_clients,
                    "Total number of locusts that would have been spawned is not 2"
                )
示例#7
0
    def test_slave_stats_report_median(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.MasterServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.master.Process", mocked_process()):
                server.mocked_send('all',
                                   Message("slave_ready", None, "fake_client"))
                self.master = MasterLocustRunner(MyTestLocust,
                                                 config.locust_config())
                sleep(0)

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

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

                server.mocked_send("all", Message("stats", data,
                                                  "fake_client"))
                sleep(0)
                s = self.master.stats.get("Task", "/", "GET")
                self.assertEqual(700, s.median_response_time)
示例#8
0
    def test_on_demand_options_propagate(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.MasterServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.master.Process", mocked_process()):
                server.mocked_send(
                    'all', Message("slave_ready", None, "zeh_fake_client1"))
                self.master = MasterLocustRunner(MyTestLocust,
                                                 config.locust_config())
                sleep(0)
                self.assertEqual(1, self.master.slave_count)
                server.outbox_all = []
                config_upd = {
                    'host': 'custom_host.com',
                    'master_host': 'new_master_host.com'
                }
                self.master.propagate_config(config_upd)
                self.assertEqual(1, len(server.outbox_all))

                msg = Message.unserialize(server.outbox_all[0]).data
                self.assertEqual(msg['host'], 'custom_host.com')
                self.assertEqual(msg['master_host'], '127.0.0.1')
示例#9
0
 def test_rebalance_locust_users_on_slave_connect(self):
     class MyTestLocust(Locust):
         pass
     
     with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
         master = MasterLocustRunner(MyTestLocust, self.options)
         server.mocked_send(Message("client_ready", None, "zeh_fake_client1"))
         self.assertEqual(1, len(master.clients))
         self.assertTrue("zeh_fake_client1" in master.clients, "Could not find fake client in master instance's clients dict")
         
         master.start_hatching(100, 20)
         self.assertEqual(1, len(server.outbox))
         client_id, msg = server.outbox.pop()
         self.assertEqual(100, msg.data["num_clients"])
         self.assertEqual(20, msg.data["hatch_rate"])
         
         # let another slave connect
         server.mocked_send(Message("client_ready", None, "zeh_fake_client2"))
         self.assertEqual(2, len(master.clients))
         self.assertEqual(2, len(server.outbox))
         client_id, msg = server.outbox.pop()
         self.assertEqual(50, msg.data["num_clients"])
         self.assertEqual(10, msg.data["hatch_rate"])
         client_id, msg = server.outbox.pop()
         self.assertEqual(50, msg.data["num_clients"])
         self.assertEqual(10, msg.data["hatch_rate"])
示例#10
0
    def test_slave_connect(self):
        import mock

        class MyTestLocust(Locust):
            pass

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

                server.mocked_send("all",
                                   Message("quit", None, "zeh_fake_client3"))
                sleep(0)
                self.assertEqual(3, self.master.slave_count)
示例#11
0
    def test_csv_stats_on_master_from_aggregated_stats(self):
        # Failing test for: https://github.com/locustio/locust/issues/1315
        with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
            master = MasterLocustRunner(self.environment, [],
                                        master_bind_host="*",
                                        master_bind_port=0)
            server.mocked_send(Message("client_ready", None, "fake_client"))

            master.stats.get("/", "GET").log(100, 23455)
            master.stats.get("/", "GET").log(800, 23455)
            master.stats.get("/", "GET").log(700, 23455)

            data = {"user_count": 1}
            self.environment.events.report_to_master.fire(
                client_id="fake_client", data=data)
            master.stats.clear_all()

            server.mocked_send(Message("stats", data, "fake_client"))
            s = master.stats.get("/", "GET")
            self.assertEqual(700, s.median_response_time)

            locust.stats.write_csv_files(master.stats,
                                         self.STATS_BASE_NAME,
                                         full_history=True)
            self.assertTrue(os.path.exists(self.STATS_FILENAME))
            self.assertTrue(os.path.exists(self.STATS_HISTORY_FILENAME))
            self.assertTrue(os.path.exists(self.STATS_FAILURES_FILENAME))
示例#12
0
 def test_drone_stats_report_with_none_response_times(self):
     class MyTestLocust(Locust):
         pass
     
     with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
         master = MasterLocustRunner(MyTestLocust, self.options)
         server.mocked_send(Message("client_ready", None, "fake_client"))
         
         master.stats.get("/mixed", "GET").log(0, 23455)
         master.stats.get("/mixed", "GET").log(800, 23455)
         master.stats.get("/mixed", "GET").log(700, 23455)
         master.stats.get("/mixed", "GET").log(None, 23455)
         master.stats.get("/mixed", "GET").log(None, 23455)
         master.stats.get("/mixed", "GET").log(None, 23455)
         master.stats.get("/mixed", "GET").log(None, 23455)
         master.stats.get("/onlyNone", "GET").log(None, 23455)
         
         data = {"user_count":1}
         events.report_to_master.fire(client_id="fake_client", data=data)
         master.stats.clear_all()
         
         server.mocked_send(Message("stats", data, "fake_client"))
         s1 = master.stats.get("/mixed", "GET")
         self.assertEqual(700, s1.median_response_time)
         self.assertEqual(500, s1.avg_response_time)            
         s2 = master.stats.get("/onlyNone", "GET")
         self.assertEqual(0, s2.median_response_time)
         self.assertEqual(0, s2.avg_response_time)
示例#13
0
 def test_master_total_stats_with_none_response_times(self):
     class MyTestLocust(Locust):
         pass
     
     with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
         master = MasterLocustRunner(MyTestLocust, self.options)
         server.mocked_send(Message("client_ready", None, "fake_client"))
         stats = RequestStats()
         stats.log_request("GET", "/1", 100, 3546)
         stats.log_request("GET", "/1", 800, 56743)
         stats.log_request("GET", "/1", None, 56743)
         stats2 = RequestStats()
         stats2.log_request("GET", "/2", 700, 2201)
         stats2.log_request("GET", "/2", None, 2201)
         stats3 = RequestStats()
         stats3.log_request("GET", "/3", None, 2201)
         server.mocked_send(Message("stats", {
             "stats":stats.serialize_stats(), 
             "stats_total": stats.total.serialize(),
             "errors":stats.serialize_errors(),
             "user_count": 1,
         }, "fake_client"))
         server.mocked_send(Message("stats", {
             "stats":stats2.serialize_stats(), 
             "stats_total": stats2.total.serialize(),
             "errors":stats2.serialize_errors(),
             "user_count": 2,
         }, "fake_client"))
         server.mocked_send(Message("stats", {
             "stats":stats3.serialize_stats(), 
             "stats_total": stats3.total.serialize(),
             "errors":stats3.serialize_errors(),
             "user_count": 2,
         }, "fake_client"))
         self.assertEqual(700, master.stats.total.median_response_time)
示例#14
0
    def test_slave_connect(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.Server",
                        mocked_rpc_server()) as server:
            master = MasterLocustRunner(MyTestLocust, self.options)
            server.mocked_send(
                Message("client_ready", None, "zeh_fake_client1"))
            sleep(0)
            self.assertEqual(1, len(master.clients))
            self.assertTrue(
                "zeh_fake_client1" in master.clients,
                "Could not find fake client in master instance's clients dict")
            server.mocked_send(
                Message("client_ready", None, "zeh_fake_client2"))
            server.mocked_send(
                Message("client_ready", None, "zeh_fake_client3"))
            server.mocked_send(
                Message("client_ready", None, "zeh_fake_client4"))
            sleep(0)
            self.assertEqual(4, len(master.clients))

            server.mocked_send(Message("quit", None, "zeh_fake_client3"))
            sleep(0)
            self.assertEqual(3, len(master.clients))
示例#15
0
 def test_spawn_fewer_locusts_than_slaves(self):
     class MyTestLocust(Locust):
         pass
     
     with mock.patch("locust.rpc.rpc.Server", mocked_rpc_server()) as server:
         master = MasterLocustRunner(MyTestLocust, self.options)
         for i in range(5):
             server.mocked_send(Message("client_ready", None, "fake_client%i" % i))
         
         master.start_hatching(2, 2)
         self.assertEqual(5, len(server.outbox))
         
         num_clients = 0
         for _, msg in server.outbox:
             num_clients += Message.unserialize(msg).data["num_clients"]
         
         self.assertEqual(2, num_clients, "Total number of locusts that would have been spawned is not 2")
示例#16
0
    def test_automatic_options_propagate(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.MasterServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.master.Process", mocked_process()):
                server.mocked_send(
                    'all', Message("slave_ready", None, "zeh_fake_client1"))
                self.master = MasterLocustRunner(MyTestLocust,
                                                 config.locust_config())
                sleep(0)
                self.assertEqual(1, self.master.slave_count)
                self.assertEqual(1, len(server.outbox_direct))
                msg = Message.unserialize(server.outbox_direct[0][1]).data
                self.assertEqual(self.master.options._config, msg)
示例#17
0
def launch(classname, n_clients, run_time=180):
    """
    Launches the tests
    :param: classname: class inherited from HttpLocust defining the test
    :param: n_clients: Number of concurrent users
    :param: run_time: Stop testing after the specified amount of seconds
    """

    base_url = os.environ.get('KOLIBRI_BASE_URL', 'http://127.0.0.1:8000')
    options = Namespace(**{
        'host': base_url,
        'num_clients': n_clients,
        'hatch_rate': n_clients,  # this way it will be 1 request per second per client
        'num_requests': 9999999,  # obsolete, discontinued in new locust versions
        'run_time': run_time,
        'no_web': True,
        'no_reset_stats': True,
        'csvfilebase': os.path.join(get_or_create_output_dir(), get_csv_filename()),
        'expect_slaves': 1,
        'master_host': '127.0.0.1',
        'master_port': 5557,
        'master_bind_host': '*',
        'master_bind_port': 5557

    })

    setup_logging('INFO', None)
    if classname.__module__ == '__main__':
        test_path = 'scenarios/{}.py'.format(get_test_calling())
    else:
        test_path = 'scenarios/{}.py'.format(classname.__module__)
    locust_executable = spawn.find_executable('locust')
    slave_args = [locust_executable, '--slave', '-f', test_path]
    for slave in range(n_clients):
        subprocess.Popen(slave_args, env={'KOLIBRI_BASE_URL': base_url})
        if '127.0.0.1' in base_url or 'localhost' in base_url:
            time.sleep(1)
        else:
            time.sleep(5)
    runners.locust_runner = MasterLocustRunner([classname], options)
    while len(runners.locust_runner.clients.ready) < options.expect_slaves:
        time.sleep(1)
    # spawn client spawning/hatching greenlets:
    runners.locust_runner.start_hatching(options.num_clients, options.hatch_rate)
    main_greenlet = runners.locust_runner.greenlet
    spawn_run_time_limit_greenlet(options)

    if options.csvfilebase:
        gevent.spawn(stats_writer, options.csvfilebase)
    try:
        main_greenlet.join()
        code = 0
        if len(runners.locust_runner.errors):
            code = 1
        shutdown(options, code=code)
    except KeyboardInterrupt:
        shutdown(options, 0)
示例#18
0
    def test_master_marks_downed_drones_as_missing(self):
        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
            master = MasterLocustRunner(MyTestLocust, self.options)
            server.mocked_send(Message("client_ready", None, "fake_client"))
            sleep(0.1)
            # print(master.clients['fake_client'].__dict__)
            assert master.clients['fake_client'].state == STATE_MISSING
示例#19
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
     """
     class MyTestLocust(Locust):
         pass
     
     with mock.patch("locust.rpc.rpc.Server", mocked_rpc_server()) as server:
         master = MasterLocustRunner(MyTestLocust, self.options)
         for i in range(5):
             server.mocked_send(Message("client_ready", None, "fake_client%i" % i))
         
         master.start_hatching(7, 7)
         self.assertEqual(5, len(server.outbox))
         
         num_clients = 0
         for _, msg in server.outbox:
             num_clients += Message.unserialize(msg).data["num_clients"]
         
         self.assertEqual(7, num_clients, "Total number of locusts that would have been spawned is not 7")
示例#20
0
def _run_locust(locust_classes, master):
    config_options(master=master)
    print('*** Starting locust: {}:{} ***'.format(options.web_host,
                                                  options.port))
    if options.master:
        runners.locust_runner = MasterLocustRunner(locust_classes, options)
    else:
        runners.locust_runner = LocalLocustRunner(locust_classes, options)
    logging.info("Starting web monitor at http://%s:%s" %
                 (options.web_host or "0.0.0.0", options.port))
    main_greenlet = gevent.spawn(locust_web.start, locust_classes, options)
    stats_printer_greenlet = None

    def shutdown(code=0):
        """
        Shut down locust by firing quitting event, printing/writing stats and exiting
        """
        logging.info("Shutting down (exit code %s), bye." % code)
        if stats_printer_greenlet is not None:
            stats_printer_greenlet.kill(block=False)
        logging.info("Cleaning up runner...")
        if runners.locust_runner is not None:
            runners.locust_runner.quit()
        logging.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():
        logging.info("Got SIGTERM signal")
        shutdown(0)

    gevent.signal_handler(signal.SIGTERM, sig_term_handler)

    try:
        logging.info("Starting Locust...")
        main_greenlet.join()
        code = 0
        lr = runners.locust_runner
        if len(lr.errors) or len(lr.exceptions) or lr.cpu_log_warning():
            code = options.exit_code_on_error
        shutdown(code=code)
    except KeyboardInterrupt:
        shutdown(0)
示例#21
0
 def test_master_current_response_times(self):
     class MyTestLocust(Locust):
         pass
     
     start_time = 1
     with mock.patch("time.time") as mocked_time:
         mocked_time.return_value = start_time
         global_stats.reset_all()
         with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
             master = MasterLocustRunner(MyTestLocust, self.options)
             mocked_time.return_value += 1.0234
             server.mocked_send(Message("client_ready", None, "fake_client"))
             stats = RequestStats()
             stats.log_request("GET", "/1", 100, 3546)
             stats.log_request("GET", "/1", 800, 56743)
             server.mocked_send(Message("stats", {
                 "stats":stats.serialize_stats(),
                 "stats_total": stats.total.get_stripped_report(),
                 "errors":stats.serialize_errors(),
                 "user_count": 1,
             }, "fake_client"))
             mocked_time.return_value += 1
             stats2 = RequestStats()
             stats2.log_request("GET", "/2", 400, 2201)
             server.mocked_send(Message("stats", {
                 "stats":stats2.serialize_stats(),
                 "stats_total": stats2.total.get_stripped_report(),
                 "errors":stats2.serialize_errors(),
                 "user_count": 2,
             }, "fake_client"))
             mocked_time.return_value += 4
             self.assertEqual(400, master.stats.total.get_current_response_time_percentile(0.5))
             self.assertEqual(800, master.stats.total.get_current_response_time_percentile(0.95))
             
             # let 10 second pass, do some more requests, send it to the master and make
             # sure the current response time percentiles only accounts for these new requests
             mocked_time.return_value += 10.10023
             stats.log_request("GET", "/1", 20, 1)
             stats.log_request("GET", "/1", 30, 1)
             stats.log_request("GET", "/1", 3000, 1)
             server.mocked_send(Message("stats", {
                 "stats":stats.serialize_stats(),
                 "stats_total": stats.total.get_stripped_report(),
                 "errors":stats.serialize_errors(),
                 "user_count": 2,
             }, "fake_client"))
             self.assertEqual(30, master.stats.total.get_current_response_time_percentile(0.5))
             self.assertEqual(3000, master.stats.total.get_current_response_time_percentile(0.95))
示例#22
0
文件: test_web.py 项目: oferg/locust
    def setUp(self):
        super(TestWebUI, self).setUp()

        stats.global_stats.clear_all()
        # parser = parse_options()[0]
        # options = parser.parse_args([])[0]
        runners.main = MasterLocustRunner([], config.locust_config())

        web.request_stats.clear_cache()

        self._web_ui_server = wsgi.WSGIServer(('127.0.0.1', 0),
                                              web.app,
                                              log=None)
        gevent.spawn(lambda: self._web_ui_server.serve_forever())
        gevent.sleep(0.5)
        self.web_port = self._web_ui_server.server_port
示例#23
0
    def test_distributed_integration_run(self):
        """
        Full integration test that starts both a MasterLocustRunner and three WorkerLocustRunner instances 
        and makes sure that their stats is sent to the Master.
        """
        class TestUser(Locust):
            wait_time = constant(0.1)

            @task
            def incr_stats(l):
                l.environment.events.request_success.fire(
                    request_type="GET",
                    name="/",
                    response_time=1337,
                    response_length=666,
                )

        with mock.patch("locust.runners.WORKER_REPORT_INTERVAL", new=0.3):
            # start a Master runner
            master_env = Environment()
            master = MasterLocustRunner(master_env, [TestUser],
                                        master_bind_host="*",
                                        master_bind_port=0)
            sleep(0)
            # start 3 Worker runners
            workers = []
            for i in range(3):
                worker_env = Environment()
                worker = WorkerLocustRunner(worker_env, [TestUser],
                                            master_host="127.0.0.1",
                                            master_port=master.server.port)
                workers.append(worker)

            # give workers time to connect
            sleep(0.1)
            # issue start command that should trigger TestUsers to be spawned in the Workers
            master.start(6, hatch_rate=1000)
            sleep(0.1)
            # check that slave nodes have started locusts
            for worker in workers:
                self.assertEqual(2, worker.user_count)
            # give time for users to generate stats, and stats to be sent to master
            sleep(1)
            master.quit()
            # make sure users are killed
            for worker in workers:
                self.assertEqual(0, worker.user_count)

        # check that stats are present in master
        self.assertGreater(
            master_env.runner.stats.total.num_requests,
            20,
            "For some reason the master node's stats has not come in",
        )
示例#24
0
    def test_sends_hatch_data_to_ready_running_hatching_drones(self):
        '''Sends hatch job to running, ready, or hatching drones'''
        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.Server", mocked_rpc()) as server:
            master = MasterLocustRunner(MyTestLocust, self.options)
            master.clients[1] = DroneNode(1)
            master.clients[2] = DroneNode(2)
            master.clients[3] = DroneNode(3)
            master.clients[1].state = STATE_INIT
            master.clients[2].state = STATE_HATCHING
            master.clients[3].state = STATE_RUNNING
            master.start_hatching(5,5)

            self.assertEqual(3, len(server.outbox))
示例#25
0
    def test_slave_stats_report_median(self):
        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.Server",
                        mocked_rpc_server()) as server:
            master = MasterLocustRunner(MyTestLocust, self.options)
            server.mocked_send(Message("client_ready", None, "fake_client"))

            master.stats.get("/", "GET").log(100, 23455)
            master.stats.get("/", "GET").log(800, 23455)
            master.stats.get("/", "GET").log(700, 23455)

            data = {"user_count": 1}
            events.report_to_master.fire(client_id="fake_client", data=data)
            master.stats.clear_all()

            server.mocked_send(Message("stats", data, "fake_client"))
            s = master.stats.get("/", "GET")
            self.assertEqual(700, s.median_response_time)
示例#26
0
    def test_sends_hatch_data_to_ready_running_hatching_slaves(self):
        '''Sends hatch job to running, ready, or hatching slaves'''
        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.Server", mocked_rpc_server()) as server:
            master = MasterLocustRunner(MyTestLocust, self.options)
            master.clients[1] = SlaveNode(1)
            master.clients[2] = SlaveNode(2)
            master.clients[3] = SlaveNode(3)
            master.clients[1].state = STATE_INIT
            master.clients[2].state = STATE_HATCHING
            master.clients[3].state = STATE_RUNNING
            master.start_hatching(5,5)

            self.assertEqual(3, len(server.outbox))
示例#27
0
class TestMasterRunner(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.master.quit()

    def test_slave_connect(self):
        import mock

        class MyTestLocust(Locust):
            pass

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

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

    def test_automatic_options_propagate(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.MasterServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.master.Process", mocked_process()):
                server.mocked_send(
                    'all', Message("slave_ready", None, "zeh_fake_client1"))
                self.master = MasterLocustRunner(MyTestLocust,
                                                 config.locust_config())
                sleep(0)
                self.assertEqual(1, self.master.slave_count)
                self.assertEqual(1, len(server.outbox_direct))
                msg = Message.unserialize(server.outbox_direct[0][1]).data
                self.assertEqual(self.master.options._config, msg)

    def test_on_demand_options_propagate(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.MasterServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.master.Process", mocked_process()):
                server.mocked_send(
                    'all', Message("slave_ready", None, "zeh_fake_client1"))
                self.master = MasterLocustRunner(MyTestLocust,
                                                 config.locust_config())
                sleep(0)
                self.assertEqual(1, self.master.slave_count)
                server.outbox_all = []
                config_upd = {
                    'host': 'custom_host.com',
                    'master_host': 'new_master_host.com'
                }
                self.master.propagate_config(config_upd)
                self.assertEqual(1, len(server.outbox_all))

                msg = Message.unserialize(server.outbox_all[0]).data
                self.assertEqual(msg['host'], 'custom_host.com')
                self.assertEqual(msg['master_host'], '127.0.0.1')

    def test_slave_stats_report_median(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.MasterServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.master.Process", mocked_process()):
                server.mocked_send('all',
                                   Message("slave_ready", None, "fake_client"))
                self.master = MasterLocustRunner(MyTestLocust,
                                                 config.locust_config())
                sleep(0)

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

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

                server.mocked_send("all", Message("stats", data,
                                                  "fake_client"))
                sleep(0)
                s = self.master.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.MasterServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.master.Process", mocked_process()):
                server.mocked_send(
                    'all', Message("slave_ready", None, "fake_client0"))
                self.master = MasterLocustRunner(MyTestLocust,
                                                 config.locust_config())
                for i in range(1, 5):
                    server.mocked_send(
                        "all", Message("slave_ready", None,
                                       "fake_client%i" % i))
                    sleep(0)

                server.outbox_direct = []
                self.master.start_hatching(7, 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(
                    7, num_clients,
                    "Total number of locusts that would have been spawned is not 7"
                )

    def test_spawn_fewer_locusts_than_slaves(self):
        import mock

        class MyTestLocust(Locust):
            pass

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

                server.outbox_direct = []
                self.master.start_hatching(2, 2)
                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(
                    2, num_clients,
                    "Total number of locusts that would have been spawned is not 2"
                )

    def test_heartbeat(self):
        import mock

        class MyTestLocust(Locust):
            pass

        with mock.patch("locust.rpc.rpc.MasterServer",
                        mocked_rpc_server()) as server:
            with mock.patch("locust.runners.master.Process", mocked_process()):
                server.mocked_send(
                    'all', Message("slave_ready", None, "fake_client0"))
                self.master = MasterLocustRunner(MyTestLocust,
                                                 config.locust_config())

                sleep(0)
                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.master.HEARTBEAT_INTERVAL)
                self.assertEqual(1, self.master.slave_count)
                sleep(runners.master.HEARTBEAT_INTERVAL)
                self.assertEqual(0, self.master.slave_count)
示例#28
0
文件: plugin.py 项目: yannlv/tancust
    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)
示例#29
0
文件: plugin.py 项目: yannlv/tancust
class Plugin(AbstractPlugin, GeneratorPlugin):

    """ Locust tank plugin """
    SECTION = 'locust'


    def __init__(self, core, cfg, cfg_updater):
        AbstractPlugin.__init__(self, core, cfg, cfg_updater)
        self.core = core
        self._locustrunner = None
        self._locustclasses = None
        self._options = None
        self._user_count = 0
        self._state = ''
        self._locuststats = ''
        self._locustslaves = None
        self.stats_reader = None
        self.reader = None
        self.host = None
        self.web_host = ''
        self.port = 8089
        self.locustfile = 'locustfile'
        self.master = False
        self.slave = False
        self.master_host = "127.0.0.1"
        self.master_port = 5557
        self.master_bind_host = "*"
        self.master_bind_port = 5557
        self.expect_slaves = 0
        self.no_web = True
        self.num_clients = int(1)
        self.hatch_rate = float(1)
        self.num_requests = None
        self.run_time = None
        self.loglevel = 'INFO'
        self.logfile = None
        self.csvfilebase = None
        self.csvappend = True
        self.print_stats = True
        self.only_summary = True
        self.list_commands = False
        self.show_task_ratio = False
        self.show_task_ratio_json = False
        self.show_version = True
        self.locustlog_level = 'INFO'
        self.cfg = cfg

        # setup logging
        ll.setup_logging(self.loglevel, self.logfile)

    @property
    def locustlog_file(self):
        logger.debug("######## DEBUG: self.core.artifacts_dir = {}".format(self.core.artifacts_dir))
        return "{}/locust.log".format(self.core.artifacts_dir)


    def get_available_options(self):
        return [
            "host", "port", "locustfile",
            "num_clients", "hatch_rate", "run_time", #"num_requests",
            "logfile", "loglevel", "csvfilebase",
            "master", "master_bind_host", "master_bind_port", "expect_slaves",
            "master_host", "master_port"
        ]


    def _get_variables(self):
        res = {}
        for option in self.core.config.get_options(self.SECTION):
            if option[0] not in self.get_available_options():
                res[option[0]] = option[1]
        logger.debug("Variables: %s", res)
        return res


    def get_reader(self):
        if self.reader is None:
            self.reader = LocustReader(self, self.locustlog_file)
        return self.reader

    def get_stats_reader(self):
        if self.stats_reader is None:
            self.stats_reader = self.reader.stats_reader
            logger.debug("######## DEBUG: plugin.reader.stats_reader.source = %s" % self.stats_reader.source)
            return self.stats_reader

    def configure(self):
        self.host = self.get_option("host")
        self.port = self.get_option("port")
        self.locustfile = self.get_option("locustfile")
        self.num_clients = int(self.get_option ("num_clients"))
        self.hatch_rate = float(self.get_option("hatch_rate"))
        self.run_time = self.get_option("run_time")
        self.logfile = self.get_option("logfile")
        self.loglevel = self.get_option("loglevel")
        self.csvfilebase = self.get_option("csvfilebase")
        self.locustlog_level = self.get_option("locustlog_level")
        self.show_version = True
        self.master = self.get_option("master")
        self.master_bind_host = self.get_option("master_bind_host")
        self.master_bind_port = self.get_option("master_bind_port")
        self.expect_slaves = self.get_option("expect_slaves")
        self.master_host = self.get_option("master_host")
        self.master_port = self.get_option("master_port")


        if self.locustlog_file:
            logger.debug("######## DEBUG: configuring Locust resplog")
            ll.setup_resplogging(self.locustlog_level, self.locustlog_file)

    def get_options(self):
        options = {optname : self.__getattribute__(optname) for optname in self.get_available_options()}
        logger.debug("##### Locust plugin: get_options() : options = {}".format(options))
        return options

    def prepare_test(self):
        logger = logging.getLogger(__name__)


        try:
            logger.debug("######## DEBUG: looking for a console object")
            ### DEBUG: enable/disable Console
            console = self.core.get_plugin_of_type(ConsolePlugin)
        except Exception as ex:
            logger.debug("######## DEBUG: Console not found: %s", ex)
            console = None

        if console:
            logger.debug("######## DEBUG: console found")
            widget = LocustInfoWidget(self)
            console.add_info_widget(widget)
            logger.debug("######## DEBUG: locust widget added to console")


        try:

            locustfile = lm.find_locustfile(self.locustfile)
            if not locustfile:
                logger.error("##### Locust plugin: 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("##### Locust plugin: The locustfile must not be named `locust.py`. Please rename the file and try again.")
                sys.exit(1)

            docstring, locusts = lm.load_locustfile(locustfile)

            logger.info("##### Locust plugin: locustfile = {}".format(locustfile))

            if not locusts:
                logger.error("##### Locust plugin: No Locust class found!")
                sys.exit(1)
            else:
                logger.info("##### Locust plugin: Locust classes found in {} : {}".format(locustfile, locusts))

            self._locustclasses = list(locusts.values())
            options = Opts(**self.get_options())
            self._options = options
            logger.debug("##### Locust plugin: main() : options = {}".format(options))


        except Exception as e:
            logger.error("##### Locust plugin: prepare_test() CRITICAL ERROR : %s", e)
            sys.exit(1)


    def is_any_slave_up(self):
        if self.master and self._locustslaves is not None:
            poll_slaves = [s.poll() for s in self._locustslaves]
            res = any([False if x is not None else True for x in poll_slaves])
            logger.debug("######## DEBUG: is_any_slave_up/any(res) = {}".format(res))
            return res
        elif self.master:
            logger.error("##### Locust plugin: no slave alive to poll")
            return False
        else:
            return False


    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)


    def shutdown(self, code=0):
        """
        Shut down locust by firing quitting event, printing stats and exiting
        """


        logger.debug("######## DEBUG: shutdown()/_locustrunner = {}".format(self._locustrunner))
        logger.info("##### Locust plugin: Cleaning up runner...")
        if self._locustrunner is not None and self.is_any_slave_up():
            #if self.csvfilebase:
            #    write_stat_csvs(self.csvfilebase)
            retcode = self._locustrunner.quit()
            logger.debug("######## DEBUG: shutdown()/_locustrunner.quit() passed # retcode = {}".format(retcode))
        logger.info("##### Locust plugin: Running teardowns...")

        while not self.reader.is_stat_queue_empty():
            logger.info("##### Locust plugin: {} items remaining is stats queue".format(self.reader.stat_queue.qsize()))
            time.sleep(1)

        ### FIXME : possibly causing a greenlet looping infinitely
        #events.quitting.fire(reverse=True)
        print_stats(self._locustrunner.request_stats)
        print_percentile_stats(self._locustrunner.request_stats)
        print_error_report()
        self.reader.close()
        logger.info("##### Locust plugin: Shutting down (exit code %s), bye." % code)

    def is_test_finished(self):
        """
        Fetch locustrunner stats: min/max/median/avg response time, current RPS, fail ratio
        """
        if self._locustrunner:
            self._locuststats = self._locustrunner.stats.total

        """
        Fetch locustrunner status: 'ready', 'hatching', 'running', 'stopped' and returns status code
        """
        logger.debug("######## DEBUG: is_test_finished()? -> Fetching locust status")
        logger.debug("######## DEBUG: is_test_finished() -> self._locustrunner.state = {}".format(self._locustrunner.state))
        logger.debug("######## DEBUG: is_test_finished() -> is_any_slave_up() = {}".format(self.is_any_slave_up()))
        self._state = self._locustrunner.state
        if self._locustrunner.state == 'stopped' or self.master and not self.is_any_slave_up():
            self._user_count = 0
            return 0
        else:
            self._user_count = self._locustrunner.user_count
            return -1

    def end_test(self, retcode):
        if self.is_test_finished() < 0:
            logger.info("##### Locust plugin: Terminating Locust")
            self.shutdown(retcode)
        else:
            logger.info("##### Locust plugin: Locust has been terminated already")
            self.shutdown(retcode)
        return retcode
示例#30
0
 def get_runner(self):
     return MasterLocustRunner(self.environment, [],
                               master_bind_host="*",
                               master_bind_port=5557)
示例#31
0
文件: main.py 项目: kaeawc/locust
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)
示例#32
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)
示例#33
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)