From 7c50e144e2b8a11f532e3e927656a18ee48ff377 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 3 Jun 2020 16:04:57 +0530 Subject: [PATCH 001/329] MMS Performance --- benchmarks/__init__.py | 0 benchmarks/jmx/call_jmx.yaml | 15 ++ benchmarks/jmx/concurrentScaleCalls.jmx.yaml | 57 +++++ benchmarks/jmx/imageInputModelPlan.jmx.yaml | 49 ++++ .../jmx/inference_mms_local_monitoring.yaml | 73 ++++++ benchmarks/jmx/test.yaml | 0 benchmarks/metrics/README.md | 126 +++++++++++ benchmarks/metrics/__init__.py | 0 benchmarks/metrics/metrics_collector.py | 211 ++++++++++++++++++ .../metrics/metrics_monitoring_server.py | 105 +++++++++ .../metrics/metrics_monitoring_test_client.py | 67 ++++++ benchmarks/metrics/mms_taurus_monitor.py | 83 +++++++ benchmarks/perf_server.py | 0 13 files changed, 786 insertions(+) create mode 100644 benchmarks/__init__.py create mode 100644 benchmarks/jmx/call_jmx.yaml create mode 100644 benchmarks/jmx/concurrentScaleCalls.jmx.yaml create mode 100644 benchmarks/jmx/imageInputModelPlan.jmx.yaml create mode 100644 benchmarks/jmx/inference_mms_local_monitoring.yaml create mode 100644 benchmarks/jmx/test.yaml create mode 100644 benchmarks/metrics/README.md create mode 100644 benchmarks/metrics/__init__.py create mode 100755 benchmarks/metrics/metrics_collector.py create mode 100644 benchmarks/metrics/metrics_monitoring_server.py create mode 100644 benchmarks/metrics/metrics_monitoring_test_client.py create mode 100644 benchmarks/metrics/mms_taurus_monitor.py create mode 100644 benchmarks/perf_server.py diff --git a/benchmarks/__init__.py b/benchmarks/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/benchmarks/jmx/call_jmx.yaml b/benchmarks/jmx/call_jmx.yaml new file mode 100644 index 000000000..1f562bccf --- /dev/null +++ b/benchmarks/jmx/call_jmx.yaml @@ -0,0 +1,15 @@ +--- +execution: +- concurrency: 1 + ramp-up: 1s + hold-for: 20s + scenario: Inference +scenarios: + Inference: + script: imageInputModelPlan.jmx +modules: + jmeter: + properties: + input_filepath : kitten.jpg + model_name : squeezenet + diff --git a/benchmarks/jmx/concurrentScaleCalls.jmx.yaml b/benchmarks/jmx/concurrentScaleCalls.jmx.yaml new file mode 100644 index 000000000..5a6fa9d90 --- /dev/null +++ b/benchmarks/jmx/concurrentScaleCalls.jmx.yaml @@ -0,0 +1,57 @@ +--- +execution: +- concurrency: 10 + iterations: 3 + ramp-up: 5s + scenario: Concurrent Scale Up Model/Scale Down Model Requests +scenarios: + Concurrent Scale Up Model/Scale Down Model Requests: + requests: + - follow-redirects: true + label: Scale Up Model + method: PUT + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(management_port,8081)}/models/${model}?min_worker=${scale_up_workers} + - follow-redirects: true + label: Scale Down Model + method: PUT + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(management_port,8081)}/models/${model}?min_worker=${scale_down_workers} + store-cache: false + store-cookie: false + use-dns-cache-mgr: false + variables: + count: ${__P(count,1)} + model: ${__P(model_name,squeezenet)} + scale_down_workers: ${__P(scale_down_workers,2)} + scale_up_workers: ${__P(scale_up_workers,8)} + +services: + - module: monitoring + server-agent: + - address: localhost:9009 + label: mms-server # if you specify label, it will be used in reports instead of ip:port + interval: 1s # polling interval + logging: True # those logs will be saved to "SAlogs_192.168.0.1_4444.csv" in the artifacts dir + metrics: + - sum_cpu_percent + - sum_memory_percent + - sum_num_handles + - mms_workers + + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: mms-server/mms_workers + condition: '>' + threshold: 8 + timeframe: 5s + fail: true + stop: true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: mms-server/mms_workers + condition: '<' + threshold: 2 + timeframe: 5s + fail: true + stop: true \ No newline at end of file diff --git a/benchmarks/jmx/imageInputModelPlan.jmx.yaml b/benchmarks/jmx/imageInputModelPlan.jmx.yaml new file mode 100644 index 000000000..9fff35181 --- /dev/null +++ b/benchmarks/jmx/imageInputModelPlan.jmx.yaml @@ -0,0 +1,49 @@ +--- +execution: +- concurrency: 4 + ramp-up: 1s + hold-for: 20s + scenario: Inference +scenarios: + Inference: + requests: + - follow-redirects: true + label: Inference Request + method: POST + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} + store-cache: false + store-cookie: false + use-dns-cache-mgr: false + variables: + model: ${__P(model_name,squeezenet_v1.1)} + +modules: + jmeter: + properties: + input_filepath : kitten.jpg + model_name : squeezenet + +services: + - module: monitoring + server-agent: + - address: localhost:9009 + label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port + interval: 1s # polling interval + logging: True # those logs will be saved to "SAlogs_192.168.0.1_4444.csv" in the artifacts dir + metrics: + - sum_cpu_percent + - sum_memory_percent + - sum_num_handles + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: mms-inference-server/sum_num_handles + condition: '>' + threshold: 180 + timeframe: 1s + fail: true + stop: true + + diff --git a/benchmarks/jmx/inference_mms_local_monitoring.yaml b/benchmarks/jmx/inference_mms_local_monitoring.yaml new file mode 100644 index 000000000..fb4afe9ec --- /dev/null +++ b/benchmarks/jmx/inference_mms_local_monitoring.yaml @@ -0,0 +1,73 @@ +--- +execution: +- concurrency: 1 + ramp-up: 5s + hold-for: 1m + scenario: Inference +scenarios: + Inference: + default-address: ${__P(protocol,https)}://${__P(hostname,127.0.0.1)}:${__P(port,8443)}/ + requests: + - follow-redirects: true + label: Inference Request + method: POST + upload-files: + - mime-type: image/jpeg + param: data + path: ${__P(input_filepath)} + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} + store-cache: false + store-cookie: false + use-dns-cache-mgr: false + variables: + model: ${__P(model_name,squeezenet_v1.1)} + +modules: + jmeter: + properties: + input_filepath : kitten.jpg + model_name : squeezenet + mms_monitoring: + class : mms_monitoring.Monitor + +#services: +# - module: monitoring +# server-agent: +# - address: localhost:9009 +# label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port +# interval: 1s # polling interval +# logging: True # those logs will be saved to "SAlogs_192.168.0.1_4444.csv" in the artifacts dir +# metrics: +# - sum_cpu_percent +# - sum_mem_percent +# - sum_num_handles +# +#reporting: +#- module: passfail +# criteria: +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: mms-inference-server/sum_mem_percent +# condition: '>' +# threshold: 4 +# timeframe: 5s +# fail: true +# stop: true + + +services: + - module: mms_monitoring + MMS_local: + - interval: 1s + metrics: + - cpu + - disk-space + - mem +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: MMS_local/cpu + condition: '>' + threshold: 30 + timeframe: 1s + diff --git a/benchmarks/jmx/test.yaml b/benchmarks/jmx/test.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/benchmarks/metrics/README.md b/benchmarks/metrics/README.md new file mode 100644 index 000000000..5dd5319da --- /dev/null +++ b/benchmarks/metrics/README.md @@ -0,0 +1,126 @@ +# Multi Model Server Metrics Monitoring Suite + +The Metrics Monitoring Suite monitors MMS, System related metrics. +We are using Taurus as a test frameworks which sits on top of popular frameworks +such as Jmeter, Locust.io, Newman etc. + +## + +## Installation + +### Ubuntu + +The script is mainly intended to run on a Ubuntu EC2 instance. For this reason, we have provided an `install_dependencies.sh` script to install everything needed to execute the benchmark on this environment. All you need to do is run this file and clone the MMS repo. + +### MacOS + +For mac, you should have python3 and java installed. If you wish to run the default benchmarks featuring a docker-based instance of MMS, you will need to install docker as well. Finally, you will need to install jmeter with plugins which can be accomplished by running `mac_install_dependencies.sh`. + +### Other + +For other environments, manual installation is necessary. The list of dependencies to be installed can be found below or by reading the ubuntu installation script. + +The benchmarking script requires the following to run: +- python3 +- A JDK and JRE +- jmeter installed through homebrew or linuxbrew with the plugin manager and the following plugins: jpgc-synthesis=2.1,jpgc-filterresults=2.1,jpgc-mergeresults=2.1,jpgc-cmd=2.1,jpgc-perfmon=2.1 +- Docker-ce with the current user added to the docker group +- Nvidia-docker (for GPU) + + +## Models + +The pre-loaded models for the benchmark can be mostly found in the [MMS model zoo](https://github.com/awslabs/multi-model-server/blob/master/docs/model_zoo.md). We currently support the following: +- [resnet: ResNet-18 (Default)](https://github.com/awslabs/multi-model-server/blob/master/docs/model_zoo.md#resnet-18) +- [squeezenet: SqueezeNet V1.1](https://github.com/awslabs/multi-model-server/blob/master/docs/model_zoo.md#squeezenet_v1.1) +- [lstm: lstm-ptb](https://github.com/awslabs/multi-model-server/blob/master/docs/model_zoo.md#lstm-ptb) +- [noop: noop-v1.0](https://s3.amazonaws.com/model-server/models/noop/noop-v1.0.model) Simple Noop model which returns "Hello world" to any input specified. +- [noop_echo: noop_echo-v1.0](https://s3.amazonaws.com/model-server/models/noop/noop_echo-v1.0.model) Simple Noop model which returns whatever input is given to it. + +## Benchmarks + +We support several basic benchmarks: +- throughput: Run inference with enough threads to occupy all workers and ensure full saturation of resources to find the throughput. The number of threads defaults to 100. +- latency: Run inference with a single thread to determine the latency +- ping: Test the throughput of pinging against the frontend +- load: Loads the same model many times in parallel. The number of loads is given by the "count" option and defaults to 16. +- repeated_scale_calls: Will scale the model up to "scale_up_workers"=16 then down to "scale_down_workers"=1 then up and down repeatedly. +- multiple_models: Loads and scales up three models (1. noop, 2. lstm, and 3. resnet), at the same time, runs inferences on them, and then scales them down. Use the options "urlN", "modelN_name", "dataN" to specify the model url, model name, and the data to pass to the model respectively. data1 and data2 are of the format "'Some garbage data being passed here'" and data3 is the filesystem path to a file to upload. + +We also support compound benchmarks: +- concurrent_inference: Runs the basic benchmark with different numbers of threads + + +## Examples + +Run basic latency test on default resnet-18 model\ +```./benchmark.py latency``` + + +Run basic throughput test on default resnet-18 model.\ +```./benchmark.py throughput``` + + +Run all benchmarks\ +```./benchmark.py --all``` + + +Run using the noop-v1.0 model\ +```./benchmark.py latency -m noop_v1.0``` + + +Run on GPU (4 gpus)\ +```./benchmark.py latency -g 4``` + + +Run with a custom image\ +```./benchmark.py latency -i {imageFilePath}``` + + +Run with a custom model (works only for CNN based models, which accept image as an input for now. We will add support for more input types in future to this command. )\ +```./benchmark.py latency -c {modelUrl} -i {imageFilePath}``` + + +Run with custom options\ +```./benchmark.py repeated_scale_calls --options scale_up_workers 100 scale_down_workers 10``` + + +Run against an already running instance of MMS\ +```./benchmark.py latency --mms 127.0.0.1``` (defaults to http, port 80, management port = port + 1)\ +```./benchmark.py latency --mms 127.0.0.1:8080 --management-port 8081```\ +```./benchmark.py latency --mms https://127.0.0.1:8443``` + + +Run verbose with only a single loop\ +```./benchmark.py latency -v -l 1``` + + +## Benchmark options + +The full list of options can be found by running with the -h or --help flags. + + +## Profiling + +### Frontend + +The benchmarks can be used in conjunction with standard profiling tools such as JProfiler to analyze the system performance. JProfiler can be downloaded from their [website](https://www.ej-technologies.com/products/jprofiler/overview.html). Once downloaded, open up JProfiler and follow these steps: + +1. Run MMS directly through gradle (do not use docker). This can be done either on your machine or on a remote machine accessible through SSH. +2. In JProfiler, select "Attach" from the ribbon and attach to the ModelServer. The process name in the attach window should be "com.amazonaws.ml.mms.ModelServer". If it is on a remote machine, select "On another computer" in the attach window and enter the SSH details. For the session startup settings, you can leave it with the defaults. At this point, you should see live CPU and Memory Usage data on JProfiler's Telemetries section. +3. Select Start Recordings in JProfiler's ribbon +4. Run the Benchmark script targeting your running MMS instance. It might run something like `./benchmark.py throughput --mms https://127.0.0.1:8443`. It can be run on either your local machine or a remote machine (if you are running remote), but we recommend running the benchmark on the same machine as the model server to avoid confounding network latencies. +5. Once the benchmark script has finished running, select Stop Recordings in JProfiler's ribbon + +Once you have stopped recording, you should be able to analyze the data. One useful section to examine is CPU views > Call Tree and CPU views > Hot Spots to see where the processor time is going. + +### Backend + +The benchmarks can also be used to analyze the backend performance using cProfile. It does not require any additional packages to run the benchmark, but viewing the logs does require an additional package. Run `pip install snakeviz` to install this. To run the python profiling, follow these steps: + +1. In the file `mms/model_service_worker.py`, set the constant BENCHMARK to true at the top to enable benchmarking. +2. Run the benchmark and MMS. They can either be done automatically inside the docker container or separately with the "--mms" flag. +3. Run MMS directly through gradle (do not use docker). This can be done either on your machine or on a remote machine accessible through SSH. +4. Run the Benchmark script targeting your running MMS instance. It might run something like `./benchmark.py throughput --mms https://127.0.0.1:8443`. It can be run on either your local machine or a remote machine (if you are running remote), but we recommend running the benchmark on the same machine as the model server to avoid confounding network latencies. +5. Run `snakeviz /tmp/mmsPythonProfile.prof` to view the profiling data. It should start up a web server on your machine and automatically open the page. +6. Don't forget to set BENCHMARK = False in the model_service_worker.py file after you are finished. diff --git a/benchmarks/metrics/__init__.py b/benchmarks/metrics/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/benchmarks/metrics/metrics_collector.py b/benchmarks/metrics/metrics_collector.py new file mode 100755 index 000000000..e0891c20e --- /dev/null +++ b/benchmarks/metrics/metrics_collector.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 + +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +MMS server metrics collector +""" +# pylint: disable=redefined-builtin + +import os +import sys +import time +import logging +import psutil +import gevent +import argparse + +TMP_DIR = "/var/folders/04/6_v1bbs55mb_hrpkphh46xcc0000gn/T" +METRICS_LOG_FILE = "{}/mms_metrics_{}.log".format(TMP_DIR, int(time.time())) +MMS_PID_FILE = "{}/.model_server.pid".format(TMP_DIR) +METRICS_COLLECTOR_PID_FILE = "{}/.metrics_collector.pid".format(TMP_DIR) +MONITOR_INTERVAL = 1 +PID_LIST_INTERVAL = 2 +AVAILABLE_METRICS = ["sum_cpu_percent", + "sum_memory_percent", + "sum_num_handles", + "mms_workers"] + +child_processes = set() +mms_process = None + + +def find_procs_by_name(name): + """Return a list of processes matching 'name'.""" + ls = [] + for p in psutil.process_iter(["name", "exe", "cmdline"]): + if name == p.info['name'] or \ + p.info['exe'] and os.path.basename(p.info['exe']) == name or \ + p.info['cmdline'] and p.info['cmdline'][0] == name: + ls.append(p) + + if len(ls) > 1: + raise Exception("Multiple processes found with name {}.".format(name)) + + return ls[0] + + +def get_process_pid_from_file(file_path): + """Get the process pid from pid file. + """ + if os.path.isfile(file_path): + with open(file_path, "r") as f: + pid = int(f.readline()) + else: + raise Exception("Invalid file {}".format(file_path)) + + return pid + + +def store_metrics_collector_pid(): + metrics_collector_process = psutil.Process(METRICS_COLLECTOR_PID_FILE) + pid_file = os.path.join() + with open(pid_file, "w") as pf: + pf.write(str(metrics_collector_process.pid)) + + +def stop_metrics_collector_process(): + """This will stop already running metrics collector process. + Note at a time only one pid file will be available. + """ + + pid = get_process_pid_from_file(METRICS_COLLECTOR_PID_FILE) + if pid: + try: + process = psutil.Process(pid) + if process.is_running(): + logging.info("Process with pid {} is running. Killing it.".format(process.pid)) + process.kill() + except Exception as e: + pass + else: + logging.info("Dead process with pid {} found in '{}'.".format(process.pid, METRICS_COLLECTOR_PID_FILE)) + + logging.info("Removing pid file '{}'.".format(METRICS_COLLECTOR_PID_FILE)) + os.remove(METRICS_COLLECTOR_PID_FILE) + + +def get_child_processes(process): + """Get all running child processes recursively""" + child_processes = [] + for p in process.children(recursive=True): + if p.status() == 'running': + child_processes.append(p) + return child_processes + + +def get_metrics(): + """ Get MMS processes specific metrics + """ + + # TODO - make this modular may be a diff function for each metric + # TODO - allow users to add new metrics easliy + # TODO - make sure available metric list is maintained + + sum_cpu_percent = 0 + sum_memory_percent = 0 + sum_num_handles = 0 + mms_workers = 0 + metrics = {} + for process in [mms_process] + list(get_child_processes(mms_process)): + try: + process.cpu_percent() # to warm-up + except: + pass + else: + cpu_percent = process.cpu_percent() + memory_percent = process.memory_percent() + sum_cpu_percent += cpu_percent + sum_memory_percent += memory_percent + sum_num_handles += process.num_fds() + mms_workers += 1 + + metrics["sum_cpu_percent"] = sum_cpu_percent + metrics["sum_memory_percent"] = sum_memory_percent + metrics["sum_num_handles"] = sum_num_handles + metrics["mms_workers"] = mms_workers + + + return metrics + + +def monitor_processes(socket, interval, metrics): + while True: + message = [] + collected_metrics = get_metrics() + for metric in metrics: + message.append(str(collected_metrics.get(metric, 0))) + message = "\t".join(message)+"\n" + + if socket: + socket.send(message.encode("latin-1")) + gevent.sleep(interval) + + +def get_mms_processes(): + """ It caches the MMS and child processes at module level. + Ensure that you call this process so that MMS process + """ + + global mms_process + global child_processes + mms_pid = get_process_pid_from_file() + if not mms_pid: + print("MMS process not found11.") + exit() + + try: + mms_process = psutil.Process(mms_pid) + except Exception as e: + print("MMS process not found. Error: {}".format(str(e))) + raise + + child_processes = set(get_child_processes(mms_process)) + + +def start_metric_collector_process(): + metric_collector_pid = get_process_pid_from_file(METRICS_COLLECTOR_PID_FILE) + if metric_collector_pid: + try: + perf_mon_process = psutil.Process(metric_collector_pid) + except Exception as e: + stop_metrics_collector_process() + else: + if perf_mon_process.is_running(): + raise Exception("Performance monitoring script already running. " + "Stop it using stop option.") + store_metrics_collector_pid() + + +def start_metric_collection(socket, interval, metrics): + bad_metrics = set(metrics) - set(AVAILABLE_METRICS) + if bad_metrics: + raise Exception("Metrics not available for monitoring {}.".format(bad_metrics)) + + get_mms_processes() + print("Started metric collection for MMS processes.....") + thread2 = gevent.spawn(monitor_processes, socket, interval, metrics) + gevent.joinall([thread2]) + + +if __name__ == "__main__": + logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + parser = argparse.ArgumentParser(prog='perf-mon-script', description='System Performance Monitoring') + sub_parse = parser.add_mutually_exclusive_group(required=True) + sub_parse.add_argument('--start', action='store_true', help='Start the perf-mon-script') + sub_parse.add_argument('--stop', action='store_true', help='Stop the perf-mon-script') + + args = parser.parse_args() + + if args.start: + start_perf_mon() + elif args.stop: + stop_perf_mon_process() diff --git a/benchmarks/metrics/metrics_monitoring_server.py b/benchmarks/metrics/metrics_monitoring_server.py new file mode 100644 index 000000000..9f9dce5c6 --- /dev/null +++ b/benchmarks/metrics/metrics_monitoring_server.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 + +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +MMS server monitoring script over the socket +""" +# pylint: disable=redefined-builtin + +import logging +import sys +from gevent import socket +from gevent import select +from gevent import monkey +monkey.patch_select() +monkey.patch_socket() + +from perf_monitor import start_perf_mon + +HOST = '' +SOCKET_LIST = [] +RECV_BUFFER = 4096 +PORT = 9009 +interval = 1 + + +def perf_server(): + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server_socket.bind((HOST, PORT)) + server_socket.listen(10) + + SOCKET_LIST.append(server_socket) + + while True: + ready_to_read, ready_to_write, in_error = select.select(SOCKET_LIST, [], [], 0) + + for sock in ready_to_read: + # a new connection request recieved + if sock == server_socket: + sockfd, addr = server_socket.accept() + SOCKET_LIST.append(sockfd) + logging.log("client ({}, {}) connected".format(addr[0], addr[1])) + + # a message from a client, not a new connection + else: + try: + # receiving data from the socket. + data = sock.recv(RECV_BUFFER).decode() + if data: + if data == 'test\n': + send_message(sock, "Yep\n") + elif data == 'exit\n': + close_socket(sock) + elif data.startswith('interval'): + try: + global interval + interval = int(data.split(":")[1][:-1]) + except Exception: + send_message(sock, "In-correct interval data") + elif data.startswith('metrics'): + metrics = data[:-1].split("metrics:")[1].split("\t") + start_perf_mon(sock, interval, metrics) + else: + # TODO - decide what to do here + pass + + else: + # remove the socket that's broken + if sock in SOCKET_LIST: + SOCKET_LIST.remove(sock) + except Exception as e: + logging.warning("Error {}".format(str(e))) + continue + + server_socket.close() + + +def send_message(socket, message): + try: + socket.send(message.encode("latin-1")) + except Exception as e: + logging.warning("Error while sending the message {}. Closing the socket.".format(str(e))) + close_socket(socket) + + +def close_socket(socket): + socket.close() + if socket in SOCKET_LIST: + SOCKET_LIST.remove(socket) + + +if __name__ == "__main__": + logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + sys.exit(perf_server()) + + diff --git a/benchmarks/metrics/metrics_monitoring_test_client.py b/benchmarks/metrics/metrics_monitoring_test_client.py new file mode 100644 index 000000000..82fe5da7c --- /dev/null +++ b/benchmarks/metrics/metrics_monitoring_test_client.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Test client for MMS Metrics Monitoring Server +""" + +import sys, socket, select + + +def client(): + host = sys.argv[1] + port = int(sys.argv[2]) + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(2) + try: + s.connect((host, port)) + except: + print('Unable to connect') + sys.exit() + + print('Connected to remote host. You can start sending messages') + sys.stdout.write('[Me] '); + sys.stdout.flush() + + while 1: + socket_list = [sys.stdin, s] + + # Get the list sockets which are readable + read_sockets, write_sockets, error_sockets = select.select(socket_list, [], []) + + for sock in read_sockets: + if sock == s: + # incoming message from remote server, s + data = sock.recv(4096).decode() + if not data: + print('\nDisconnected from the server') + sys.exit() + else: + # print data + sys.stdout.write(data) + sys.stdout.write('[Me] '); + sys.stdout.flush() + + else: + # user entered a message + msg = sys.stdin.readline() + s.send(msg.encode("latin-1")) + sys.stdout.write('[Me] '); + sys.stdout.flush() + + +if __name__ == "__main__": + if (len(sys.argv) < 3): + print('Usage : python chat_client.py hostname port') + sys.exit() + + sys.exit(client()) diff --git a/benchmarks/metrics/mms_taurus_monitor.py b/benchmarks/metrics/mms_taurus_monitor.py new file mode 100644 index 000000000..0bcfc1daa --- /dev/null +++ b/benchmarks/metrics/mms_taurus_monitor.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Taurus local plugin for MMS monitoring. +This file should be placed in Python Path along with metrics_collector.py file +""" +# pylint: disable=redefined-builtin + +import csv +from bzt.modules import monitoring +from bzt import TaurusConfigError +from bzt.utils import dehumanize_time +from bzt.six import PY3 + +from metrics_collector import get_metrics, get_mms_processes + + +class Monitor(monitoring.Monitoring): + def __init__(self): + super(Monitor, self).__init__() + + self.client_classes.update({'MMS_local': MMSLocalClient}) + + +class MMSLocalClient(monitoring.LocalClient): + AVAILABLE_METRICS = monitoring.LocalClient.AVAILABLE_METRICS + ["sum_cpu_percent", "sum_memory_percent", + "sum_num_handles", "mms_workers"] + + def __init__(self, parent_log, label, config, engine=None): + + super(MMSLocalClient, self).__init__(parent_log, label, config, engine=engine) + self.label = 'MMSLocalClient' + + def connect(self): + exc = TaurusConfigError('Metric is required in Local monitoring client') + metric_names = self.config.get('metrics', exc) + + bad_list = set(metric_names) - set(self.AVAILABLE_METRICS) + if bad_list: + self.log.warning('Wrong metrics found: %s', bad_list) + + good_list = set(metric_names) & set(self.AVAILABLE_METRICS) + if not good_list: + raise exc + + self.metrics = list(set(good_list)) + + self.monitor = MMSLocalMonitor(self.log, self.metrics, self.engine) + self.interval = dehumanize_time(self.config.get("interval", self.engine.check_interval)) + + if self.config.get("logging", False): + if not PY3: + self.log.warning("Logging option doesn't work on python2.") + else: + self.logs_file = self.engine.create_artifact("local_monitoring_logs", ".csv") + with open(self.logs_file, "a", newline='') as mon_logs: + logs_writer = csv.writer(mon_logs, delimiter=',') + metrics = ['ts'] + sorted([metric for metric in good_list]) + logs_writer.writerow(metrics) + + +class MMSLocalMonitor(monitoring.LocalMonitor): + + def _calc_resource_stats(self, interval): + result =super()._calc_resource_stats(interval) + get_mms_processes() + result.update(get_metrics()) + return result + + + + + diff --git a/benchmarks/perf_server.py b/benchmarks/perf_server.py new file mode 100644 index 000000000..e69de29bb From 74ae3e5b0a51a62fec698209726480dc2326612a Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 3 Jun 2020 19:38:40 +0530 Subject: [PATCH 002/329] monitoring read me --- benchmarks/metrics/README.md | 126 ------------------ benchmarks/monitoring/README.md | 93 +++++++++++++ .../{metrics => monitoring}/__init__.py | 0 benchmarks/monitoring/metrics/__init__.py | 0 .../metrics_collector.py | 0 .../metrics_monitoring_server.py | 0 .../metrics_monitoring_taurus.py} | 7 +- .../metrics_monitoring_test_client.py | 0 benchmarks/monitoring/process.py | 0 9 files changed, 96 insertions(+), 130 deletions(-) delete mode 100644 benchmarks/metrics/README.md create mode 100644 benchmarks/monitoring/README.md rename benchmarks/{metrics => monitoring}/__init__.py (100%) create mode 100644 benchmarks/monitoring/metrics/__init__.py rename benchmarks/{metrics => monitoring}/metrics_collector.py (100%) rename benchmarks/{metrics => monitoring}/metrics_monitoring_server.py (100%) rename benchmarks/{metrics/mms_taurus_monitor.py => monitoring/metrics_monitoring_taurus.py} (93%) rename benchmarks/{metrics => monitoring}/metrics_monitoring_test_client.py (100%) create mode 100644 benchmarks/monitoring/process.py diff --git a/benchmarks/metrics/README.md b/benchmarks/metrics/README.md deleted file mode 100644 index 5dd5319da..000000000 --- a/benchmarks/metrics/README.md +++ /dev/null @@ -1,126 +0,0 @@ -# Multi Model Server Metrics Monitoring Suite - -The Metrics Monitoring Suite monitors MMS, System related metrics. -We are using Taurus as a test frameworks which sits on top of popular frameworks -such as Jmeter, Locust.io, Newman etc. - -## - -## Installation - -### Ubuntu - -The script is mainly intended to run on a Ubuntu EC2 instance. For this reason, we have provided an `install_dependencies.sh` script to install everything needed to execute the benchmark on this environment. All you need to do is run this file and clone the MMS repo. - -### MacOS - -For mac, you should have python3 and java installed. If you wish to run the default benchmarks featuring a docker-based instance of MMS, you will need to install docker as well. Finally, you will need to install jmeter with plugins which can be accomplished by running `mac_install_dependencies.sh`. - -### Other - -For other environments, manual installation is necessary. The list of dependencies to be installed can be found below or by reading the ubuntu installation script. - -The benchmarking script requires the following to run: -- python3 -- A JDK and JRE -- jmeter installed through homebrew or linuxbrew with the plugin manager and the following plugins: jpgc-synthesis=2.1,jpgc-filterresults=2.1,jpgc-mergeresults=2.1,jpgc-cmd=2.1,jpgc-perfmon=2.1 -- Docker-ce with the current user added to the docker group -- Nvidia-docker (for GPU) - - -## Models - -The pre-loaded models for the benchmark can be mostly found in the [MMS model zoo](https://github.com/awslabs/multi-model-server/blob/master/docs/model_zoo.md). We currently support the following: -- [resnet: ResNet-18 (Default)](https://github.com/awslabs/multi-model-server/blob/master/docs/model_zoo.md#resnet-18) -- [squeezenet: SqueezeNet V1.1](https://github.com/awslabs/multi-model-server/blob/master/docs/model_zoo.md#squeezenet_v1.1) -- [lstm: lstm-ptb](https://github.com/awslabs/multi-model-server/blob/master/docs/model_zoo.md#lstm-ptb) -- [noop: noop-v1.0](https://s3.amazonaws.com/model-server/models/noop/noop-v1.0.model) Simple Noop model which returns "Hello world" to any input specified. -- [noop_echo: noop_echo-v1.0](https://s3.amazonaws.com/model-server/models/noop/noop_echo-v1.0.model) Simple Noop model which returns whatever input is given to it. - -## Benchmarks - -We support several basic benchmarks: -- throughput: Run inference with enough threads to occupy all workers and ensure full saturation of resources to find the throughput. The number of threads defaults to 100. -- latency: Run inference with a single thread to determine the latency -- ping: Test the throughput of pinging against the frontend -- load: Loads the same model many times in parallel. The number of loads is given by the "count" option and defaults to 16. -- repeated_scale_calls: Will scale the model up to "scale_up_workers"=16 then down to "scale_down_workers"=1 then up and down repeatedly. -- multiple_models: Loads and scales up three models (1. noop, 2. lstm, and 3. resnet), at the same time, runs inferences on them, and then scales them down. Use the options "urlN", "modelN_name", "dataN" to specify the model url, model name, and the data to pass to the model respectively. data1 and data2 are of the format "'Some garbage data being passed here'" and data3 is the filesystem path to a file to upload. - -We also support compound benchmarks: -- concurrent_inference: Runs the basic benchmark with different numbers of threads - - -## Examples - -Run basic latency test on default resnet-18 model\ -```./benchmark.py latency``` - - -Run basic throughput test on default resnet-18 model.\ -```./benchmark.py throughput``` - - -Run all benchmarks\ -```./benchmark.py --all``` - - -Run using the noop-v1.0 model\ -```./benchmark.py latency -m noop_v1.0``` - - -Run on GPU (4 gpus)\ -```./benchmark.py latency -g 4``` - - -Run with a custom image\ -```./benchmark.py latency -i {imageFilePath}``` - - -Run with a custom model (works only for CNN based models, which accept image as an input for now. We will add support for more input types in future to this command. )\ -```./benchmark.py latency -c {modelUrl} -i {imageFilePath}``` - - -Run with custom options\ -```./benchmark.py repeated_scale_calls --options scale_up_workers 100 scale_down_workers 10``` - - -Run against an already running instance of MMS\ -```./benchmark.py latency --mms 127.0.0.1``` (defaults to http, port 80, management port = port + 1)\ -```./benchmark.py latency --mms 127.0.0.1:8080 --management-port 8081```\ -```./benchmark.py latency --mms https://127.0.0.1:8443``` - - -Run verbose with only a single loop\ -```./benchmark.py latency -v -l 1``` - - -## Benchmark options - -The full list of options can be found by running with the -h or --help flags. - - -## Profiling - -### Frontend - -The benchmarks can be used in conjunction with standard profiling tools such as JProfiler to analyze the system performance. JProfiler can be downloaded from their [website](https://www.ej-technologies.com/products/jprofiler/overview.html). Once downloaded, open up JProfiler and follow these steps: - -1. Run MMS directly through gradle (do not use docker). This can be done either on your machine or on a remote machine accessible through SSH. -2. In JProfiler, select "Attach" from the ribbon and attach to the ModelServer. The process name in the attach window should be "com.amazonaws.ml.mms.ModelServer". If it is on a remote machine, select "On another computer" in the attach window and enter the SSH details. For the session startup settings, you can leave it with the defaults. At this point, you should see live CPU and Memory Usage data on JProfiler's Telemetries section. -3. Select Start Recordings in JProfiler's ribbon -4. Run the Benchmark script targeting your running MMS instance. It might run something like `./benchmark.py throughput --mms https://127.0.0.1:8443`. It can be run on either your local machine or a remote machine (if you are running remote), but we recommend running the benchmark on the same machine as the model server to avoid confounding network latencies. -5. Once the benchmark script has finished running, select Stop Recordings in JProfiler's ribbon - -Once you have stopped recording, you should be able to analyze the data. One useful section to examine is CPU views > Call Tree and CPU views > Hot Spots to see where the processor time is going. - -### Backend - -The benchmarks can also be used to analyze the backend performance using cProfile. It does not require any additional packages to run the benchmark, but viewing the logs does require an additional package. Run `pip install snakeviz` to install this. To run the python profiling, follow these steps: - -1. In the file `mms/model_service_worker.py`, set the constant BENCHMARK to true at the top to enable benchmarking. -2. Run the benchmark and MMS. They can either be done automatically inside the docker container or separately with the "--mms" flag. -3. Run MMS directly through gradle (do not use docker). This can be done either on your machine or on a remote machine accessible through SSH. -4. Run the Benchmark script targeting your running MMS instance. It might run something like `./benchmark.py throughput --mms https://127.0.0.1:8443`. It can be run on either your local machine or a remote machine (if you are running remote), but we recommend running the benchmark on the same machine as the model server to avoid confounding network latencies. -5. Run `snakeviz /tmp/mmsPythonProfile.prof` to view the profiling data. It should start up a web server on your machine and automatically open the page. -6. Don't forget to set BENCHMARK = False in the model_service_worker.py file after you are finished. diff --git a/benchmarks/monitoring/README.md b/benchmarks/monitoring/README.md new file mode 100644 index 000000000..c8ed1a6c5 --- /dev/null +++ b/benchmarks/monitoring/README.md @@ -0,0 +1,93 @@ +# Metrics Monitoring Suite + +The Metrics Monitoring suite helps in monitoring the process, sub-process and system wide metrics while performance test +cases are running. It allows to specify the pass/fail criteria for metrics in the test case. +We use Taurus test automation framework to run the test cases and metrics monitoring. + + +## Taurus + +[Taurus](https://gettaurus.org) is Apache 2.0 Licenced, test automation framework which extends and abstracts +different popular load testing frameworks such as JMeter, Locust etc. Taurus simplifies creating, running and analyzing the +performance tests. + +## Installation +Refer the Taurus installation guide [here](https://gettaurus.org/install/Installation/). + + +## How to run tests +To run a metrics monitoring test case, you need to specify below in Taurus test case yaml. +1. Load test case scenario +2. Metrics to monitor +3. Pass/fail criteria + +#### 1. Load test case scenario +You can specify the test scenarios, in the scenario section of the yaml. + +There are multiple ways you can specify your test scenario. +1. Use existing Jmeter/locust test script +Taurus provides support for different executors such as JMeter so you can use test script written in those frameworks as it is. +Details about executor types are provided [here](https://gettaurus.org/docs/ExecutionSettings/). +Details about how to run an existing JMeter script are provided [here](https://gettaurus.org/docs/JMeter/). + +To get you started quickly, we have provided a sample JMeter Script and a Taurus yaml file here and here. +Use shell command below to run the test case: +``` +bzt call_jmx.yaml +``` + +Here is how the Taurus yaml looks like. +```yaml code + +``` + +2. Write a Taurus script +You can also write a Taurus script for a specific executor type. +To get you started quickly, we have provided a Taurus script with JMeter executor. + +``` +bzt inference_test.yaml +``` +```yaml code + +``` + + +#### 2. Metrics to monitor +You can specify the different metrics to monitor in services/monitoring section of the yaml. + +Metrics can be monitored in two ways: +1. Standalone monitoring server +If your server is hosted on different machine, you will be using this method. Before running the test case +you have to start a metric_monitoring_server.py script. It will be communicating with Taurus test client over sockets. +The address and port of the monitoring script should be specified in test case yaml. + +To start monitoring script on server use command below: +``` +sudo python metric_monitoring_server.py +``` + +Sample test yaml: +```yaml code + +``` + + +2. Taurus local monitoring plugin +If your test client is running on the server itself, you may want to use this method. +We have provided a custom Taurus plugin as metrics_monitoring_taurus.py. Make sure that the benchmarks/monitoring folder +is in PYTHON_PATH. You need to specify the monitoring class. + +Sample test yaml: +```yaml code + +``` + +#### 3. Pass/Fail criteria +You can specify the pass/fail criteria for the test cases. +Read more about it [here](https://gettaurus.org/docs/PassFail/) + +Sample test yaml: +```yaml code + +``` diff --git a/benchmarks/metrics/__init__.py b/benchmarks/monitoring/__init__.py similarity index 100% rename from benchmarks/metrics/__init__.py rename to benchmarks/monitoring/__init__.py diff --git a/benchmarks/monitoring/metrics/__init__.py b/benchmarks/monitoring/metrics/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/benchmarks/metrics/metrics_collector.py b/benchmarks/monitoring/metrics_collector.py similarity index 100% rename from benchmarks/metrics/metrics_collector.py rename to benchmarks/monitoring/metrics_collector.py diff --git a/benchmarks/metrics/metrics_monitoring_server.py b/benchmarks/monitoring/metrics_monitoring_server.py similarity index 100% rename from benchmarks/metrics/metrics_monitoring_server.py rename to benchmarks/monitoring/metrics_monitoring_server.py diff --git a/benchmarks/metrics/mms_taurus_monitor.py b/benchmarks/monitoring/metrics_monitoring_taurus.py similarity index 93% rename from benchmarks/metrics/mms_taurus_monitor.py rename to benchmarks/monitoring/metrics_monitoring_taurus.py index 0bcfc1daa..1ac43c174 100644 --- a/benchmarks/metrics/mms_taurus_monitor.py +++ b/benchmarks/monitoring/metrics_monitoring_taurus.py @@ -22,19 +22,18 @@ from bzt.utils import dehumanize_time from bzt.six import PY3 -from metrics_collector import get_metrics, get_mms_processes +from metrics_collector import get_metrics, get_mms_processes, AVAILABLE_METRICS as MMS_AVAILABLE_METRICS class Monitor(monitoring.Monitoring): def __init__(self): super(Monitor, self).__init__() - self.client_classes.update({'MMS_local': MMSLocalClient}) class MMSLocalClient(monitoring.LocalClient): - AVAILABLE_METRICS = monitoring.LocalClient.AVAILABLE_METRICS + ["sum_cpu_percent", "sum_memory_percent", - "sum_num_handles", "mms_workers"] + AVAILABLE_METRICS = monitoring.LocalClient.AVAILABLE_METRICS + \ + MMS_AVAILABLE_METRICS def __init__(self, parent_log, label, config, engine=None): diff --git a/benchmarks/metrics/metrics_monitoring_test_client.py b/benchmarks/monitoring/metrics_monitoring_test_client.py similarity index 100% rename from benchmarks/metrics/metrics_monitoring_test_client.py rename to benchmarks/monitoring/metrics_monitoring_test_client.py diff --git a/benchmarks/monitoring/process.py b/benchmarks/monitoring/process.py new file mode 100644 index 000000000..e69de29bb From 589ab874fa5483a9fa197eb5a73e9db6dd1d466d Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 3 Jun 2020 19:43:53 +0530 Subject: [PATCH 003/329] monitoring read me --- benchmarks/monitoring/README.md | 85 +++++++++++++++++---------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/benchmarks/monitoring/README.md b/benchmarks/monitoring/README.md index c8ed1a6c5..14caa92ef 100644 --- a/benchmarks/monitoring/README.md +++ b/benchmarks/monitoring/README.md @@ -26,31 +26,33 @@ You can specify the test scenarios, in the scenario section of the yaml. There are multiple ways you can specify your test scenario. 1. Use existing Jmeter/locust test script -Taurus provides support for different executors such as JMeter so you can use test script written in those frameworks as it is. -Details about executor types are provided [here](https://gettaurus.org/docs/ExecutionSettings/). -Details about how to run an existing JMeter script are provided [here](https://gettaurus.org/docs/JMeter/). -To get you started quickly, we have provided a sample JMeter Script and a Taurus yaml file here and here. -Use shell command below to run the test case: -``` -bzt call_jmx.yaml -``` - -Here is how the Taurus yaml looks like. -```yaml code - -``` + Taurus provides support for different executors such as JMeter so you can use test script written in those frameworks as it is. + Details about executor types are provided [here](https://gettaurus.org/docs/ExecutionSettings/). + Details about how to run an existing JMeter script are provided [here](https://gettaurus.org/docs/JMeter/). + + To get you started quickly, we have provided a sample JMeter Script and a Taurus yaml file here and here. + Use shell command below to run the test case: + ``` + bzt call_jmx.yaml + ``` + + Here is how the Taurus yaml looks like. + ```yaml code + + ``` 2. Write a Taurus script -You can also write a Taurus script for a specific executor type. -To get you started quickly, we have provided a Taurus script with JMeter executor. -``` -bzt inference_test.yaml -``` -```yaml code - -``` + You can also write a Taurus script for a specific executor type. + To get you started quickly, we have provided a Taurus script with JMeter executor. + + ``` + bzt inference_test.yaml + ``` + ```yaml code + + ``` #### 2. Metrics to monitor @@ -58,30 +60,31 @@ You can specify the different metrics to monitor in services/monitoring section Metrics can be monitored in two ways: 1. Standalone monitoring server -If your server is hosted on different machine, you will be using this method. Before running the test case -you have to start a metric_monitoring_server.py script. It will be communicating with Taurus test client over sockets. -The address and port of the monitoring script should be specified in test case yaml. - -To start monitoring script on server use command below: -``` -sudo python metric_monitoring_server.py -``` - -Sample test yaml: -```yaml code - -``` + If your server is hosted on different machine, you will be using this method. Before running the test case + you have to start a metric_monitoring_server.py script. It will be communicating with Taurus test client over sockets. + The address and port of the monitoring script should be specified in test case yaml. + + To start monitoring script on server use command below: + ``` + sudo python metric_monitoring_server.py + ``` + + Sample test yaml: + ```yaml code + + ``` 2. Taurus local monitoring plugin -If your test client is running on the server itself, you may want to use this method. -We have provided a custom Taurus plugin as metrics_monitoring_taurus.py. Make sure that the benchmarks/monitoring folder -is in PYTHON_PATH. You need to specify the monitoring class. -Sample test yaml: -```yaml code - -``` + If your test client is running on the server itself, you may want to use this method. + We have provided a custom Taurus plugin as metrics_monitoring_taurus.py. Make sure that the benchmarks/monitoring folder + is in PYTHON_PATH. You need to specify the monitoring class. + + Sample test yaml: + ```yaml code + + ``` #### 3. Pass/Fail criteria You can specify the pass/fail criteria for the test cases. From 5edf9a142ef606929cdd255347c75eb79a8c2462 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 4 Jun 2020 01:49:50 +0530 Subject: [PATCH 004/329] perfomance monitoring --- benchmarks/monitoring/README.md | 164 ++++++++++++--- benchmarks/monitoring/metrics/__init__.py | 55 +++++ benchmarks/monitoring/metrics_collector.py | 150 +++----------- .../monitoring/metrics_monitoring_server.py | 17 +- .../monitoring/metrics_monitoring_taurus.py | 32 +-- benchmarks/monitoring/process.py | 67 +++++++ benchmarks/monitoring/tests/call_jmx.yaml | 18 ++ benchmarks/monitoring/tests/inference.jmx | 84 ++++++++ benchmarks/monitoring/tests/inference.yaml | 24 +++ .../tests/inference_server_monitoring.yaml | 38 ++++ .../inference_server_monitoring_criteria.yaml | 49 +++++ .../inference_taurus_local_monitoring.yaml | 43 ++++ ...ence_taurus_local_monitoring_criteria.yaml | 52 +++++ benchmarks/monitoring/tests/kitten.jpg | Bin 0 -> 110969 bytes .../tests/register_and_inference.jmx | 189 ++++++++++++++++++ benchmarks/perf_server.py | 0 16 files changed, 820 insertions(+), 162 deletions(-) create mode 100644 benchmarks/monitoring/tests/call_jmx.yaml create mode 100644 benchmarks/monitoring/tests/inference.jmx create mode 100644 benchmarks/monitoring/tests/inference.yaml create mode 100644 benchmarks/monitoring/tests/inference_server_monitoring.yaml create mode 100644 benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml create mode 100644 benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml create mode 100644 benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml create mode 100644 benchmarks/monitoring/tests/kitten.jpg create mode 100644 benchmarks/monitoring/tests/register_and_inference.jmx delete mode 100644 benchmarks/perf_server.py diff --git a/benchmarks/monitoring/README.md b/benchmarks/monitoring/README.md index 14caa92ef..0264ccd00 100644 --- a/benchmarks/monitoring/README.md +++ b/benchmarks/monitoring/README.md @@ -1,7 +1,6 @@ -# Metrics Monitoring Suite +# Performance Regression Suite -The Metrics Monitoring suite helps in monitoring the process, sub-process and system wide metrics while performance test -cases are running. It allows to specify the pass/fail criteria for metrics in the test case. +This test suite helps in running the load tests and monitoring the process, sub-process and system wide metrics. It allows to specify the pass/fail criteria for metrics in the test case. We use Taurus test automation framework to run the test cases and metrics monitoring. @@ -15,45 +14,95 @@ performance tests. Refer the Taurus installation guide [here](https://gettaurus.org/install/Installation/). -## How to run tests +## How to create and run tests To run a metrics monitoring test case, you need to specify below in Taurus test case yaml. 1. Load test case scenario 2. Metrics to monitor 3. Pass/fail criteria +#### Assumptions made in this guide +1. For some of the test cases, it is assumed that you have started the MMS server with squeezenet model registered + and workers are assigned to it. + #### 1. Load test case scenario You can specify the test scenarios, in the scenario section of the yaml. There are multiple ways you can specify your test scenario. -1. Use existing Jmeter/locust test script +1. Use existing JMeter/locust test script - Taurus provides support for different executors such as JMeter so you can use test script written in those frameworks as it is. + Taurus provides support for different executors such as JMeter. You can use test script written in those frameworks as it is. Details about executor types are provided [here](https://gettaurus.org/docs/ExecutionSettings/). Details about how to run an existing JMeter script are provided [here](https://gettaurus.org/docs/JMeter/). - To get you started quickly, we have provided a sample JMeter Script and a Taurus yaml file here and here. - Use shell command below to run the test case: - ``` - bzt call_jmx.yaml + To get you started quickly, we have provided a sample JMeter script and a Taurus yaml file [here](tests/register_and_inference.jmx) and [here](tests/call_jmx.yaml) . + + Here is how the call_jmx.yaml looks like. Adjust the module/jmeter/properties section as per your environment. + + ```yaml + execution: + - concurrency: 1 + ramp-up: 1s + hold-for: 40s + scenario: Inference + scenarios: + Inference: + script: register_and_inference.jmx + modules: + jmeter: + properties: + hostname : 127.0.0.1 + port : 8080 + management_port : 8081 + protocol : http + input_filepath : kitten.jpg + ``` - Here is how the Taurus yaml looks like. - ```yaml code + Use Taurus command below to run the test case: + ```bash + bzt call_jmx.yaml ``` 2. Write a Taurus script You can also write a Taurus script for a specific executor type. - To get you started quickly, we have provided a Taurus script with JMeter executor. + For quick reference, we have provided a Taurus script with JMeter executor [here](tests/inference.yaml). - ``` - bzt inference_test.yaml - ``` - ```yaml code + Below is the yaml. + + ```yaml + execution: + - concurrency: 4 + ramp-up: 1s + hold-for: 20s + scenario: Inference + scenarios: + Inference: + requests: + - follow-redirects: true + label: Inference Request + method: POST + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} + store-cache: false + store-cookie: false + use-dns-cache-mgr: false + variables: + model: ${__P(model_name,squeezenet_v1.1)} + + modules: + jmeter: + properties: + input_filepath : kitten.jpg + model_name : squeezenet ``` + Use command Taurus command below to run the test yaml. + + ```bash + bzt inference_test.yaml + ``` #### 2. Metrics to monitor You can specify the different metrics to monitor in services/monitoring section of the yaml. @@ -62,27 +111,70 @@ Metrics can be monitored in two ways: 1. Standalone monitoring server If your server is hosted on different machine, you will be using this method. Before running the test case - you have to start a metric_monitoring_server.py script. It will be communicating with Taurus test client over sockets. - The address and port of the monitoring script should be specified in test case yaml. + you have to start a [metric_monitoring_server.py](metrics_monitoring_server.py) script. It will be communicating with Taurus test client over sockets. + The address and port(default=9009) of the monitoring script should be specified in test case yaml. To start monitoring script on server use command below: + + ```bash + sudo python3 benchmarks/monitoring/metric_monitoring_server.py ``` - sudo python metric_monitoring_server.py + + Test yaml with monitoring section config. Complete yaml can be found [here](tests/inference_server_monitoring.yaml) + + ```yaml + + services: + - module: monitoring + server-agent: + - address: localhost:9009 # metric monitoring service address + label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port + interval: 1s # polling interval + logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir + metrics: # metrics should be supported by monitoring service + - sum_cpu_percent # cpu percent used by all the mms server processes and workers + - sum_memory_percent + - sum_num_handles + - server_workers # no of mms workers ``` - Sample test yaml: - ```yaml code + Use command Taurus command below to run the test yaml and observe the Metrics widget on CLI live report. + ```bash + bzt inference_server_monitoring.yaml ``` + 2. Taurus local monitoring plugin If your test client is running on the server itself, you may want to use this method. We have provided a custom Taurus plugin as metrics_monitoring_taurus.py. Make sure that the benchmarks/monitoring folder - is in PYTHON_PATH. You need to specify the monitoring class. + is in PYTHONPATH. You need to specify the monitoring class. + + Use command below to add update PYTHONPATH. - Sample test yaml: - ```yaml code + ```bash + export MMS_HOME= + export PYTHONPATH=$MMS_HOME/benchmarks/monitoring:$PYTHONPATH + ``` + + Relevant test yaml sections. Test yaml can be found [here](tests/inference_taurus_local_monitoring.yaml) + + ``` + modules: + server_local_monitoring: + # metrics_monitoring_taurus and dependencies should be in python path + class : metrics_monitoring_taurus.Monitor # monitoring class. + + services: + - module: server_local_monitoring # should be added in modules section + ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + - interval: 1s + metrics: + - cpu + - disk-space + - mem + - sum_memory_percent ``` @@ -90,7 +182,25 @@ Metrics can be monitored in two ways: You can specify the pass/fail criteria for the test cases. Read more about it [here](https://gettaurus.org/docs/PassFail/) -Sample test yaml: -```yaml code +Relevant test yaml section: +```yaml +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: mms-inference-server/sum_num_handles + condition: '>' + threshold: 180 + timeframe: 1s + fail: true + stop: true ``` + +Test yamls can be found [here](tests/inference_server_monitoring_criteria.yaml) and [here](tests/inference_taurus_local_monitoring_criteria.yaml). +Use command below to run the test case + +```bash +bzt inference_server_monitoring_criteria.yaml +bzt inference_taurus_local_monitoring_criteria.yaml +``` \ No newline at end of file diff --git a/benchmarks/monitoring/metrics/__init__.py b/benchmarks/monitoring/metrics/__init__.py index e69de29bb..637857180 100644 --- a/benchmarks/monitoring/metrics/__init__.py +++ b/benchmarks/monitoring/metrics/__init__.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Custom metrics +""" +# pylint: disable=redefined-builtin + +AVAILABLE_METRICS = ["sum_cpu_percent", + "sum_memory_percent", + "sum_num_handles", + "server_workers"] + + +def get_metrics(server_process, child_processes): + """ Get Server processes specific metrics + """ + + # TODO - make this modular may be a diff function for each metric + # TODO - allow users to add new metrics easily + # TODO - make sure available metric list is maintained + + sum_cpu_percent = 0 + sum_memory_percent = 0 + sum_num_handles = 0 + server_workers = 0 + metrics = {} + for process in [server_process] + child_processes: + try: + process.cpu_percent() # to warm-up + except: + pass + else: + cpu_percent = process.cpu_percent() + memory_percent = process.memory_percent() + sum_cpu_percent += cpu_percent + sum_memory_percent += memory_percent + sum_num_handles += process.num_fds() + server_workers += 1 + + metrics["sum_cpu_percent"] = sum_cpu_percent + metrics["sum_memory_percent"] = sum_memory_percent + metrics["sum_num_handles"] = sum_num_handles + metrics["server_workers"] = server_workers + + return metrics \ No newline at end of file diff --git a/benchmarks/monitoring/metrics_collector.py b/benchmarks/monitoring/metrics_collector.py index e0891c20e..8a9bfe4b8 100755 --- a/benchmarks/monitoring/metrics_collector.py +++ b/benchmarks/monitoring/metrics_collector.py @@ -11,7 +11,7 @@ # permissions and limitations under the License. """ -MMS server metrics collector +Server metrics collector """ # pylint: disable=redefined-builtin @@ -23,49 +23,21 @@ import gevent import argparse -TMP_DIR = "/var/folders/04/6_v1bbs55mb_hrpkphh46xcc0000gn/T" -METRICS_LOG_FILE = "{}/mms_metrics_{}.log".format(TMP_DIR, int(time.time())) -MMS_PID_FILE = "{}/.model_server.pid".format(TMP_DIR) -METRICS_COLLECTOR_PID_FILE = "{}/.metrics_collector.pid".format(TMP_DIR) -MONITOR_INTERVAL = 1 -PID_LIST_INTERVAL = 2 -AVAILABLE_METRICS = ["sum_cpu_percent", - "sum_memory_percent", - "sum_num_handles", - "mms_workers"] - -child_processes = set() -mms_process = None - - -def find_procs_by_name(name): - """Return a list of processes matching 'name'.""" - ls = [] - for p in psutil.process_iter(["name", "exe", "cmdline"]): - if name == p.info['name'] or \ - p.info['exe'] and os.path.basename(p.info['exe']) == name or \ - p.info['cmdline'] and p.info['cmdline'][0] == name: - ls.append(p) +import tempfile +from process import find_procs_by_name, get_process_pid_from_file, get_child_processes, get_server_processes +from metrics import AVAILABLE_METRICS, get_metrics - if len(ls) > 1: - raise Exception("Multiple processes found with name {}.".format(name)) - - return ls[0] - - -def get_process_pid_from_file(file_path): - """Get the process pid from pid file. - """ - if os.path.isfile(file_path): - with open(file_path, "r") as f: - pid = int(f.readline()) - else: - raise Exception("Invalid file {}".format(file_path)) - return pid +# TODO - move these variables to config file +TMP_DIR = "/var/folders/04/6_v1bbs55mb_hrpkphh46xcc0000gn/T" # TODO - use tempfile. Currently there is an issue with sudo +METRICS_LOG_FILE = "{}/server_metrics_{}.log".format(TMP_DIR, int(time.time())) +SERVER_PID_FILE = "{}/.model_server.pid".format(TMP_DIR) # MMS specific +METRICS_COLLECTOR_PID_FILE = "{}/.metrics_collector.pid".format(TMP_DIR) +MONITOR_INTERVAL = 1 def store_metrics_collector_pid(): + """ Stores the process id of metrics collector process""" metrics_collector_process = psutil.Process(METRICS_COLLECTOR_PID_FILE) pid_file = os.path.join() with open(pid_file, "w") as pf: @@ -82,96 +54,46 @@ def stop_metrics_collector_process(): try: process = psutil.Process(pid) if process.is_running(): - logging.info("Process with pid {} is running. Killing it.".format(process.pid)) + logging.info(logging.INFO, "Process with pid {} is running. Killing it.".format(process.pid)) process.kill() except Exception as e: pass else: - logging.info("Dead process with pid {} found in '{}'.".format(process.pid, METRICS_COLLECTOR_PID_FILE)) + logging.info(logging.INFO, "Dead process with pid {} found in '{}'.".format(process.pid, METRICS_COLLECTOR_PID_FILE)) - logging.info("Removing pid file '{}'.".format(METRICS_COLLECTOR_PID_FILE)) + logging.info(logging.INFO, "Removing pid file '{}'.".format(METRICS_COLLECTOR_PID_FILE)) os.remove(METRICS_COLLECTOR_PID_FILE) -def get_child_processes(process): - """Get all running child processes recursively""" - child_processes = [] - for p in process.children(recursive=True): - if p.status() == 'running': - child_processes.append(p) - return child_processes - - -def get_metrics(): - """ Get MMS processes specific metrics +def monitor_processes(server_process, metrics, interval, socket): + """ Monitor the metrics of server_process and its child processes """ - - # TODO - make this modular may be a diff function for each metric - # TODO - allow users to add new metrics easliy - # TODO - make sure available metric list is maintained - - sum_cpu_percent = 0 - sum_memory_percent = 0 - sum_num_handles = 0 - mms_workers = 0 - metrics = {} - for process in [mms_process] + list(get_child_processes(mms_process)): - try: - process.cpu_percent() # to warm-up - except: - pass - else: - cpu_percent = process.cpu_percent() - memory_percent = process.memory_percent() - sum_cpu_percent += cpu_percent - sum_memory_percent += memory_percent - sum_num_handles += process.num_fds() - mms_workers += 1 - - metrics["sum_cpu_percent"] = sum_cpu_percent - metrics["sum_memory_percent"] = sum_memory_percent - metrics["sum_num_handles"] = sum_num_handles - metrics["mms_workers"] = mms_workers - - - return metrics - - -def monitor_processes(socket, interval, metrics): while True: message = [] - collected_metrics = get_metrics() + collected_metrics = get_metrics(server_process, get_child_processes(server_process)) for metric in metrics: message.append(str(collected_metrics.get(metric, 0))) message = "\t".join(message)+"\n" if socket: socket.send(message.encode("latin-1")) - gevent.sleep(interval) - -def get_mms_processes(): - """ It caches the MMS and child processes at module level. - Ensure that you call this process so that MMS process - """ + # TODO - log metrics to a file METRICS_LOG_FILE if METRICS_LOG_FILE is provided + gevent.sleep(interval) - global mms_process - global child_processes - mms_pid = get_process_pid_from_file() - if not mms_pid: - print("MMS process not found11.") - exit() - try: - mms_process = psutil.Process(mms_pid) - except Exception as e: - print("MMS process not found. Error: {}".format(str(e))) - raise +def start_metric_collection(server_process, metrics, interval, socket): + bad_metrics = set(metrics) - set(AVAILABLE_METRICS) + if bad_metrics: + raise Exception("Metrics not available for monitoring {}.".format(bad_metrics)) - child_processes = set(get_child_processes(mms_process)) + logging.log(logging.INFO, "Started metric collection for target server processes.....") + thread = gevent.spawn(monitor_processes, server_process, metrics, interval, socket) + gevent.joinall([thread]) def start_metric_collector_process(): + """Spawn a metric collection process and keep on monitoring """ metric_collector_pid = get_process_pid_from_file(METRICS_COLLECTOR_PID_FILE) if metric_collector_pid: try: @@ -183,17 +105,9 @@ def start_metric_collector_process(): raise Exception("Performance monitoring script already running. " "Stop it using stop option.") store_metrics_collector_pid() - - -def start_metric_collection(socket, interval, metrics): - bad_metrics = set(metrics) - set(AVAILABLE_METRICS) - if bad_metrics: - raise Exception("Metrics not available for monitoring {}.".format(bad_metrics)) - - get_mms_processes() - print("Started metric collection for MMS processes.....") - thread2 = gevent.spawn(monitor_processes, socket, interval, metrics) - gevent.joinall([thread2]) + server_pid = get_process_pid_from_file(SERVER_PID_FILE) + server_process = get_server_processes(server_pid) + start_metric_collection(server_process, AVAILABLE_METRICS, MONITOR_INTERVAL, None) if __name__ == "__main__": @@ -206,6 +120,6 @@ def start_metric_collection(socket, interval, metrics): args = parser.parse_args() if args.start: - start_perf_mon() + start_metric_collector_process() elif args.stop: - stop_perf_mon_process() + stop_metrics_collector_process() diff --git a/benchmarks/monitoring/metrics_monitoring_server.py b/benchmarks/monitoring/metrics_monitoring_server.py index 9f9dce5c6..c5fc30b28 100644 --- a/benchmarks/monitoring/metrics_monitoring_server.py +++ b/benchmarks/monitoring/metrics_monitoring_server.py @@ -11,7 +11,7 @@ # permissions and limitations under the License. """ -MMS server monitoring script over the socket +Server monitoring script over the socket """ # pylint: disable=redefined-builtin @@ -23,8 +23,13 @@ monkey.patch_select() monkey.patch_socket() -from perf_monitor import start_perf_mon +from metrics_collector import start_metric_collection +from process import find_procs_by_name, get_process_pid_from_file, get_child_processes, get_server_processes +# TODO - move these variables to config +TMP_DIR = "/var/folders/04/6_v1bbs55mb_hrpkphh46xcc0000gn/T" +# TODO - use tempfile. Currently there is an issue with sudo +SERVER_PID_FILE = "{}/.model_server.pid".format(TMP_DIR) # MMS specific HOST = '' SOCKET_LIST = [] RECV_BUFFER = 4096 @@ -37,8 +42,8 @@ def perf_server(): server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((HOST, PORT)) server_socket.listen(10) - SOCKET_LIST.append(server_socket) + logging.log(logging.INFO, "Started metrics monitoring server on port {}".format(PORT)) while True: ready_to_read, ready_to_write, in_error = select.select(SOCKET_LIST, [], [], 0) @@ -48,7 +53,7 @@ def perf_server(): if sock == server_socket: sockfd, addr = server_socket.accept() SOCKET_LIST.append(sockfd) - logging.log("client ({}, {}) connected".format(addr[0], addr[1])) + logging.log(logging.INFO, "client ({}, {}) connected".format(addr[0], addr[1])) # a message from a client, not a new connection else: @@ -68,7 +73,9 @@ def perf_server(): send_message(sock, "In-correct interval data") elif data.startswith('metrics'): metrics = data[:-1].split("metrics:")[1].split("\t") - start_perf_mon(sock, interval, metrics) + server_pid = get_process_pid_from_file(SERVER_PID_FILE) + server_process = get_server_processes(server_pid) + start_metric_collection(server_process, metrics, interval, sock) else: # TODO - decide what to do here pass diff --git a/benchmarks/monitoring/metrics_monitoring_taurus.py b/benchmarks/monitoring/metrics_monitoring_taurus.py index 1ac43c174..9002d6254 100644 --- a/benchmarks/monitoring/metrics_monitoring_taurus.py +++ b/benchmarks/monitoring/metrics_monitoring_taurus.py @@ -11,8 +11,9 @@ # permissions and limitations under the License. """ -Taurus local plugin for MMS monitoring. -This file should be placed in Python Path along with metrics_collector.py file +Taurus local plugin for server monitoring. +Should be used when server and Taurus are running on same machine. +This file should be placed in Python Path along with monitoring package. """ # pylint: disable=redefined-builtin @@ -22,23 +23,29 @@ from bzt.utils import dehumanize_time from bzt.six import PY3 -from metrics_collector import get_metrics, get_mms_processes, AVAILABLE_METRICS as MMS_AVAILABLE_METRICS +from metrics import get_metrics, AVAILABLE_METRICS as AVAILABLE_SERVER_METRICS +from process import get_process_pid_from_file, get_server_processes, get_child_processes + +# TODO - move these variables to config +TMP_DIR = "/var/folders/04/6_v1bbs55mb_hrpkphh46xcc0000gn/T" +# TODO - use tempfile. Currently there is an issue with sudo +SERVER_PID_FILE = "{}/.model_server.pid".format(TMP_DIR) # MMS specific class Monitor(monitoring.Monitoring): def __init__(self): super(Monitor, self).__init__() - self.client_classes.update({'MMS_local': MMSLocalClient}) + self.client_classes.update({'ServerLocalClient': ServerLocalClient}) -class MMSLocalClient(monitoring.LocalClient): +class ServerLocalClient(monitoring.LocalClient): AVAILABLE_METRICS = monitoring.LocalClient.AVAILABLE_METRICS + \ - MMS_AVAILABLE_METRICS + AVAILABLE_SERVER_METRICS def __init__(self, parent_log, label, config, engine=None): - super(MMSLocalClient, self).__init__(parent_log, label, config, engine=engine) - self.label = 'MMSLocalClient' + super(ServerLocalClient, self).__init__(parent_log, label, config, engine=engine) + self.label = 'ServerLocalClient' def connect(self): exc = TaurusConfigError('Metric is required in Local monitoring client') @@ -54,7 +61,7 @@ def connect(self): self.metrics = list(set(good_list)) - self.monitor = MMSLocalMonitor(self.log, self.metrics, self.engine) + self.monitor = ServerLocalMonitor(self.log, self.metrics, self.engine) self.interval = dehumanize_time(self.config.get("interval", self.engine.check_interval)) if self.config.get("logging", False): @@ -68,12 +75,13 @@ def connect(self): logs_writer.writerow(metrics) -class MMSLocalMonitor(monitoring.LocalMonitor): +class ServerLocalMonitor(monitoring.LocalMonitor): def _calc_resource_stats(self, interval): result =super()._calc_resource_stats(interval) - get_mms_processes() - result.update(get_metrics()) + server_pid = get_process_pid_from_file(SERVER_PID_FILE) + server_process = get_server_processes(server_pid) + result.update(get_metrics(server_process, get_child_processes(server_process))) return result diff --git a/benchmarks/monitoring/process.py b/benchmarks/monitoring/process.py index e69de29bb..7cdf09258 100644 --- a/benchmarks/monitoring/process.py +++ b/benchmarks/monitoring/process.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Utility methods for process related information +""" +# pylint: disable=redefined-builtin + +import os +import psutil + + +def find_procs_by_name(name): + """Return a list of processes matching 'name'.""" + ls = [] + for p in psutil.process_iter(["name", "exe", "cmdline"]): + if name == p.info['name'] or \ + p.info['exe'] and os.path.basename(p.info['exe']) == name or \ + p.info['cmdline'] and p.info['cmdline'][0] == name: + ls.append(p) + + if len(ls) > 1: + raise Exception("Multiple processes found with name {}.".format(name)) + + return ls[0] + + +def get_process_pid_from_file(file_path): + """Get the process pid from pid file. + """ + if os.path.isfile(file_path): + with open(file_path, "r") as f: + pid = int(f.readline()) + else: + raise Exception("Invalid file {}".format(file_path)) + + return pid + + +def get_child_processes(process): + """Get all running child processes recursively""" + child_processes = [] + for p in process.children(recursive=True): + if p.status() == 'running': + child_processes.append(p) + return child_processes + + +def get_server_processes(server_process_pid): + """ It caches the main server and child processes at module level. + Ensure that you call this process so that MMS process + """ + try: + server_process = psutil.Process(server_process_pid) + except Exception as e: + print("Server process not found. Error: {}".format(str(e))) + raise + return server_process \ No newline at end of file diff --git a/benchmarks/monitoring/tests/call_jmx.yaml b/benchmarks/monitoring/tests/call_jmx.yaml new file mode 100644 index 000000000..d75695e49 --- /dev/null +++ b/benchmarks/monitoring/tests/call_jmx.yaml @@ -0,0 +1,18 @@ +--- +execution: +- concurrency: 1 + ramp-up: 1s + hold-for: 40s + scenario: Inference +scenarios: + Inference: + script: register_and_inference.jmx +modules: + jmeter: + properties: + port : 8080 + management_port : 8081 + protocol : http + input_filepath : kitten.jpg + + diff --git a/benchmarks/monitoring/tests/inference.jmx b/benchmarks/monitoring/tests/inference.jmx new file mode 100644 index 000000000..909c03cfd --- /dev/null +++ b/benchmarks/monitoring/tests/inference.jmx @@ -0,0 +1,84 @@ + + + + + + false + true + false + + + + + + + + + + model + squeezenet + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,2)} + + ${__P(threads,4)} + ${__P(rampup,1)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + + diff --git a/benchmarks/monitoring/tests/inference.yaml b/benchmarks/monitoring/tests/inference.yaml new file mode 100644 index 000000000..3fb71b303 --- /dev/null +++ b/benchmarks/monitoring/tests/inference.yaml @@ -0,0 +1,24 @@ + +execution: +- concurrency: 4 + ramp-up: 1s + hold-for: 20s + scenario: Inference +scenarios: + Inference: + requests: + - follow-redirects: true + label: Inference Request + method: POST + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} + store-cache: false + store-cookie: false + use-dns-cache-mgr: false + variables: + model: ${__P(model_name,squeezenet_v1.1)} + +modules: + jmeter: + properties: + input_filepath : kitten.jpg + model_name : squeezenet diff --git a/benchmarks/monitoring/tests/inference_server_monitoring.yaml b/benchmarks/monitoring/tests/inference_server_monitoring.yaml new file mode 100644 index 000000000..1e3b9f205 --- /dev/null +++ b/benchmarks/monitoring/tests/inference_server_monitoring.yaml @@ -0,0 +1,38 @@ + +execution: +- concurrency: 4 + ramp-up: 1s + hold-for: 20s + scenario: Inference +scenarios: + Inference: + requests: + - follow-redirects: true + label: Inference Request + method: POST + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} + store-cache: false + store-cookie: false + use-dns-cache-mgr: false + variables: + model: ${__P(model_name,squeezenet_v1.1)} + +modules: + jmeter: + properties: + input_filepath : kitten.jpg + model_name : squeezenet + + +services: + - module: monitoring + server-agent: + - address: localhost:9009 # metric monitoring service address + label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port + interval: 1s # polling interval + logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir + metrics: # metrics should be supported by monitoring service + - sum_cpu_percent # cpu percent used by all the mms server processes and workers + - sum_memory_percent + - sum_num_handles + - server_workers # no of mms workers diff --git a/benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml b/benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml new file mode 100644 index 000000000..5f08515b8 --- /dev/null +++ b/benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml @@ -0,0 +1,49 @@ + +execution: +- concurrency: 4 + ramp-up: 1s + hold-for: 20s + scenario: Inference +scenarios: + Inference: + requests: + - follow-redirects: true + label: Inference Request + method: POST + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} + store-cache: false + store-cookie: false + use-dns-cache-mgr: false + variables: + model: ${__P(model_name,squeezenet_v1.1)} + +modules: + jmeter: + properties: + input_filepath : kitten.jpg + model_name : squeezenet + + +services: + - module: monitoring + server-agent: + - address: localhost:9009 # metric monitoring service address + label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port + interval: 1s # polling interval + logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir + metrics: # metrics should be supported by monitoring service + - sum_cpu_percent # cpu percent used by all the mms server processes and workers + - sum_memory_percent + - sum_num_handles + - server_workers # no of mms workers + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: mms-inference-server/sum_num_handles + condition: '>' + threshold: 180 + timeframe: 1s + fail: true + stop: true \ No newline at end of file diff --git a/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml b/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml new file mode 100644 index 000000000..64cbccb20 --- /dev/null +++ b/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml @@ -0,0 +1,43 @@ +--- +execution: +- concurrency: 1 + ramp-up: 5s + hold-for: 20s + scenario: Inference +scenarios: + Inference: + default-address: ${__P(protocol,https)}://${__P(hostname,127.0.0.1)}:${__P(port,8443)}/ + requests: + - follow-redirects: true + label: Inference Request + method: POST + upload-files: + - mime-type: image/jpeg + param: data + path: ${__P(input_filepath)} + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} + store-cache: false + store-cookie: false + use-dns-cache-mgr: false + variables: + model: ${__P(model_name,squeezenet_v1.1)} + +modules: + jmeter: + properties: + input_filepath : kitten.jpg + model_name : squeezenet + server_local_monitoring: + # metrics_monitoring_taurus and dependencies should be in python path + class : metrics_monitoring_taurus.Monitor # monitoring class. + +services: + - module: server_local_monitoring # should be added in modules section + ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + - interval: 1s + metrics: + - cpu + - disk-space + - mem + - sum_memory_percent + diff --git a/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml b/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml new file mode 100644 index 000000000..4ae1e84a7 --- /dev/null +++ b/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml @@ -0,0 +1,52 @@ +--- +execution: +- concurrency: 1 + ramp-up: 5s + hold-for: 20s + scenario: Inference +scenarios: + Inference: + default-address: ${__P(protocol,https)}://${__P(hostname,127.0.0.1)}:${__P(port,8443)}/ + requests: + - follow-redirects: true + label: Inference Request + method: POST + upload-files: + - mime-type: image/jpeg + param: data + path: ${__P(input_filepath)} + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} + store-cache: false + store-cookie: false + use-dns-cache-mgr: false + variables: + model: ${__P(model_name,squeezenet_v1.1)} + +modules: + jmeter: + properties: + input_filepath : kitten.jpg + model_name : squeezenet + server_local_monitoring: + # metrics_monitoring_taurus and dependencies should be in python path + class : metrics_monitoring_taurus.Monitor # monitoring class. + +services: + - module: server_local_monitoring # should be added in modules section + ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + - interval: 1s + metrics: + - cpu + - disk-space + - mem + - sum_memory_percent + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_memory_percent + condition: '>' + threshold: 14 + timeframe: 1s + diff --git a/benchmarks/monitoring/tests/kitten.jpg b/benchmarks/monitoring/tests/kitten.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ffcd2be2c674fddc4886117c12f3b29e4367aba8 GIT binary patch literal 110969 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

J66l>z7T20v&BrMUK^shcTlD$)2!& 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

hPAkbbTr(^R-tMK2 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%0DL;n=L?~l?ng&I8MPm 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 z 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%0D>0jKMROl@?0Zb`>KdeJYDW6roP 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 NZ`_ZLr78E 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 + + + + + false + true + false + + + + + + + + + + cnn_url + https://s3.amazonaws.com/model-server/models/squeezenet_v1.1/squeezenet_v1.1.model + = + The url from where to fetch noop model from + + + scale_up_workers + ${__P(min_workers,1)} + = + The workers to scale No op model to + + + scale_down_workers + 0 + Offload the No-Op Model + = + + + model + squeezenet_v1.1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${cnn_url} + POST + true + false + true + false + + + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_up_workers} + PUT + true + false + true + false + + + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_down_workers} + DELETE + true + false + true + false + + + + + + + + + diff --git a/benchmarks/perf_server.py b/benchmarks/perf_server.py deleted file mode 100644 index e69de29bb..000000000 From 464359b398f34a637295e3d2f4896f1c14092737 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 4 Jun 2020 01:58:15 +0530 Subject: [PATCH 005/329] remove old files --- benchmarks/__init__.py | 0 benchmarks/jmx/call_jmx.yaml | 15 ---- benchmarks/jmx/concurrentScaleCalls.jmx.yaml | 57 --------------- benchmarks/jmx/imageInputModelPlan.jmx.yaml | 49 ------------- .../jmx/inference_mms_local_monitoring.yaml | 73 ------------------- benchmarks/jmx/test.yaml | 0 6 files changed, 194 deletions(-) delete mode 100644 benchmarks/__init__.py delete mode 100644 benchmarks/jmx/call_jmx.yaml delete mode 100644 benchmarks/jmx/concurrentScaleCalls.jmx.yaml delete mode 100644 benchmarks/jmx/imageInputModelPlan.jmx.yaml delete mode 100644 benchmarks/jmx/inference_mms_local_monitoring.yaml delete mode 100644 benchmarks/jmx/test.yaml diff --git a/benchmarks/__init__.py b/benchmarks/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/benchmarks/jmx/call_jmx.yaml b/benchmarks/jmx/call_jmx.yaml deleted file mode 100644 index 1f562bccf..000000000 --- a/benchmarks/jmx/call_jmx.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 1s - hold-for: 20s - scenario: Inference -scenarios: - Inference: - script: imageInputModelPlan.jmx -modules: - jmeter: - properties: - input_filepath : kitten.jpg - model_name : squeezenet - diff --git a/benchmarks/jmx/concurrentScaleCalls.jmx.yaml b/benchmarks/jmx/concurrentScaleCalls.jmx.yaml deleted file mode 100644 index 5a6fa9d90..000000000 --- a/benchmarks/jmx/concurrentScaleCalls.jmx.yaml +++ /dev/null @@ -1,57 +0,0 @@ ---- -execution: -- concurrency: 10 - iterations: 3 - ramp-up: 5s - scenario: Concurrent Scale Up Model/Scale Down Model Requests -scenarios: - Concurrent Scale Up Model/Scale Down Model Requests: - requests: - - follow-redirects: true - label: Scale Up Model - method: PUT - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(management_port,8081)}/models/${model}?min_worker=${scale_up_workers} - - follow-redirects: true - label: Scale Down Model - method: PUT - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(management_port,8081)}/models/${model}?min_worker=${scale_down_workers} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - count: ${__P(count,1)} - model: ${__P(model_name,squeezenet)} - scale_down_workers: ${__P(scale_down_workers,2)} - scale_up_workers: ${__P(scale_up_workers,8)} - -services: - - module: monitoring - server-agent: - - address: localhost:9009 - label: mms-server # if you specify label, it will be used in reports instead of ip:port - interval: 1s # polling interval - logging: True # those logs will be saved to "SAlogs_192.168.0.1_4444.csv" in the artifacts dir - metrics: - - sum_cpu_percent - - sum_memory_percent - - sum_num_handles - - mms_workers - - -reporting: -- module: passfail - criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - subject: mms-server/mms_workers - condition: '>' - threshold: 8 - timeframe: 5s - fail: true - stop: true - - class: bzt.modules.monitoring.MonitoringCriteria - subject: mms-server/mms_workers - condition: '<' - threshold: 2 - timeframe: 5s - fail: true - stop: true \ No newline at end of file diff --git a/benchmarks/jmx/imageInputModelPlan.jmx.yaml b/benchmarks/jmx/imageInputModelPlan.jmx.yaml deleted file mode 100644 index 9fff35181..000000000 --- a/benchmarks/jmx/imageInputModelPlan.jmx.yaml +++ /dev/null @@ -1,49 +0,0 @@ ---- -execution: -- concurrency: 4 - ramp-up: 1s - hold-for: 20s - scenario: Inference -scenarios: - Inference: - requests: - - follow-redirects: true - label: Inference Request - method: POST - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - model: ${__P(model_name,squeezenet_v1.1)} - -modules: - jmeter: - properties: - input_filepath : kitten.jpg - model_name : squeezenet - -services: - - module: monitoring - server-agent: - - address: localhost:9009 - label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port - interval: 1s # polling interval - logging: True # those logs will be saved to "SAlogs_192.168.0.1_4444.csv" in the artifacts dir - metrics: - - sum_cpu_percent - - sum_memory_percent - - sum_num_handles - -reporting: -- module: passfail - criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - subject: mms-inference-server/sum_num_handles - condition: '>' - threshold: 180 - timeframe: 1s - fail: true - stop: true - - diff --git a/benchmarks/jmx/inference_mms_local_monitoring.yaml b/benchmarks/jmx/inference_mms_local_monitoring.yaml deleted file mode 100644 index fb4afe9ec..000000000 --- a/benchmarks/jmx/inference_mms_local_monitoring.yaml +++ /dev/null @@ -1,73 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 5s - hold-for: 1m - scenario: Inference -scenarios: - Inference: - default-address: ${__P(protocol,https)}://${__P(hostname,127.0.0.1)}:${__P(port,8443)}/ - requests: - - follow-redirects: true - label: Inference Request - method: POST - upload-files: - - mime-type: image/jpeg - param: data - path: ${__P(input_filepath)} - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - model: ${__P(model_name,squeezenet_v1.1)} - -modules: - jmeter: - properties: - input_filepath : kitten.jpg - model_name : squeezenet - mms_monitoring: - class : mms_monitoring.Monitor - -#services: -# - module: monitoring -# server-agent: -# - address: localhost:9009 -# label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port -# interval: 1s # polling interval -# logging: True # those logs will be saved to "SAlogs_192.168.0.1_4444.csv" in the artifacts dir -# metrics: -# - sum_cpu_percent -# - sum_mem_percent -# - sum_num_handles -# -#reporting: -#- module: passfail -# criteria: -# - class: bzt.modules.monitoring.MonitoringCriteria -# subject: mms-inference-server/sum_mem_percent -# condition: '>' -# threshold: 4 -# timeframe: 5s -# fail: true -# stop: true - - -services: - - module: mms_monitoring - MMS_local: - - interval: 1s - metrics: - - cpu - - disk-space - - mem -reporting: -- module: passfail - criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - subject: MMS_local/cpu - condition: '>' - threshold: 30 - timeframe: 1s - diff --git a/benchmarks/jmx/test.yaml b/benchmarks/jmx/test.yaml deleted file mode 100644 index e69de29bb..000000000 From e4444ea6b19020285616e7141325f95bc3f879fb Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 4 Jun 2020 02:01:14 +0530 Subject: [PATCH 006/329] small changes --- benchmarks/monitoring/__init__.py | 15 +++++++++++++++ .../monitoring/metrics_monitoring_taurus.py | 5 ----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/benchmarks/monitoring/__init__.py b/benchmarks/monitoring/__init__.py index e69de29bb..b451f88e4 100644 --- a/benchmarks/monitoring/__init__.py +++ b/benchmarks/monitoring/__init__.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Metrics Monitoring suite +""" \ No newline at end of file diff --git a/benchmarks/monitoring/metrics_monitoring_taurus.py b/benchmarks/monitoring/metrics_monitoring_taurus.py index 9002d6254..5b1406dd8 100644 --- a/benchmarks/monitoring/metrics_monitoring_taurus.py +++ b/benchmarks/monitoring/metrics_monitoring_taurus.py @@ -83,8 +83,3 @@ def _calc_resource_stats(self, interval): server_process = get_server_processes(server_pid) result.update(get_metrics(server_process, get_child_processes(server_process))) return result - - - - - From 0cbacede65c208bbb0ffaa4e43960e55335626b6 Mon Sep 17 00:00:00 2001 From: Mahesh Ambule <15611578+maheshambule@users.noreply.github.com> Date: Thu, 4 Jun 2020 02:25:48 +0530 Subject: [PATCH 007/329] Delete metrics_monitoring_test_client.py --- .../metrics_monitoring_test_client.py | 67 ------------------- 1 file changed, 67 deletions(-) delete mode 100644 benchmarks/monitoring/metrics_monitoring_test_client.py diff --git a/benchmarks/monitoring/metrics_monitoring_test_client.py b/benchmarks/monitoring/metrics_monitoring_test_client.py deleted file mode 100644 index 82fe5da7c..000000000 --- a/benchmarks/monitoring/metrics_monitoring_test_client.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -Test client for MMS Metrics Monitoring Server -""" - -import sys, socket, select - - -def client(): - host = sys.argv[1] - port = int(sys.argv[2]) - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.settimeout(2) - try: - s.connect((host, port)) - except: - print('Unable to connect') - sys.exit() - - print('Connected to remote host. You can start sending messages') - sys.stdout.write('[Me] '); - sys.stdout.flush() - - while 1: - socket_list = [sys.stdin, s] - - # Get the list sockets which are readable - read_sockets, write_sockets, error_sockets = select.select(socket_list, [], []) - - for sock in read_sockets: - if sock == s: - # incoming message from remote server, s - data = sock.recv(4096).decode() - if not data: - print('\nDisconnected from the server') - sys.exit() - else: - # print data - sys.stdout.write(data) - sys.stdout.write('[Me] '); - sys.stdout.flush() - - else: - # user entered a message - msg = sys.stdin.readline() - s.send(msg.encode("latin-1")) - sys.stdout.write('[Me] '); - sys.stdout.flush() - - -if __name__ == "__main__": - if (len(sys.argv) < 3): - print('Usage : python chat_client.py hostname port') - sys.exit() - - sys.exit(client()) From d1b37b17e7851d23c5c1ba92518bd881bfaf44b6 Mon Sep 17 00:00:00 2001 From: Mahesh Ambule <15611578+maheshambule@users.noreply.github.com> Date: Thu, 4 Jun 2020 02:31:09 +0530 Subject: [PATCH 008/329] Update README.md --- benchmarks/monitoring/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmarks/monitoring/README.md b/benchmarks/monitoring/README.md index 0264ccd00..3bfb670fc 100644 --- a/benchmarks/monitoring/README.md +++ b/benchmarks/monitoring/README.md @@ -101,7 +101,7 @@ There are multiple ways you can specify your test scenario. Use command Taurus command below to run the test yaml. ```bash - bzt inference_test.yaml + bzt inference.yaml ``` #### 2. Metrics to monitor @@ -148,7 +148,7 @@ Metrics can be monitored in two ways: 2. Taurus local monitoring plugin If your test client is running on the server itself, you may want to use this method. - We have provided a custom Taurus plugin as metrics_monitoring_taurus.py. Make sure that the benchmarks/monitoring folder + We have provided a custom Taurus plugin as [metrics_monitoring_taurus.py](metrics_monitoring_taurus.py). Make sure that the benchmarks/monitoring folder is in PYTHONPATH. You need to specify the monitoring class. Use command below to add update PYTHONPATH. @@ -203,4 +203,4 @@ Use command below to run the test case ```bash bzt inference_server_monitoring_criteria.yaml bzt inference_taurus_local_monitoring_criteria.yaml -``` \ No newline at end of file +``` From 1ee4712943c64bcff7dd0d5351725cbe9c668316 Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Thu, 4 Jun 2020 18:40:08 +0530 Subject: [PATCH 009/329] externalize config file, add requirements.txt, code refactor for tmp dir --- benchmarks/monitoring/README.md | 11 +++++--- benchmarks/monitoring/config.ini | 6 +++++ benchmarks/monitoring/configuration.py | 26 +++++++++++++++++++ benchmarks/monitoring/metrics_collector.py | 17 ++++++------ .../monitoring/metrics_monitoring_server.py | 22 ++++++++-------- .../monitoring/metrics_monitoring_taurus.py | 17 +++++------- benchmarks/monitoring/process.py | 11 +++++--- benchmarks/monitoring/requirements.txt | 1 + .../inference_taurus_local_monitoring.yaml | 13 +++++----- ...ence_taurus_local_monitoring_criteria.yaml | 18 ++++++------- 10 files changed, 90 insertions(+), 52 deletions(-) create mode 100644 benchmarks/monitoring/config.ini create mode 100644 benchmarks/monitoring/configuration.py create mode 100644 benchmarks/monitoring/requirements.txt diff --git a/benchmarks/monitoring/README.md b/benchmarks/monitoring/README.md index 3bfb670fc..f3096577d 100644 --- a/benchmarks/monitoring/README.md +++ b/benchmarks/monitoring/README.md @@ -111,13 +111,18 @@ Metrics can be monitored in two ways: 1. Standalone monitoring server If your server is hosted on different machine, you will be using this method. Before running the test case - you have to start a [metric_monitoring_server.py](metrics_monitoring_server.py) script. It will be communicating with Taurus test client over sockets. + you have to start a [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will be communicating with Taurus test client over sockets. The address and port(default=9009) of the monitoring script should be specified in test case yaml. + To install monitoring server dependencies, use the following command + ```bash + pip install -r requirements.txt + ``` + To start monitoring script on server use command below: - ```bash - sudo python3 benchmarks/monitoring/metric_monitoring_server.py + ```bash + python benchmarks/monitoring/metrics_monitoring_server.py ``` Test yaml with monitoring section config. Complete yaml can be found [here](tests/inference_server_monitoring.yaml) diff --git a/benchmarks/monitoring/config.ini b/benchmarks/monitoring/config.ini new file mode 100644 index 000000000..27bc4c37b --- /dev/null +++ b/benchmarks/monitoring/config.ini @@ -0,0 +1,6 @@ +[server] +pid_file = model_server.pid + +[monitoring] +HOST = +PORT = 9009 \ No newline at end of file diff --git a/benchmarks/monitoring/configuration.py b/benchmarks/monitoring/configuration.py new file mode 100644 index 000000000..e5ebeb998 --- /dev/null +++ b/benchmarks/monitoring/configuration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Read configuration file +""" +# pylint: disable=redefined-builtin +import configparser + +config = configparser.ConfigParser() +config.read('./config.ini') + +def get(section, key, default=''): + try: + return config[section][key] + except: + return default diff --git a/benchmarks/monitoring/metrics_collector.py b/benchmarks/monitoring/metrics_collector.py index 8a9bfe4b8..4f85a42d1 100755 --- a/benchmarks/monitoring/metrics_collector.py +++ b/benchmarks/monitoring/metrics_collector.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License is located at @@ -22,17 +22,18 @@ import psutil import gevent import argparse - import tempfile -from process import find_procs_by_name, get_process_pid_from_file, get_child_processes, get_server_processes -from metrics import AVAILABLE_METRICS, get_metrics +logger = logging.getLogger(__name__) -# TODO - move these variables to config file -TMP_DIR = "/var/folders/04/6_v1bbs55mb_hrpkphh46xcc0000gn/T" # TODO - use tempfile. Currently there is an issue with sudo +from process import find_procs_by_name, get_process_pid_from_file, get_child_processes, \ + get_server_processes, get_server_pidfile +from metrics import AVAILABLE_METRICS, get_metrics + +TMP_DIR = tempfile.gettempdir() METRICS_LOG_FILE = "{}/server_metrics_{}.log".format(TMP_DIR, int(time.time())) -SERVER_PID_FILE = "{}/.model_server.pid".format(TMP_DIR) # MMS specific METRICS_COLLECTOR_PID_FILE = "{}/.metrics_collector.pid".format(TMP_DIR) + MONITOR_INTERVAL = 1 @@ -105,7 +106,7 @@ def start_metric_collector_process(): raise Exception("Performance monitoring script already running. " "Stop it using stop option.") store_metrics_collector_pid() - server_pid = get_process_pid_from_file(SERVER_PID_FILE) + server_pid = get_process_pid_from_file(get_server_pidfile()) server_process = get_server_processes(server_pid) start_metric_collection(server_process, AVAILABLE_METRICS, MONITOR_INTERVAL, None) diff --git a/benchmarks/monitoring/metrics_monitoring_server.py b/benchmarks/monitoring/metrics_monitoring_server.py index c5fc30b28..e4267777b 100644 --- a/benchmarks/monitoring/metrics_monitoring_server.py +++ b/benchmarks/monitoring/metrics_monitoring_server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License is located at @@ -11,7 +11,7 @@ # permissions and limitations under the License. """ -Server monitoring script over the socket +Remote server monitoring script """ # pylint: disable=redefined-builtin @@ -20,22 +20,22 @@ from gevent import socket from gevent import select from gevent import monkey + monkey.patch_select() monkey.patch_socket() from metrics_collector import start_metric_collection -from process import find_procs_by_name, get_process_pid_from_file, get_child_processes, get_server_processes +from process import find_procs_by_name, get_process_pid_from_file, \ + get_child_processes, get_server_processes, get_server_pidfile +import configuration + +HOST = str(configuration.get('monitoring','HOST')) +PORT = int(configuration.get('monitoring','PORT', 9009)) -# TODO - move these variables to config -TMP_DIR = "/var/folders/04/6_v1bbs55mb_hrpkphh46xcc0000gn/T" -# TODO - use tempfile. Currently there is an issue with sudo -SERVER_PID_FILE = "{}/.model_server.pid".format(TMP_DIR) # MMS specific -HOST = '' SOCKET_LIST = [] RECV_BUFFER = 4096 -PORT = 9009 -interval = 1 +interval = 1 def perf_server(): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -73,7 +73,7 @@ def perf_server(): send_message(sock, "In-correct interval data") elif data.startswith('metrics'): metrics = data[:-1].split("metrics:")[1].split("\t") - server_pid = get_process_pid_from_file(SERVER_PID_FILE) + server_pid = get_process_pid_from_file(get_server_pidfile()) server_process = get_server_processes(server_pid) start_metric_collection(server_process, metrics, interval, sock) else: diff --git a/benchmarks/monitoring/metrics_monitoring_taurus.py b/benchmarks/monitoring/metrics_monitoring_taurus.py index 5b1406dd8..6985edb15 100644 --- a/benchmarks/monitoring/metrics_monitoring_taurus.py +++ b/benchmarks/monitoring/metrics_monitoring_taurus.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License is located at @@ -11,7 +11,7 @@ # permissions and limitations under the License. """ -Taurus local plugin for server monitoring. +Taurus Local plugin for server monitoring. Should be used when server and Taurus are running on same machine. This file should be placed in Python Path along with monitoring package. """ @@ -24,13 +24,8 @@ from bzt.six import PY3 from metrics import get_metrics, AVAILABLE_METRICS as AVAILABLE_SERVER_METRICS -from process import get_process_pid_from_file, get_server_processes, get_child_processes - -# TODO - move these variables to config -TMP_DIR = "/var/folders/04/6_v1bbs55mb_hrpkphh46xcc0000gn/T" -# TODO - use tempfile. Currently there is an issue with sudo -SERVER_PID_FILE = "{}/.model_server.pid".format(TMP_DIR) # MMS specific - +from process import get_process_pid_from_file, get_server_processes, \ + get_child_processes, get_server_pidfile class Monitor(monitoring.Monitoring): def __init__(self): @@ -78,8 +73,8 @@ def connect(self): class ServerLocalMonitor(monitoring.LocalMonitor): def _calc_resource_stats(self, interval): - result =super()._calc_resource_stats(interval) - server_pid = get_process_pid_from_file(SERVER_PID_FILE) + result = super()._calc_resource_stats(interval) + server_pid = get_process_pid_from_file(get_server_pidfile()) server_process = get_server_processes(server_pid) result.update(get_metrics(server_process, get_child_processes(server_process))) return result diff --git a/benchmarks/monitoring/process.py b/benchmarks/monitoring/process.py index 7cdf09258..641e24a1c 100644 --- a/benchmarks/monitoring/process.py +++ b/benchmarks/monitoring/process.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License is located at @@ -17,7 +17,8 @@ import os import psutil - +import tempfile +import configuration def find_procs_by_name(name): """Return a list of processes matching 'name'.""" @@ -64,4 +65,8 @@ def get_server_processes(server_process_pid): except Exception as e: print("Server process not found. Error: {}".format(str(e))) raise - return server_process \ No newline at end of file + return server_process + + +def get_server_pidfile(): + return "{0}/.{1}".format(tempfile.gettempdir(), configuration.get('server', 'pid_file', 'model_server.pid')) diff --git a/benchmarks/monitoring/requirements.txt b/benchmarks/monitoring/requirements.txt new file mode 100644 index 000000000..53e3cabc4 --- /dev/null +++ b/benchmarks/monitoring/requirements.txt @@ -0,0 +1 @@ +gevent==20.5.2 \ No newline at end of file diff --git a/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml b/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml index 64cbccb20..4da037c69 100644 --- a/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml +++ b/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml @@ -34,10 +34,9 @@ modules: services: - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - sum_memory_percent - + - interval: 1s + metrics: + - cpu + - disk-space + - mem + - sum_memory_percent \ No newline at end of file diff --git a/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml b/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml index 4ae1e84a7..d27aa86f2 100644 --- a/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml +++ b/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml @@ -36,17 +36,17 @@ services: ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - interval: 1s metrics: - - cpu - - disk-space - - mem - - sum_memory_percent + - cpu + - disk-space + - mem + - sum_memory_percent reporting: - module: passfail criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_memory_percent - condition: '>' - threshold: 14 - timeframe: 1s + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_memory_percent + condition: '>' + threshold: 14 + timeframe: 1s From 80e6eaa1c67bccc5863bce5522ac2dcc531ece7f Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 5 Jun 2020 18:32:13 +0530 Subject: [PATCH 010/329] Circleci project setup (#1) circleci config, local execution, scripts, newman tests, tautus --- .circleci/config.yml | 139 +++ .circleci/images/Dockerfile.python3.6 | 50 + .circleci/scripts/linux_build.sh | 3 + .circleci/scripts/linux_test_api.sh | 77 ++ .circleci/scripts/linux_test_benchmark.sh | 14 + .circleci/scripts/linux_test_python.sh | 3 + .circleci/scripts/linux_test_taurus.sh | 17 + run_circleci_tests.py | 83 ++ taurus/imageInputModelPlan.jmx.yaml | 51 + test/README.md | 59 ++ test/postman/environment.json | 44 + test/postman/https_test_collection.json | 335 ++++++ .../inference_api_test_collection.json | 204 ++++ test/postman/inference_data.json | 898 +++++++++++++++++ .../management_api_test_collection.json | 952 ++++++++++++++++++ test/regression_tests.sh | 100 ++ test/resources/certs.pem | 16 + test/resources/config.properties | 4 + test/resources/key.pem | 15 + test/resources/kitten.jpg | Bin 0 -> 110969 bytes test/screenshot/postman.png | Bin 0 -> 135879 bytes 21 files changed, 3064 insertions(+) create mode 100644 .circleci/config.yml create mode 100644 .circleci/images/Dockerfile.python3.6 create mode 100755 .circleci/scripts/linux_build.sh create mode 100755 .circleci/scripts/linux_test_api.sh create mode 100755 .circleci/scripts/linux_test_benchmark.sh create mode 100755 .circleci/scripts/linux_test_python.sh create mode 100755 .circleci/scripts/linux_test_taurus.sh create mode 100644 run_circleci_tests.py create mode 100644 taurus/imageInputModelPlan.jmx.yaml create mode 100644 test/README.md create mode 100644 test/postman/environment.json create mode 100644 test/postman/https_test_collection.json create mode 100644 test/postman/inference_api_test_collection.json create mode 100644 test/postman/inference_data.json create mode 100644 test/postman/management_api_test_collection.json create mode 100755 test/regression_tests.sh create mode 100644 test/resources/certs.pem create mode 100644 test/resources/config.properties create mode 100644 test/resources/key.pem create mode 100644 test/resources/kitten.jpg create mode 100644 test/screenshot/postman.png diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..006a7d8e8 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,139 @@ +version: 2.1 + + +executors: + docker-python-3: + docker: + - image: prashantsail/mms-build:latest + environment: + _JAVA_OPTIONS: "-Xmx2048m" + + +commands: + attach-mms-workspace: + description: "Attach the MMS directory which was saved into workspace" + steps: + - attach_workspace: + at: . + + install-mms-server: + description: "Install MMS server from a wheel" + steps: + - run: + name: Install MMS + command: pip install dist/*.whl + + exeucute-api-tests: + description: "Execute API tests from a collection" + parameters: + collection: + type: enum + enum: [management, inference, https] + default: management + steps: + - run: + name: Start MMS, Execute << parameters.collection >> API Tests, Stop MMS + command: .circleci/scripts/linux_test_api.sh << parameters.collection >> + - store_artifacts: + name: Store server logs from << parameters.collection >> API tests + path: mms_<< parameters.collection >>.log + - store_artifacts: + name: Store << parameters.collection >> API test results + path: test/<< parameters.collection >>-api-report.html + + +jobs: + build: + executor: docker-python-3 + steps: + - checkout + - run: + name: Build frontend + command: .circleci/scripts/linux_build.sh + - run: + name: Optimize before moving to workspace + command: rm -rf .git # Save disk space & time required for copy to\from workspace + - store_artifacts: + name: Store gradle testng results + path: frontend/server/build/reports/tests/test + - persist_to_workspace: + root: . + paths: + - . + + python_tests: + executor: docker-python-3 + steps: + - attach-mms-workspace + - run: + name: Execute python unit tests + command: .circleci/scripts/linux_test_python.sh + - store_artifacts: + name: Store python Test results + path: htmlcov + + api_tests: + executor: docker-python-3 + steps: + - attach-mms-workspace + - install-mms-server + - exeucute-api-tests: + collection: management + - exeucute-api-tests: + collection: inference + - exeucute-api-tests: + collection: https + + benchmark: + executor: docker-python-3 + steps: + - attach-mms-workspace + - install-mms-server + - run: + name: Start MMS, Execute benchmark tests, Stop MMS + command: .circleci/scripts/linux_test_benchmark.sh + - store_artifacts: + name: Store server logs from benchmark tests + path: mms.log + - store_artifacts: + name: Store Benchmark Latency resnet-18 results + path: /tmp/MMSBenchmark/out/latency/resnet-18/report/ + destination: benchmark-latency-resnet-18 + + taurus: + executor: docker-python-3 + steps: + - attach-mms-workspace + - install-mms-server + - run: + name: Install Taurus + command: pip install bzt + - run: + name: Start MMS, Execute Taurus testcases, Stop MMS + command: .circleci/scripts/linux_test_taurus.sh + - store_artifacts: + name: Store server logs from Taurus tests + path: mms.log + - store_artifacts: + name: Store Taurus results + path: /tmp/cci-taurus/ + destination: taurus-results + + +workflows: + version: 2 + build_and_test: + jobs: + - build + - python_tests: + requires: + - build + - api_tests: + requires: + - build + - benchmark: + requires: + - build + - taurus: + requires: + - build \ No newline at end of file diff --git a/.circleci/images/Dockerfile.python3.6 b/.circleci/images/Dockerfile.python3.6 new file mode 100644 index 000000000..9105dea95 --- /dev/null +++ b/.circleci/images/Dockerfile.python3.6 @@ -0,0 +1,50 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. +# + +FROM awsdeeplearningteam/mms-build:python3.6@sha256:2c1afa8834907ceec641d254dffbf4bcc659ca2d00fd6f2872d7521f32c9fa2e + +# 2020 - Updated Build and Test dependencies + +# Python packages for MMS Server +RUN pip install psutil \ + && pip install future \ + && pip install Pillow \ + && pip install wheel \ + && pip install twine \ + && pip install requests \ + && pip install mock \ + && pip install numpy \ + && pip install Image \ + && pip install mxnet==1.5.0 + +# Python packages for pytests +RUN pip install pytest==4.0.0 \ + && pip install pytest-cov \ + && pip install pytest-mock + +# Python packages for benchmark +RUN pip install pandas + +# Install NodeJS and packages for API tests +RUN curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - \ + && sudo apt-get install -y nodejs \ + && sudo npm install -g newman newman-reporter-html + +# Install jmeter for benchmark +RUN cd /opt \ + && wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.3.tgz \ + && tar -xzf apache-jmeter-5.3.tgz \ + && cd apache-jmeter-5.3 \ + && ln -s /opt/apache-jmeter-5.3/bin/jmeter /usr/local/bin/jmeter \ + && wget https://jmeter-plugins.org/get/ -O lib/ext/jmeter-plugins-manager-1.4.jar \ + && wget http://search.maven.org/remotecontent?filepath=kg/apc/cmdrunner/2.2/cmdrunner-2.2.jar -O lib/cmdrunner-2.2.jar \ + && java -cp lib/ext/jmeter-plugins-manager-1.4.jar org.jmeterplugins.repository.PluginManagerCMDInstaller \ + && bin/PluginsManagerCMD.sh install jpgc-synthesis=2.1,jpgc-filterresults=2.1,jpgc-mergeresults=2.1,jpgc-cmd=2.1,jpgc-perfmon=2.1 \ No newline at end of file diff --git a/.circleci/scripts/linux_build.sh b/.circleci/scripts/linux_build.sh new file mode 100755 index 000000000..019e629f9 --- /dev/null +++ b/.circleci/scripts/linux_build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +python setup.py bdist_wheel --universal \ No newline at end of file diff --git a/.circleci/scripts/linux_test_api.sh b/.circleci/scripts/linux_test_api.sh new file mode 100755 index 000000000..23f908abb --- /dev/null +++ b/.circleci/scripts/linux_test_api.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +MODEL_STORE_DIR='test/model_store' + +MMS_LOG_FILE_MANAGEMENT='mms_management.log' +MMS_LOG_FILE_INFERENCE='mms_inference.log' +MMS_LOG_FILE_HTTPS='mms_https.log' +MMS_CONFIG_FILE_HTTPS='test/resources/config.properties' + +POSTMAN_ENV_FILE='test/postman/environment.json' +POSTMAN_COLLECTION_MANAGEMENT='test/postman/management_api_test_collection.json' +POSTMAN_COLLECTION_INFERENCE='test/postman/inference_api_test_collection.json' +POSTMAN_COLLECTION_HTTPS='test/postman/https_test_collection.json' +POSTMAN_DATA_FILE_INFERENCE='test/postman/inference_data.json' + +REPORT_FILE_MANAGEMENT='test/management-api-report.html' +REPORT_FILE_INFERENCE='test/inference-api-report.html' +REPORT_FILE_HTTPS='test/https-api-report.html' + +start_mms_server() { + multi-model-server --start --model-store $1 >> $2 2>&1 + sleep 10 +} + +start_mms_secure_server() { + multi-model-server --start --mms-config $MMS_CONFIG_FILE_HTTPS --model-store $1 >> $2 2>&1 + sleep 10 +} + +stop_mms_server() { + multi-model-server --stop +} + +trigger_management_tests(){ + start_mms_server $MODEL_STORE_DIR $MMS_LOG_FILE_MANAGEMENT + newman run -e $POSTMAN_ENV_FILE $POSTMAN_COLLECTION_MANAGEMENT \ + -r cli,html --reporter-html-export $REPORT_FILE_MANAGEMENT --verbose + stop_mms_server +} + +trigger_inference_tests(){ + start_mms_server $MODEL_STORE_DIR $MMS_LOG_FILE_INFERENCE + newman run -e $POSTMAN_ENV_FILE $POSTMAN_COLLECTION_INFERENCE -d $POSTMAN_DATA_FILE_INFERENCE \ + -r cli,html --reporter-html-export $REPORT_FILE_INFERENCE --verbose + stop_mms_server +} + +trigger_https_tests(){ + start_mms_secure_server $MODEL_STORE_DIR $MMS_LOG_FILE_HTTPS + newman run --insecure -e $POSTMAN_ENV_FILE $POSTMAN_COLLECTION_HTTPS \ + -r cli,html --reporter-html-export $REPORT_FILE_HTTPS --verbose + stop_mms_server +} + +mkdir -p $MODEL_STORE_DIR + +case $1 in + 'management') + trigger_management_tests + ;; + 'inference') + trigger_inference_tests + ;; + 'https') + trigger_https_tests + ;; + 'ALL') + trigger_management_tests + trigger_inference_tests + trigger_https_tests + ;; + *) + echo $1 'Invalid' + echo 'Please specify any one of - management | inference | https | ALL' + exit 1 + ;; +esac \ No newline at end of file diff --git a/.circleci/scripts/linux_test_benchmark.sh b/.circleci/scripts/linux_test_benchmark.sh new file mode 100755 index 000000000..73c66f58e --- /dev/null +++ b/.circleci/scripts/linux_test_benchmark.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Hack needed to make it work with existing benchmark.py +# benchmark.py expects jmeter to be present at a very specific location +mkdir -p /home/ubuntu/.linuxbrew/Cellar/jmeter/5.3/libexec/bin/ +ln -s /opt/apache-jmeter-5.3/bin/jmeter /home/ubuntu/.linuxbrew/Cellar/jmeter/5.3/libexec/bin/jmeter + +multi-model-server --start >> mms.log 2>&1 +sleep 30 + +cd benchmarks +python benchmark.py latency + +multi-model-server --stop \ No newline at end of file diff --git a/.circleci/scripts/linux_test_python.sh b/.circleci/scripts/linux_test_python.sh new file mode 100755 index 000000000..15e19c185 --- /dev/null +++ b/.circleci/scripts/linux_test_python.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +python -m pytest --cov-report html:htmlcov --cov=mms/ mms/tests/unit_tests/ \ No newline at end of file diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_taurus.sh new file mode 100755 index 000000000..083849355 --- /dev/null +++ b/.circleci/scripts/linux_test_taurus.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +multi-model-server --start \ + --models squeezenet=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar \ + >> mms.log 2>&1 +sleep 90 + +cd taurus + +curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg +bzt -o modules.jmeter.path=/opt/apache-jmeter-5.3/bin/jmeter \ + -o settings.artifacts-dir=/tmp/cci-taurus/ \ + -o modules.console.disable=true \ + imageInputModelPlan.jmx.yaml \ + -report + +multi-model-server --stop \ No newline at end of file diff --git a/run_circleci_tests.py b/run_circleci_tests.py new file mode 100644 index 000000000..960d0abe5 --- /dev/null +++ b/run_circleci_tests.py @@ -0,0 +1,83 @@ +# Make sure you have following dependencies installed on your local machine +# 1. PyYAML (pip install PyYaml) +# 2. CircleCI cli from - https://circleci.com/docs/2.0/local-cli/#installation +# 3. docker + +import subprocess +import yaml +import copy +from collections import OrderedDict +from functools import reduce + +workflow = 'build_and_test' +job = 'python_tests' + +circleci_config_file = '.circleci/config.yml' +processed_file = 'processed.yml' +processed_config = {} +xformed_file = 'xformed.yml' +xformed_config = {} +xformed_job_name = 'mms_xformed_job' +blacklisted_steps = ['persist_to_workspace', 'attach_workspace', 'store_artifacts'] + +# Create processed YAML using circleci cli's 'config process' commands +subprocess.check_call('circleci config process {} > {}'.format(circleci_config_file, processed_file), shell=True) + +# Read the processed config +with open(processed_file) as fstream: + try: + processed_config = yaml.safe_load(fstream) + except yaml.YAMLError as err: + print(err) + +jobs_in_workflow = processed_config['workflows'][workflow]['jobs'] + +# Recursively iterate over jobs in the workflow to generate an ordered list of parent jobs +def getJobsToExec(job_name): + result = [] + for job in jobs_in_workflow : + if(isinstance(job, str) and job_name == job): + # We have reached a root job (outter most parent), no further traversal + break + elif(isinstance(job, dict) and job_name == list(job)[0]): + # Find all parent jpbs, recurse to find their respective ancestors + parent_jobs = job[job_name].get('requires',[]) + for pjob in parent_jobs : + result += getJobsToExec(pjob) + break + return (result + [job_name]) + +# The list might contain duplicate parent jobs due to muliple fan-ins like config; remove the duplicates +jobs_to_exec = list(OrderedDict.fromkeys(getJobsToExec(job))) + +# Merge all the steps from jobs to be executed +def getJobSteps(result, job_name): + job_steps = processed_config['jobs'][job_name]['steps'] + filtered_job_steps = list(filter(lambda step: list(step)[0] not in blacklisted_steps, job_steps)) + return result + filtered_job_steps + +merged_steps = reduce(getJobSteps, jobs_to_exec, []) + +# Create a new job, using the first job as a reference +# This ensures configs like executor, environment, etc are maintained as it is from the first job +first_job = jobs_to_exec[0] +xformed_job = copy.deepcopy(processed_config['jobs'][first_job]) + +# Add the merged steps to this newly introduced job +xformed_job['steps'] = merged_steps + +# Create a duplicate config(transformed) with the newly introduced job (as the only job in config) +xformed_config = copy.deepcopy(processed_config) +xformed_config['jobs'] = {} +xformed_config['jobs'][xformed_job_name] = xformed_job + +# Create a transformed yaml +with open(xformed_file, 'w+') as fstream: + yaml.dump(xformed_config,fstream) + +# Locally execute the newly created job +# This job has all the steps(ordered and merged) from the mentioned job along with it's ancestors +subprocess.check_call('circleci local execute -c {} --job {}'.format(xformed_file, xformed_job_name), shell=True) + +# Clean up, remove the processed and transformed yml files +subprocess.check_call('rm {} {}'.format(processed_file, xformed_file), shell=True) diff --git a/taurus/imageInputModelPlan.jmx.yaml b/taurus/imageInputModelPlan.jmx.yaml new file mode 100644 index 000000000..c662874d0 --- /dev/null +++ b/taurus/imageInputModelPlan.jmx.yaml @@ -0,0 +1,51 @@ +--- +execution: +- concurrency: 1 + ramp-up: 5s + hold-for: 1m + scenario: Inference +scenarios: + Inference: + default-address: ${__P(protocol,https)}://${__P(hostname,127.0.0.1)}:${__P(port,8443)}/ + requests: + - follow-redirects: true + label: Inference Request + method: POST + upload-files: + - mime-type: image/jpeg + param: data + path: ${__P(input_filepath)} + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} + store-cache: false + store-cookie: false + use-dns-cache-mgr: false + variables: + cnn_url: ${__P(url, https://s3.amazonaws.com/model-server/models/squeezenet_v1.1/squeezenet_v1.1.model)} + model: ${__P(model_name,squeezenet_v1.1)} + scale_down_workers: '0' + scale_up_workers: ${__P(min_workers,1)} +modules: + jmeter: + properties: + input_filepath : kitten.jpg + model_name : squeezenet +services: + - module: monitoring + local: + - interval: 2s + logging: True + metrics: + - cpu + - disk-space + - mem +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: local/cpu + condition: '>' + threshold: 100 + timeframe: 6s +- module: junit-xml + filename: ${TAURUS_ARTIFACTS_DIR}/output/results.xml + data-source: pass-fail diff --git a/test/README.md b/test/README.md new file mode 100644 index 000000000..93cd23621 --- /dev/null +++ b/test/README.md @@ -0,0 +1,59 @@ +# MMS Regression Tests + +This folder contains nightly regression tests execututed against MMS master.These tests use [POSTMAN](https://www.postman.com/downloads/) for exercising all the Management & Inference APIs. + +### Latest Test Run Status + + +### Running the test manually. + +Clone Torch Serve Repo & Build the Docker Image for the execition env. + +``` +git clone https://github.com/awslabs/multi-model-server.git + +run mms docker + +``` + +This would build a docker Image with a pytorch/torchserve:latest in which we would run our Regression Tests. + +``` +docker run -it --user root pytorch/mms:latest /bin/bash +``` + +In the Docker CLI execute the following cmds. + +``` +apt-get update +apt-get install -y git wget sudo +git clone https://github.com/awslabs/multi-model-server.git +cd serve +``` +To execute tests on master run: + +`./test/regression_tests.sh ` + +To execute tests on different run: + +`./test/regression_tests.sh ` + + +You can view the logs for Test execution & the Torch serve in the /tmp dir. + +``` +cat /tmp/test_exec.log +cat /tmp/ts.log +``` + +### Adding tests + +To add to the tests, import a collection (in /postman) to Postman and add new requests. +Specifically to test for inference against a new model +* Open /postman/inference_data.json +* Add new json object with the new model url and payload. + +![POSTMAN UI](screenshot/postman.png) + +Afterwards, export the collection as a v2.1 collection and replace the existing exported collection. +To add a new suite of tests, add a new collection to /postman and update regression_tests.sh to run the new collection and buldsepc.yml to keep track of the report. diff --git a/test/postman/environment.json b/test/postman/environment.json new file mode 100644 index 000000000..920aef558 --- /dev/null +++ b/test/postman/environment.json @@ -0,0 +1,44 @@ +{ + "id": "b9eb6b86-585b-4fca-abd0-972ec686e3e8", + "name": "Multi-Model-server", + "values": [ + { + "key": "hostname", + "value": "localhost", + "enabled": true + }, + { + "key": "protocol", + "value": "http", + "enabled": true + }, + { + "key": "mgmt-port", + "value": "8081", + "enabled": true + }, + { + "key": "model-name", + "value": "densenet161", + "enabled": true + }, + { + "key": "pred-port", + "value": "8080", + "enabled": true + }, + { + "key": "sec-mgmt-port", + "value": "8444", + "enabled": true + }, + { + "key": "sec-pred-port", + "value": "8443", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2020-05-15T07:34:40.974Z", + "_postman_exported_using": "Postman/7.24.0" +} \ No newline at end of file diff --git a/test/postman/https_test_collection.json b/test/postman/https_test_collection.json new file mode 100644 index 000000000..175f1bc42 --- /dev/null +++ b/test/postman/https_test_collection.json @@ -0,0 +1,335 @@ +{ + "info": { + "_postman_id": "cd002524-ce12-4ff7-ab75-932a05403f83", + "name": "MMS - https_test_collection", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "HTTPS Inference API Description", + "event": [ + { + "listen": "test", + "script": { + "id": "638ec081-ebf4-4634-a9ea-f675613c2127", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "OPTIONS", + "header": [], + "url": { + "raw": "https://{{hostname}}:{{sec-pred-port}}", + "protocol": "https", + "host": [ + "{{hostname}}" + ], + "port": "{{sec-pred-port}}" + } + }, + "response": [] + }, + { + "name": "HTTPS Management API Description", + "event": [ + { + "listen": "test", + "script": { + "id": "81b8730a-0b89-4569-b042-1076266563ba", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "OPTIONS", + "header": [], + "url": { + "raw": "https://{{hostname}}:{{sec-pred-port}}", + "protocol": "https", + "host": [ + "{{hostname}}" + ], + "port": "{{sec-pred-port}}" + } + }, + "response": [] + }, + { + "name": "HTTPS Register Model - SqueezeNet", + "event": [ + { + "listen": "test", + "script": { + "id": "7e9a9528-e9f1-446c-b1ba-7dce112ffa30", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "https://{{hostname}}:{{sec-mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar&model_name=squeezenetv1_1&initial_workers=1&synchronous=true", + "protocol": "https", + "host": [ + "{{hostname}}" + ], + "port": "{{sec-mgmt-port}}", + "path": [ + "models" + ], + "query": [ + { + "key": "url", + "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + }, + { + "key": "model_name", + "value": "squeezenetv1_1" + }, + { + "key": "initial_workers", + "value": "1" + }, + { + "key": "synchronous", + "value": "true" + } + ] + } + }, + "response": [] + }, + { + "name": "HTTPS Get SqueezeNet Model Description", + "event": [ + { + "listen": "test", + "script": { + "id": "cd11b9cc-335a-415a-8315-54ddda6f6d8a", + "exec": [ + "pm.test(\"Successful GET request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "https://{{hostname}}:{{sec-mgmt-port}}/models/squeezenetv1_1", + "protocol": "https", + "host": [ + "{{hostname}}" + ], + "port": "{{sec-mgmt-port}}", + "path": [ + "models", + "squeezenetv1_1" + ] + } + }, + "response": [] + }, + { + "name": "HTTPS Scale up Workers - Synchronous", + "event": [ + { + "listen": "test", + "script": { + "id": "ba06ca94-e630-4f72-adf4-49d1a80ded31", + "exec": [ + "pm.test(\"Successful PUT request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200, 201, 202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "https://{{hostname}}:{{sec-mgmt-port}}/models/squeezenetv1_1?min_worker=5&max_worker=5&synchronous=true", + "protocol": "https", + "host": [ + "{{hostname}}" + ], + "port": "{{sec-mgmt-port}}", + "path": [ + "models", + "squeezenetv1_1" + ], + "query": [ + { + "key": "min_worker", + "value": "5" + }, + { + "key": "max_worker", + "value": "5" + }, + { + "key": "synchronous", + "value": "true" + } + ] + } + }, + "response": [] + }, + { + "name": "HTTPS Scale up Workers - Asynchronous", + "event": [ + { + "listen": "test", + "script": { + "id": "5a13e310-65a9-4982-84c7-d89f29d6c27a", + "exec": [ + "pm.test(\"Successful PUT request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "https://{{hostname}}:{{sec-mgmt-port}}/models/squeezenetv1_1?min_worker=6&max_worker=6&synchronous=false", + "protocol": "https", + "host": [ + "{{hostname}}" + ], + "port": "{{sec-mgmt-port}}", + "path": [ + "models", + "squeezenetv1_1" + ], + "query": [ + { + "key": "min_worker", + "value": "6" + }, + { + "key": "max_worker", + "value": "6" + }, + { + "key": "synchronous", + "value": "false" + } + ] + } + }, + "response": [] + }, + { + "name": "HTTPS - Inference - SqueezeNet", + "event": [ + { + "listen": "test", + "script": { + "id": "7c1c4eaa-48f8-4734-8737-78b4b2766b29", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + "});" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "content-type": true + } + }, + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "file", + "file": { + "src": "../examples/image_classifier/kitten.jpg" + }, + "options": { + "raw": { + "language": "text" + } + } + }, + "url": { + "raw": "https://{{hostname}}:{{sec-pred-port}}/predictions/squeezenetv1_1", + "protocol": "https", + "host": [ + "{{hostname}}" + ], + "port": "{{sec-pred-port}}", + "path": [ + "predictions", + "squeezenetv1_1" + ] + } + }, + "response": [] + }, + { + "name": "HTTPS UnRegister Model SqueezeNet", + "event": [ + { + "listen": "test", + "script": { + "id": "de94e7b6-d4fa-4e10-8b54-4052753de19e", + "exec": [ + "pm.test(\"Successful DELETE request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "https://{{hostname}}:{{sec-mgmt-port}}/models/squeezenetv1_1", + "protocol": "https", + "host": [ + "{{hostname}}" + ], + "port": "{{sec-mgmt-port}}", + "path": [ + "models", + "squeezenetv1_1" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} +} \ No newline at end of file diff --git a/test/postman/inference_api_test_collection.json b/test/postman/inference_api_test_collection.json new file mode 100644 index 000000000..8226030d5 --- /dev/null +++ b/test/postman/inference_api_test_collection.json @@ -0,0 +1,204 @@ +{ + "info": { + "_postman_id": "e69000d9-d3c8-49bd-879a-ad42b95b042a", + "name": "MMS - inference_api_collection", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Model Zoo - Register Model", + "event": [ + { + "listen": "test", + "script": { + "id": "80fa33ea-ff6a-4535-9328-ddffb980062a", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url={{url}}&model_name={{model_name}}&initial_workers={{worker}}&synchronous={{synchronous}}", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models" + ], + "query": [ + { + "key": "url", + "value": "{{url}}" + }, + { + "key": "model_name", + "value": "{{model_name}}" + }, + { + "key": "initial_workers", + "value": "{{worker}}" + }, + { + "key": "synchronous", + "value": "{{synchronous}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Model Zoo - Inference Model", + "event": [ + { + "listen": "test", + "script": { + "id": "d6b1f2cf-6ffb-4850-b276-108f7f65fbd9", + "exec": [ + "var type_response = pm.iterationData.get(\"content-type\");", + "validators = {", + " image_classification: validate_image_classification,", + " default_json: validate_default", + "};", + "", + "pm.test(\"Successful POST request\", function() {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});", + "", + "if (type_response === \"text/plain\") {", + " pm.test(\"Test expected TEXT response\", function() {", + " pm.response.to.have.body(pm.iterationData.get(\"expected\"));", + " });", + "", + "} else if (type_response === \"application/json\") {", + " if (pm.iterationData.has(\"validator\")) {", + " var validator = pm.iterationData.get(\"validator\"); ", + " } else {", + " var validator = \"default_json\";", + " }", + " pm.test(\"Test expected JSON response\", function() {", + " var actual_obj = pm.response.json();", + " var expected_obj = pm.iterationData.get(\"expected\");", + " pm.expect(validators[validator](actual_obj, expected_obj)).to.be.true;", + " });", + "", + "}", + "", + "function get_tolerance_value(expected_val) {", + " var tolerance_percent = pm.iterationData.get(\"tolerance\")", + " return (expected_val * tolerance_percent) / 100;", + " ", + "}", + "", + "function validate_image_classification(actual_obj,expected_obj) {", + " if (_.size(expected_obj) != _.size(actual_obj)) {", + " return false;", + " }", + "", + " for (i = 0; i < expected_obj.length; i += 1) {", + " if(actual_obj[i][\"class\"] !== expected_obj[i][\"class\"]){", + " return false", + " }", + "", + " expected_val = expected_obj[i][\"probability\"]", + " actual_val = actual_obj[i][\"probability\"]", + "", + " tolerance_value = get_tolerance_value(expected_val);", + " if (!(Math.abs(expected_val - actual_val) < tolerance_value)) {", + " return false;", + " }", + " }", + " return true;", + "}", + "", + "", + "", + "/* Simple and nested json object can be compared using validate_default when key and value are constant.", + "-Notes-", + "The order of keys within an object may change.", + "If the output is array of objects then the objects compared are positional and cannot change order.", + "*/", + "function validate_default(actual_obj, expected_obj) {", + " return _.isEqual(actual_obj, expected_obj);", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "content-type": true + } + }, + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "file", + "file": { + "src": "{{file}}" + } + }, + "url": { + "raw": "{{protocol}}://{{hostname}}:{{pred-port}}/predictions/{{model_name}}", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{pred-port}}", + "path": [ + "predictions", + "{{model_name}}" + ] + } + }, + "response": [] + }, + { + "name": "Model Zoo - Unregister model", + "event": [ + { + "listen": "test", + "script": { + "id": "a14dd390-4176-45e7-af00-999676685f4a", + "exec": [ + "pm.test(\"Successful DELETE request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/{{model_name}}", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "{{model_name}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} +} \ No newline at end of file diff --git a/test/postman/inference_data.json b/test/postman/inference_data.json new file mode 100644 index 000000000..b4eb7fb8a --- /dev/null +++ b/test/postman/inference_data.json @@ -0,0 +1,898 @@ +[ + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/alexnet.mar", + "model_name":"alexnet", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.4766186773777008, + "class":"n02127052 lynx, catamount" + }, + { + "probability":0.20687074959278107, + "class":"n02128757 snow leopard, ounce, Panthera uncia" + }, + { + "probability":0.135288268327713, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.09019536525011063, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.04814659804105759, + "class":"n02123159 tiger cat" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/caffenet.mar", + "model_name":"caffenet", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.7166884541511536, + "class":"n02127052 lynx, catamount" + }, + { + "probability":0.09750154614448547, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.0745730996131897, + "class":"n02123159 tiger cat" + }, + { + "probability":0.06743090599775314, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.03200334310531616, + "class":"n02128757 snow leopard, ounce, Panthera uncia" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-inception_v1.mar", + "model_name":"inception_v1", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.3833286464214325, + "class":"n02123159 tiger cat" + }, + { + "probability":0.33825597167015076, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.17002010345458984, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.09881989657878876, + "class":"n02127052 lynx, catamount" + }, + { + "probability":0.001223195344209671, + "class":"n02123394 Persian cat" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/inception-bn.mar", + "model_name":"inception-bn", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.5972931981086731, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.30086880922317505, + "class":"n02123159 tiger cat" + }, + { + "probability":0.08586657792329788, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.009726772084832191, + "class":"n02127052 lynx, catamount" + }, + { + "probability":0.0008877559448592365, + "class":"n03598930 jigsaw puzzle" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-mobilenet.mar", + "model_name":"mobilenet", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":216.0907745361328, + "class":"n02948072 candle, taper, wax light" + }, + { + "probability":186.8531494140625, + "class":"n04456115 torch" + }, + { + "probability":178.81483459472656, + "class":"n03347037 fire screen, fireguard" + }, + { + "probability":169.5437469482422, + "class":"n03666591 lighter, light, igniter, ignitor" + }, + { + "probability":145.08824157714844, + "class":"n09472597 volcano" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/nin.mar", + "model_name":"nin", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.978486180305481, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.021282507106661797, + "class":"n02123159 tiger cat" + }, + { + "probability":9.809057519305497e-05, + "class":"n02124075 Egyptian cat" + }, + { + "probability":8.281067130155861e-05, + "class":"n02128385 leopard, Panthera pardus" + }, + { + "probability":2.5670484319562092e-05, + "class":"n02128757 snow leopard, ounce, Panthera uncia" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-152.mar", + "model_name":"resnet-152", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.7149006128311157, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.22877003252506256, + "class":"n02123159 tiger cat" + }, + { + "probability":0.040323738008737564, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.008370742201805115, + "class":"n02127052 lynx, catamount" + }, + { + "probability":0.00067278987262398, + "class":"n02129604 tiger, Panthera tigris" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar", + "model_name":"resnet-18", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.3633013665676117, + "class":"n02123159 tiger cat" + }, + { + "probability":0.2988913953304291, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.18073132634162903, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.07343611121177673, + "class":"n02127052 lynx, catamount" + }, + { + "probability":0.02035967819392681, + "class":"n02128757 snow leopard, ounce, Panthera uncia" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/resnext-101-64x4d.mar", + "model_name":"resnext101", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.5961099863052368, + "class":"n02123159 tiger cat" + }, + { + "probability":0.4005117118358612, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.0012956340797245502, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.0011538631515577435, + "class":"n04074963 remote control, remote" + }, + { + "probability":0.000405249185860157, + "class":"n04286575 spotlight, spot" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet18v1.mar", + "model_name":"resnet18-v1", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":16.35452651977539, + "class":"n02999410 chain" + }, + { + "probability":16.15776824951172, + "class":"n10148035 groom, bridegroom" + }, + { + "probability":15.857562065124512, + "class":"n04141076 sax, saxophone" + }, + { + "probability":15.083999633789062, + "class":"n04507155 umbrella" + }, + { + "probability":14.938633918762207, + "class":"n09229709 bubble" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet34v1.mar", + "model_name":"resnet34-v1", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":12.617600440979004, + "class":"n04286575 spotlight, spot" + }, + { + "probability":10.216148376464844, + "class":"n03637318 lampshade, lamp shade" + }, + { + "probability":9.676478385925293, + "class":"n03942813 ping-pong ball" + }, + { + "probability":9.529446601867676, + "class":"n02708093 analog clock" + }, + { + "probability":9.494606018066406, + "class":"n03691459 loudspeaker, speaker, speaker unit, loudspeaker system, speaker system" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet50v1.mar", + "model_name":"resnet50-v1", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":177.36087036132812, + "class":"n03982430 pool table, billiard table, snooker table" + }, + { + "probability":174.36256408691406, + "class":"n03942813 ping-pong ball" + }, + { + "probability":172.44488525390625, + "class":"n03661043 library" + }, + { + "probability":163.6439971923828, + "class":"n02788148 bannister, banister, balustrade, balusters, handrail" + }, + { + "probability":159.4976043701172, + "class":"n03065424 coil, spiral, volute, whorl, helix" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet101v1.mar", + "model_name":"resnet101-v1", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":107.36915588378906, + "class":"n02823428 beer bottle" + }, + { + "probability":99.51375579833984, + "class":"n04485082 tripod" + }, + { + "probability":95.99050903320312, + "class":"n04069434 reflex camera" + }, + { + "probability":84.3740463256836, + "class":"n04557648 water bottle" + }, + { + "probability":83.7496566772461, + "class":"n02841315 binoculars, field glasses, opera glasses" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet152v1.mar", + "model_name":"resnet152-v1", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":61.71363067626953, + "class":"n07930864 cup" + }, + { + "probability":60.88921356201172, + "class":"n03832673 notebook, notebook computer" + }, + { + "probability":60.11431121826172, + "class":"n03691459 loudspeaker, speaker, speaker unit, loudspeaker system, speaker system" + }, + { + "probability":53.656005859375, + "class":"n07920052 espresso" + }, + { + "probability":53.115779876708984, + "class":"n03492542 hard disc, hard disk, fixed disk" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet18v2.mar", + "model_name":"resnet18-v2", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":24.102333068847656, + "class":"n04553703 washbasin, handbasin, washbowl, lavabo, wash-hand basin" + }, + { + "probability":23.69866943359375, + "class":"n04254120 soap dispenser" + }, + { + "probability":19.214643478393555, + "class":"n02747177 ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin" + }, + { + "probability":18.93875503540039, + "class":"n03691459 loudspeaker, speaker, speaker unit, loudspeaker system, speaker system" + }, + { + "probability":18.90488052368164, + "class":"n04201297 shoji" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet34v2.mar", + "model_name":"resnet34-v2", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":9.2193603515625, + "class":"n02708093 analog clock" + }, + { + "probability":7.803028583526611, + "class":"n03782006 monitor" + }, + { + "probability":7.681037425994873, + "class":"n04286575 spotlight, spot" + }, + { + "probability":7.129834175109863, + "class":"n03028079 church, church building" + }, + { + "probability":7.0597100257873535, + "class":"n04152593 screen, CRT screen" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet50v2.mar", + "model_name":"resnet50-v2", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":15.76949691772461, + "class":"n06359193 web site, website, internet site, site" + }, + { + "probability":14.25102710723877, + "class":"n07565083 menu" + }, + { + "probability":13.321331024169922, + "class":"n03944341 pinwheel" + }, + { + "probability":11.99173641204834, + "class":"n06596364 comic book" + }, + { + "probability":11.768353462219238, + "class":"n03291819 envelope" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet101v2.mar", + "model_name":"resnet101-v2", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":7.887596130371094, + "class":"n04380533 table lamp" + }, + { + "probability":7.870771884918213, + "class":"n03627232 knot" + }, + { + "probability":7.605418682098389, + "class":"n02093859 Kerry blue terrier" + }, + { + "probability":7.55618143081665, + "class":"n04033995 quilt, comforter, comfort, puff" + }, + { + "probability":7.256267547607422, + "class":"n03884397 panpipe, pandean pipe, syrinx" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet152v2.mar", + "model_name":"resnet152-v2", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":36.13174819946289, + "class":"n03970156 plunger, plumber's helper" + }, + { + "probability":29.110092163085938, + "class":"n02708093 analog clock" + }, + { + "probability":28.711875915527344, + "class":"n04152593 screen, CRT screen" + }, + { + "probability":28.073928833007812, + "class":"n01930112 nematode, nematode worm, roundworm" + }, + { + "probability":28.058176040649414, + "class":"n04404412 television, television system" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/shufflenet.mar", + "model_name":"shufflenet", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.0010000000474974513, + "class":"n03792972 mountain tent" + }, + { + "probability":0.0010000000474974513, + "class":"n03773504 missile" + }, + { + "probability":0.0010000000474974513, + "class":"n03775071 mitten" + }, + { + "probability":0.0010000000474974513, + "class":"n03775546 mixing bowl" + }, + { + "probability":0.0010000000474974513, + "class":"n03776460 mobile home, manufactured home" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-squeezenet.mar", + "model_name":"onnx-squeezenet", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.6482629179954529, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.2318260818719864, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.10045160353183746, + "class":"n02123159 tiger cat" + }, + { + "probability":0.013487360440194607, + "class":"n02127052 lynx, catamount" + }, + { + "probability":0.003135664388537407, + "class":"n02128757 snow leopard, ounce, Panthera uncia" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar", + "model_name":"squeezenet_v1.1", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.8582231402397156, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.09159984439611435, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.03748767822980881, + "class":"n02123159 tiger cat" + }, + { + "probability":0.006165081635117531, + "class":"n02128385 leopard, Panthera pardus" + }, + { + "probability":0.0031715999357402325, + "class":"n02127052 lynx, catamount" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/vgg16.mar", + "model_name":"vgg16", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.8178463578224182, + "class":"n02123159 tiger cat" + }, + { + "probability":0.12500879168510437, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.05412120372056961, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.0020657714921981096, + "class":"n02127052 lynx, catamount" + }, + { + "probability":0.0005614628316834569, + "class":"n02128757 snow leopard, ounce, Panthera uncia" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-vgg16.mar", + "model_name":"onnx-vgg16", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":101.13871765136719, + "class":"n02124075 Egyptian cat" + }, + { + "probability":89.77296447753906, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":88.40411376953125, + "class":"n02123159 tiger cat" + }, + { + "probability":76.17413330078125, + "class":"n02125311 cougar, puma, catamount, mountain lion, painter, panther, Felis concolor" + }, + { + "probability":74.09810638427734, + "class":"n07930864 cup" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-vgg16_bn.mar", + "model_name":"onnx-vgg16_bn", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":16.06477165222168, + "class":"n03196217 digital clock" + }, + { + "probability":13.64653491973877, + "class":"n04286575 spotlight, spot" + }, + { + "probability":13.565534591674805, + "class":"n03692522 loupe, jeweler's loupe" + }, + { + "probability":13.479013442993164, + "class":"n03388043 fountain" + }, + { + "probability":12.639715194702148, + "class":"n03666591 lighter, light, igniter, ignitor" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/vgg19.mar", + "model_name":"vgg19", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.5058671832084656, + "class":"n02123159 tiger cat" + }, + { + "probability":0.28164851665496826, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.20637290179729462, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.0046674045734107494, + "class":"n02127052 lynx, catamount" + }, + { + "probability":0.00011857607023557648, + "class":"n03598930 jigsaw puzzle" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-vgg19.mar", + "model_name":"onnx-vgg19", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":0.3949933350086212, + "class":"n02124075 Egyptian cat" + }, + { + "probability":0.34417903423309326, + "class":"n02123159 tiger cat" + }, + { + "probability":0.25566166639328003, + "class":"n02123045 tabby, tabby cat" + }, + { + "probability":0.0027325130067765713, + "class":"n02127052 lynx, catamount" + }, + { + "probability":0.0002417804644210264, + "class":"n07930864 cup" + } + ], + "tolerance":1 + }, + { + "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-vgg19_bn.mar", + "model_name":"onnx-vgg19_bn", + "worker":1, + "synchronous":"true", + "file":"test/resources/kitten.jpg", + "content-type":"application/json", + "validator":"image_classification", + "expected":[ + { + "probability":17.26873016357422, + "class":"n04589890 window screen" + }, + { + "probability":16.25399398803711, + "class":"n03347037 fire screen, fireguard" + }, + { + "probability":16.093460083007812, + "class":"n04286575 spotlight, spot" + }, + { + "probability":16.02733039855957, + "class":"n04590129 window shade" + }, + { + "probability":15.910074234008789, + "class":"n03637318 lampshade, lamp shade" + } + ], + "tolerance":1 + } +] \ No newline at end of file diff --git a/test/postman/management_api_test_collection.json b/test/postman/management_api_test_collection.json new file mode 100644 index 000000000..35e22a625 --- /dev/null +++ b/test/postman/management_api_test_collection.json @@ -0,0 +1,952 @@ +{ + "info": { + "_postman_id": "5c45fe29-4bb1-4df4-9e13-a4fd410e4775", + "name": "MMS - management_api_collection", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Register Model", + "event": [ + { + "listen": "test", + "script": { + "id": "4f706340-af84-431b-affd-6d6f53c89e80", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar&model_name=squeezenet1_1", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models" + ], + "query": [ + { + "key": "url", + "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + }, + { + "key": "model_name", + "value": "squeezenet1_1" + } + ] + } + }, + "response": [] + }, + { + "name": "Get Valid Model", + "event": [ + { + "listen": "test", + "script": { + "id": "3d6c7158-f729-42cf-a2ec-4e62b7d764d0", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/squeezenet1_1", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "squeezenet1_1" + ] + } + }, + "response": [] + }, + { + "name": "List Models", + "event": [ + { + "listen": "test", + "script": { + "id": "54f1209e-4819-4f06-9d26-f39593a889d9", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models" + ] + } + }, + "response": [] + }, + { + "name": "Scale Min Workers - Asynchronous", + "event": [ + { + "listen": "test", + "script": { + "id": "47ac7b07-04c2-4128-9662-387e26446f7f", + "exec": [ + "pm.test(\"Successful PUT request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/squeezenet1_1?min_worker=3", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "squeezenet1_1" + ], + "query": [ + { + "key": "min_worker", + "value": "3" + } + ] + } + }, + "response": [] + }, + { + "name": "Scale Min Workers - Synchronous", + "event": [ + { + "listen": "test", + "script": { + "id": "3a8cf27e-b0d9-4df9-bdcf-9eadb239807d", + "exec": [ + "pm.test(\"Successful PUT request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/squeezenet1_1?min_worker=4&synchronous=true", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "squeezenet1_1" + ], + "query": [ + { + "key": "min_worker", + "value": "4" + }, + { + "key": "synchronous", + "value": "true" + } + ] + } + }, + "response": [] + }, + { + "name": "Scale Min Workers with GPU", + "event": [ + { + "listen": "test", + "script": { + "id": "b73619d3-3abc-4628-8e52-ecbcda561191", + "exec": [ + "pm.test(\"Successful PUT request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/squeezenet1_1?min_worker=6&number_gpu=1", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "squeezenet1_1" + ], + "query": [ + { + "key": "min_worker", + "value": "6" + }, + { + "key": "number_gpu", + "value": "1" + } + ] + } + }, + "response": [] + }, + { + "name": "UnRegister Model", + "event": [ + { + "listen": "test", + "script": { + "id": "0e10d089-7183-4adf-ba81-d14678f5ecc7", + "exec": [ + "pm.test(\"Successful DELETE request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/squeezenet1_1", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "squeezenet1_1" + ] + } + }, + "response": [] + }, + { + "name": "Register Model with Additional Params", + "event": [ + { + "listen": "test", + "script": { + "id": "5f79bffa-f260-440d-9d49-a3553972b8eb", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar&model_name=squeezenet1_1&initial_workers=1&synchronous=true", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models" + ], + "query": [ + { + "key": "url", + "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + }, + { + "key": "model_name", + "value": "squeezenet1_1" + }, + { + "key": "initial_workers", + "value": "1" + }, + { + "key": "synchronous", + "value": "true" + } + ] + } + }, + "response": [] + }, + { + "name": "Register Model Synchronous", + "event": [ + { + "listen": "test", + "script": { + "id": "74b42a61-dda4-4d70-9543-e61a5be2dd9b", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar&model_name=resnet18&synchronous=true", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models" + ], + "query": [ + { + "key": "url", + "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar" + }, + { + "key": "model_name", + "value": "resnet18" + }, + { + "key": "synchronous", + "value": "true" + } + ] + } + }, + "response": [] + }, + { + "name": "UnRegister Model", + "event": [ + { + "listen": "test", + "script": { + "id": "d74f53dc-5aac-4afc-8acf-849a309054e5", + "exec": [ + "pm.test(\"Successful DELETE request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "resnet18" + ] + } + }, + "response": [] + }, + { + "name": "Register Resnet Model", + "event": [ + { + "listen": "test", + "script": { + "id": "11206673-aec5-4d26-a06c-96fab1fc81f3", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar&model_name=resnet18&synchronous=false", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models" + ], + "query": [ + { + "key": "url", + "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar" + }, + { + "key": "model_name", + "value": "resnet18" + }, + { + "key": "synchronous", + "value": "false" + } + ] + } + }, + "response": [] + }, + { + "name": "List with Limit", + "event": [ + { + "listen": "test", + "script": { + "id": "55acdbb9-cae8-4ad1-b2a4-b50a3549e8be", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?limit=1", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models" + ], + "query": [ + { + "key": "limit", + "value": "1" + } + ] + } + }, + "response": [] + }, + { + "name": "List with Pagination", + "event": [ + { + "listen": "test", + "script": { + "id": "d1b17cfc-90af-4b61-af07-081224ba7f81", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?limit=1&next_page_token=1", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models" + ], + "query": [ + { + "key": "limit", + "value": "1" + }, + { + "key": "next_page_token", + "value": "1" + } + ] + } + }, + "response": [] + }, + { + "name": "Update GPU Count", + "event": [ + { + "listen": "test", + "script": { + "id": "e00d8108-3ce9-4a02-9fe1-3e38907d7f7b", + "exec": [ + "pm.test(\"Successful PUT request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?number_gpu=10", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "resnet18" + ], + "query": [ + { + "key": "number_gpu", + "value": "10" + } + ] + } + }, + "response": [] + }, + { + "name": "Scale up Workers - Synchronous", + "event": [ + { + "listen": "test", + "script": { + "id": "f575937f-1665-4cf4-91a7-31c351eb9424", + "exec": [ + "pm.test(\"Successful PUT request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?min_worker=5&max_worker=5&synchronous=true", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "resnet18" + ], + "query": [ + { + "key": "min_worker", + "value": "5" + }, + { + "key": "max_worker", + "value": "5" + }, + { + "key": "synchronous", + "value": "true" + } + ] + } + }, + "response": [] + }, + { + "name": "Scale up Workers - Asynchronous", + "event": [ + { + "listen": "test", + "script": { + "id": "a65c2a28-de18-4ac5-a893-9aa2cf3cef77", + "exec": [ + "pm.test(\"Successful PUT request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?min_worker=6&max_worker=6&synchronous=false", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "resnet18" + ], + "query": [ + { + "key": "min_worker", + "value": "6" + }, + { + "key": "max_worker", + "value": "6" + }, + { + "key": "synchronous", + "value": "false" + } + ] + } + }, + "response": [] + }, + { + "name": "Update Timeout to -1", + "event": [ + { + "listen": "test", + "script": { + "id": "96e4be74-20d4-4343-b950-2738d6034341", + "exec": [ + "pm.test(\"Successful PUT request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?timeout=-1", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "resnet18" + ], + "query": [ + { + "key": "timeout", + "value": "-1" + } + ] + } + }, + "response": [] + }, + { + "name": "Update Timeout to 0", + "event": [ + { + "listen": "test", + "script": { + "id": "d2c89755-a457-48ef-8dea-fdd61e88d1e5", + "exec": [ + "pm.test(\"Successful PUT request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?timeout=0", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "resnet18" + ], + "query": [ + { + "key": "timeout", + "value": "0" + } + ] + } + }, + "response": [] + }, + { + "name": "Register Model - Invalid URL", + "event": [ + { + "listen": "test", + "script": { + "id": "427b68f9-de96-49b8-9477-450fa580e257", + "exec": [ + "pm.test(\"Invalid URL POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([400]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/invalid-resnet-18.mar&model_name=invalid-resnet18", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models" + ], + "query": [ + { + "key": "url", + "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/invalid-resnet-18.mar" + }, + { + "key": "model_name", + "value": "invalid-resnet18" + } + ] + } + }, + "response": [] + }, + { + "name": "Get Model - Invalid Model", + "event": [ + { + "listen": "test", + "script": { + "id": "36836026-4477-493b-898a-b642628a51d3", + "exec": [ + "pm.test(\"Valid ERROR message\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([404]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/invalid_squeezenet1_1", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "invalid_squeezenet1_1" + ] + } + }, + "response": [] + }, + { + "name": "List Models - Invalid Next Page Token", + "event": [ + { + "listen": "test", + "script": { + "id": "ccc07f12-6f45-4213-b4f5-e37f7d0ceadb", + "exec": [ + "pm.test(\"Successful POST request\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?next_page_token=12", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models" + ], + "query": [ + { + "key": "next_page_token", + "value": "12" + } + ] + } + }, + "response": [] + }, + { + "name": "Update Worker with Invalid Worker Count", + "event": [ + { + "listen": "test", + "script": { + "id": "ec7d233e-54bf-456a-a148-811385a34cc4", + "exec": [ + "pm.test(\"Valid ERROR message\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([400]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?min_worker=10&max_worker=9", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "resnet18" + ], + "query": [ + { + "key": "min_worker", + "value": "10" + }, + { + "key": "max_worker", + "value": "9" + } + ] + } + }, + "response": [] + }, + { + "name": "UnRegister Invalid Model Name", + "event": [ + { + "listen": "test", + "script": { + "id": "08052d51-fd23-4249-a17f-933e557bdacc", + "exec": [ + "pm.test(\"Valid ERROR message\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([404]);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/invalid_squeezenet1_1", + "protocol": "{{protocol}}", + "host": [ + "{{hostname}}" + ], + "port": "{{mgmt-port}}", + "path": [ + "models", + "invalid_squeezenet1_1" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/test/regression_tests.sh b/test/regression_tests.sh new file mode 100755 index 000000000..4ad53b7d1 --- /dev/null +++ b/test/regression_tests.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +set -x +set -e + +MMS_REPO="https://github.com/awslabs/multi-model-server.git" +BRANCH=${1:-master} +ROOT_DIR="/workspace/" +CODEBUILD_WD=$(pwd) +MODEL_STORE=$ROOT_DIR"/model_store" +MMS_LOG_FILE="/tmp/mms.log" +TEST_EXECUTION_LOG_FILE="/tmp/test_exec.log" + +install_mms_from_source() { + echo "Cloning & Building Multi Model Server Repo from " $1 + + sudo apt-get -y install nodejs-dev node-gyp libssl1.0-dev + sudo apt-get -y install npm + sudo npm install -g n + sudo n latest + export PATH="$PATH" + sudo npm install -g newman newman-reporter-html + pip install mxnet-mkl + # Clone & Build MMS + echo "Installing MMS from source" + git clone -b $2 $1 + cd multi-model-server + pip install . + cd - + echo "MMS Succesfully installed" + +} + + +start_mms() { + + # Start MMS with Model Store + multi-model-server --start --model-store $1 &>> $2 + sleep 10 + curl http://127.0.0.1:8081/models + +} + +stop_mms_serve() { + multi-model-server --stop +} + +start_secure_mms() { + + # Start MMS with Model Store + multi-model-server --start --mms-config resources/config.properties --model-store $1 &>> $2 + sleep 10 + curl --insecure -X GET https://127.0.0.1:8444/models +} + + +run_postman_test() { + # Run Postman Scripts + mkdir $ROOT_DIR/report/ + cd $CODEBUILD_WD/test/ + set +e + # Run Management API Tests + stop_mms_serve + start_mms $MODEL_STORE $MMS_LOG_FILE + newman run -e postman/environment.json --bail --verbose postman/management_api_test_collection.json \ + -r cli,html --reporter-html-export $ROOT_DIR/report/management_report.html >>$1 2>&1 + + # Run Inference API Tests after Restart + stop_mms_serve + start_mms $MODEL_STORE $MMS_LOG_FILE + newman run -e postman/environment.json --bail --verbose postman/inference_api_test_collection.json \ + -d postman/inference_data.json -r cli,html --reporter-html-export $ROOT_DIR/report/inference_report.html >>$1 2>&1 + + + # Run Https test cases + stop_mms_serve + start_secure_mms $MODEL_STORE $MMS_LOG_FILE + newman run --insecure -e postman/environment.json --bail --verbose postman/https_test_collection.json \ + -r cli,html --reporter-html-export $ROOT_DIR/report/MMS_https_test_report.html >>$1 2>&1 + + stop_mms_serve + set -e + cd - +} + + +sudo rm -rf $ROOT_DIR && sudo mkdir $ROOT_DIR +sudo chown -R $USER:$USER $ROOT_DIR +cd $ROOT_DIR +mkdir $MODEL_STORE + +sudo rm -f $TEST_EXECUTION_LOG_FILE $MMS_LOG_FILE + +echo "** Execuing MMS Regression Test Suite executon for " $MMS_REPO " **" + +install_mms_from_source $MMS_REPO $BRANCH +run_postman_test $TEST_EXECUTION_LOG_FILE + +echo "** Tests Complete ** " +exit 0 diff --git a/test/resources/certs.pem b/test/resources/certs.pem new file mode 100644 index 000000000..63de5116d --- /dev/null +++ b/test/resources/certs.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICiDCCAfGgAwIBAgIEeC8zQzANBgkqhkiG9w0BAQsFADB2MQswCQYDVQQGEwJV +UzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJUGFsbyBBbHRvMRIwEAYD +VQQKEwlBbWF6b24gQUkxDjAMBgNVBAsTBU14TmV0MRowGAYDVQQDExFtbXMuYW1h +em9uYXdzLmNvbTAgFw0xODA2MjAwMjExMjhaGA8yMTE3MDExMjAyMTEyOFowdjEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVBhbG8g +QWx0bzESMBAGA1UEChMJQW1hem9uIEFJMQ4wDAYDVQQLEwVNeE5ldDEaMBgGA1UE +AxMRbW1zLmFtYXpvbmF3cy5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +AMcbCEP6kn9pUcap5+kYO/5xEl7SL965gSQ2TbFrVv+sLVkLSK8yTtcILr7RUINz +FsD151Q7VyQCvpVzkOFew2s2mAFWWxPJYmxo1j/R3IkJakrrTrMy1R3jsqOQMrxY +TLGR5LIe2pjdAnb9xWe2NB125619WDG7RrdHWZDfvSPxAgMBAAGjITAfMB0GA1Ud +DgQWBBRWjdEyNchYAkdPoyudKJY9YP3JPzANBgkqhkiG9w0BAQsFAAOBgQBMAvqG +cqvD3ColO2Ihgb/LCfCdV14e1YhusVFeKyZkSKFYyQR+MoBOxqMQqJ24gVzgqTU/ +h+LkMqZcxxJAME08BzPgP5b06DBM4K0o0XUfYUViFpYXB0qCG5CA/0S7ONldBGaZ +fv6JrnQ/a1NYBi92AaqXA4VmuaowWLVEFuPV1A== +-----END CERTIFICATE----- diff --git a/test/resources/config.properties b/test/resources/config.properties new file mode 100644 index 000000000..ce6e2f3b9 --- /dev/null +++ b/test/resources/config.properties @@ -0,0 +1,4 @@ +inference_address=https://127.0.0.1:8443 +management_address=https://127.0.0.1:8444 +private_key_file=test/resources/key.pem +certificate_file=test/resources/certs.pem diff --git a/test/resources/key.pem b/test/resources/key.pem new file mode 100644 index 000000000..20c5f68db --- /dev/null +++ b/test/resources/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDHGwhD+pJ/aVHGqefpGDv+cRJe0i/euYEkNk2xa1b/rC1ZC0iv +Mk7XCC6+0VCDcxbA9edUO1ckAr6Vc5DhXsNrNpgBVlsTyWJsaNY/0dyJCWpK606z +MtUd47KjkDK8WEyxkeSyHtqY3QJ2/cVntjQdduetfVgxu0a3R1mQ370j8QIDAQAB +AoGANRoxlyfSQKcPR2PzVUjAX3k6xA1c9RMWrVjKWeJd/qymH5SR2yAYxOMKzJu4 +1IYycF5lRyLYd+M/f06mOmVyysH3D7hkrNz57Z07UrZ0dO/mmUKRL7zc44mo22ck +JtQRwWJMplgew7N8OyqEZbcLOpahjlkL4+KZIWOuO7X5m30CQQDob/rzNY8gfhEm +oEHHQ4dCqa/b5as2OqpFoGBZ+iX3dumBf+UKuSHlvEozt4ZMm29DYSjhiGXgLUFw +6NBhWxpXAkEA20oNdGiYAyyGJ6TKkD3FNZYoqB5+E/Cq6c0AACssB4OrJtiGiBFq +R1h5HTEwYMe+ciZ4CI5MvBukjAdlfn7W9wJAXOIqyTe060oVdncB8ivlCFmgweHk +ajZFRq+Q8UPKGjq1kx9VmtRiXFjC2inTjBds/eL8oCuOcmgDR6hxZQYv3wJAcMLv +kECIinlGsvQGRY297wQ7+9dSNaa3/Gmx6mRIy8RlKiCFbUqnP/C6tswoeFu+DqzB +ZITn6IK+ZlMXWaiXmQJBAK7V4rR+4VdpYUu1OqPRxChkcM+Y4Wa985A46/8yoo3i +0PEenvApVzhVzS9jF6WEqIKcffBAmOxaXOn3kmn8w2A= +-----END RSA PRIVATE KEY----- diff --git a/test/resources/kitten.jpg b/test/resources/kitten.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ffcd2be2c674fddc4886117c12f3b29e4367aba8 GIT binary patch literal 110969 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

J66l>z7T20v&BrMUK^shcTlD$)2!& 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

hPAkbbTr(^R-tMK2 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%0DL;n=L?~l?ng&I8MPm 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 z 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%0D>0jKMROl@?0Zb`>KdeJYDW6roP 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 NZ`_ZLr78E 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%0ARQh+aoyvHFMcA@(3;r93j~|SXP>*Sc z({%LBK!1NUa1)&!`rsTY7^C`cZohK^ z1BzyBbhP{oUEG`)nCA~Lx5aEJWlT(rWOm0Y;-BaTd*q65R{LIWUUpph3w`(vYbVX? z>^)*jX?~^IJ8dwhgV`@%G5X#T`+&eSz%tv}*#UQK6W`t4QN0Rf28}jS{23gAKoImA znV1y6TmZ3y9e!XkkhgkjkpE3B5s;X){plhhY@k>`fwK9K0iiDetbX)Dc7Zq0zYpLY zFIch>s4bRsL;DM7v)QV$5K`*kje1q@uc9Q625V$$D&_9(uH4M5g9;1rLk2a%fzU-& ztlb;XonF*Gm^qjL0)f@^z04oWs>?4BOs3J3g!(sE@>f)AyD5I&1w zI;XPu@Dqc8k^5B9ZOV}Q2{0HeMfZ63eDB@Wn^4XVa*YKdrQ2iSa6oER?;ODmh{^4ohTegIN zQhN8^wCImF{;$SNG6-a<#pBw=0FLP=4OA41@uGJrXGT92gbV_3ObrxvSf=DnkTLA$fR2e|lSp2X${>VT2w8N~vpuXgqxvzUA{4aAWUke+r zk?pesUn&}bt(^9{{I?a1H^iW~_cPzjOS!Q;FqHT&3jTJ60vhZyidn@rd}r!jN1lBl zUl_@6cluC?fxML5diA_e|6x!5sy{yf@D?Z(mHd7H>fnSZ$cR68=5~;Jzo(ZptlDzN*5% z>p=mGo(%Sn&RID*@25d+{(-i?fi_|PGqL2#Hs|$jST`X0iIkF30%%!D7#I|Q+h=DU zepIo}w}y=8EA$nCP(rV2BhH^3oP_xF=@Z0D7LY%eeZ&8Z=`PHNkR%!3+}@_L#ekuC zPVQQZM&iBY4`BdqRgSYPZ<%G8k55V>|AAM_WC6SfVk>w_S8wCW+sr4YrXc;ak|UaJ zzH=6-mS;|v>#nR!rToFR0FYnBOOQBR5VhdO0b>1JmX7+6rLI97wk=NFPPr6{6{z85_e6_o@8N{dRhcPoo?C-O1x zU88@x)%LeO32!5un~@`%#iHn3J-H{d=}z88)L%4|-y>7R@PjKrF4H|@cc$WU zI*QA39Wi5ZIuuflnv+3Vxq! zY`lxZW;UKJ9ol6i;g4~1&Gn89O^@(*yCfe7(Hn(Nw^(mW_s(FWH^LYQdJ(v56I)yZ z=6#ZCYMA$Czi1MQfrqoSo%P~SX~dWHyX|C<19SE+IH+@dw~}C_Km|#JHvzx5EKx02 zkpQNk6tmdYf#A2&X0zzJJTL?kkb3heQCNZbKz_eybF~dow!w+h*-Sow-#F&UbWvPe zQs>WAkbFKw4YUjeTu=BK@LL}8MkCxnt1OGs?t;0(BIg5jAT@){GGn@!3<5kYDF*^h z(HDv9a0SWZ(Ct!P+JiO5>3`32-{!PG{bR2U#yu{a)3@QD8yg2x+kYe+n>FaM49?bj zd@Ic^-6qhH@-beWcL`J5qH=pao{5o;YPqfj4C;eyi~&79g2kQjNWxY?B7X=ZHjVwQ z5)U&J(5fK_=>~2zsK?YX2hwQ+<@tNnEf7?p)fvHC;e4dQ;d+`4*A%$%y~#zja}3XVHmXxc1{@Y$e0wai1udop09C~;y$m$sE zP)k*V9K3zE(o5`a5cYpd5+5_hB)gwov&CR zUE#edUo>D}kL1gw71Qkj(Uf_%E3K2|x_nN48)2{fsJ;SI0_GE+0yvL3UQMR6)&K6w z3Dh7$akSu6IEI6Ms`g)ObTJqxu;P6Aw(kEi_SJDwrt9Ct00Ydx0Md;JN~eHyi69aJ z3Mw$Pl$7Am3@xn^(v65nNOz0UDuPH2-O@<&-ke?c?Abm0p5Gs?&+-|WXXd%%y1sQ+ zJsRf%l2kp@PQxeTxk^-l(l<3U?oiN{1fOeV_<)SiKgJw^G(t_`C5ZJ{BpBu?G46a4 zJivaj&pHUBzpDOJNZ7JR>jk%|6Zt)KXfUnV*a~h0ulj9IaL9k;k5m zSfzjboO?6(Zm|)U7hMVjqmC7@z+X99Y5%6kcC6Z|U;+_b;sA75U1ai?=5=zx_d%K@ zS6pJ$;PDP9+;WW+pqrMVqXg@Tmc2>tlB>fqp^{B!r*$3}Nc+mUShyjLSd%_K5f!vR zVh2PWTvR1{JE}KsIJ)m(h(p9%w66Yp8~QJGi5yN`0M`2mL8{|#X3?)VF;R??U2UYY zh4s2DFPkywt;OtUu1-jVo*A2LYu>omc5hL5U;s93X8c^e)kSgB-V4BQHimTShm%dd zO?BxFKDKiF)pE3UwfXB|DKh;hOV!D1A}H5>hsck2b6sLka~Jlle{ z@cA(ABCWJ*h?G9rGp|b|EyQ5mlILQJWuj22(SMS9rw011L?83cqUmEi@BLW%1lg~{ zW$q`tSrUL=iIWp{n2@UZIpNtrg^foR+!3c{Q>{8hG*s(qn~PNFg|%BSiV3%-c^}Lr z6memQv7PXZV}s%JwRtX?yMg?zkc#Z*$ghalCciILgB%x2T&FKwwL_x z{d@5{aik|AUF`&h#&T~`yO3Aer`}m__mp~6J%x|%guicP9c-?fd&-%nHPZ(M299;W zKsLPFZ865~1A$2&%Qs~GVb!FMj_ne1jQ*fi=6FMKcVI!$FH27br&9HWk#5?>^EP{! ziTNsoI1sws$udxQuR<*d))wiqRx!*aCYTaV&o~WhWJl;=p6k&C3=>Nk9t%SGEfo#5 ze5{q&Tzq~tQRvU)&1byy2D!i&^7l%&fEzoMaU%+?kAsG=-2nA1-Q;^z$<^(v?_YkR z2JE`$Gqy;X;cZGBL?E4#9T0`&Z@qjP9etOLOyNypDpC2}C@ZFOO**JbKLUAEVG1{9 zDd1X*JT`tmLr$jNBb>_ib|qTLJ;ISvvyjQV{`A;_8^@Fn9ox=Ulp3T_51@v2}Aj5$2zoBs{L4*SfwmV;JFumSR$2vfkRs? zerNZIMCRnvEdEPFNQndV^x88cG@gyK&u@YeDWe^Q>wUPh>G71NF8IUN*Sp47wDHWo zf(}o|wr`O_Du}W^UCR9(-OnFjcS0J{1`4!2XIdQo3G|rmO~|*jJlWWr_*WeV+!XUo>T3Zb1fZoL!ETjcaYn0q~F28~fCjM*SE|RI&Jvq?v9>WTK zOMusJ96;m0VQD*5sLf}f?|{G1Hi{=^2deZqKNp};b3fOL^L&;g+a11FX?F!|t??se zR^0|xsh9Q@`0kb8@HsuOx=XeHPAkJ{3iDeBCPz!r3}aHKI!9{$T}%JEj*Y;2@8eLr z#4#7?e?yZ-F}NBXW3o|zPVDS-VzB)n1vZAROsq~ppSPW9f-*%_tc{EI>W61QxEq)6 z?;>#OHR=8H^^9ULjEqOjA7OD#ownYtJaK#Qh-E@9qh-IwNQ}|8K=yQhW@|c}pNN4K zDNe2Z=3!j4-y9Isy0SGI-M)QWj@c9_ox?n-dkh+?uCA`zOgv;{WF43EI$ddL#C~3Fj3~9D ziAr#@GhCQ+7}ss_D$s%yUV011_bRW||12fdACLfVO_ql%-AH&R;MF4Cbh$cH`e&T7 zq5iKf`6X!Ot|A=twrl>Fn+0+#yu7zg4_Elp6Sj`lo4SFn-J6N@D;j9r3C}%qzc(FS z%2@tTg%Im#YF`^tK~4m=Qo@w27p?o0_B|(j*J1Yq7e;j*LBelp_jqA6Eno1&lA3Hk z?dwn}C513~d`QnxSNy$A(2K|7;6ie!zr9ycuqwJt95Bo!6$C`i+BwhUlrLfcTj0E) zNpbZ4Qh`ln)Cv3aXM*$vAV z0;-5CC7br;q2Nj79;68UwaUz>ipUie+1%p3KK zeufG9U#U587kW3UmH6)1{xz*Z$qkbz>AQzw>wHgK8z$cRff}y6CsBBOZwik8;B2?p z7jL$&(WDLkl8xx2v1%0U2Eu| zea14Bv#xB%NljMqDm;7l#zzPDDm4cmH4Tk;U>z}pM@7>Jwz@)%e9omaWqT}Y`}7_- zS*t3{-nz3nJUQ~vD}^T3S9t;tf!QPJ$wyFj@PdHCC!^FoV%4X0>y7tMfj*n+ias*( zmb!@NYH8;n`&DCMjsSxy8%5$OIIM?KxaEQQi~Yew{J zqm`InSGDQ%(6*xZ$@;_vE3cLrpI?I%3{}7*G!KDHo}3tC)94 z{hdDqksFfO$Hk`CQOXQs-D3Dpjv72082Z$o9hSOYl96l=~bLIoN)f$tj>e+psI!jf3b7{rcj|nAjU$oN>tMW*wvKbsH zJ3o(T3eq+ihEAO0wF_3ERse5&XU2ccH`%*g=##cKT6fDbs;!C*Gt=AHQd z_P$s`KKc-I?_Pz?oGHYC0mXnOXtiMHpgV35kh6PyOr60H8-i!mYZP~wq{UxY>OZOC zaIjy^Q#G#tVjK8i;(;$MzaQ_wFPo%{@EMV94d$8_zress&Qe9}0Drs0p||SF^qtZd z4j74m<0(Bnj?46E!U!v=2Ald+Fr^jrk3q5Uci2sL()uA$$k;dSZ#}51r|VhE4`2k?MAY6IH1FoGhG?b95tCP)dWx z#B}}I&6Lel!;X7DeK{&c+uA2gQpA&vj20Otn_T)NR4{A7>+BG^!Uk+K#8zech~R$h zWr)j-97cPBu0nm@yzTbxPsC|_x(DOG4?Sq|NcLH;P zn);Q`x{>#;p7rnc;bLEdyQ9-#U~7qBnFZYKWf;5oKouo{1bjFG|<7z z2yp`=SYgpkl?b}TdQewzoz%Ln#D0(go)b!rw>hqxdj03#I!4e$qz`f{Ovc5DZv)7y z8?+(yci=x$M_5e9fpu@*5ZnH2-S8#KPCN+%&B z?D1_xRELY3ZH*x#@$ZBJT1^2UU^_`#y-w<&>O6_|Bf3@nn6?<*FzV6Nex0SA&c zB{{5f>UDq+V#p3SB|d`$z+$!)u;A!<8@bO>CS>?c;y|f4aL}w$Y>*t_1ayBp?08Xx zUMYGSHcUEY@*#$Gxe8waUx{uIev*FwQH}CbJ}4UB1oge#n(J8D=M7M{UsHHl`=fS6 z*O+jj?Z^y-hNyj0I29Xztiy_rEI0p1UIQv{U6kk`g(*oO=kXp|w997XU2ZMJ_`vVd z>D@446h>~f1^`W8acyf^+G&+ufr3(zaPBg7E9?hBN!vOvM3fintzq4r^3drOm)%!< z9bYR7n1`jDQgcU&mQzsP|88rw^q{ei2HTae15{tu;8*#I%ukW@<5v?RezADX#MrPw z`3KEdOdJxr?Io7;Hl51v3#w@J?QfT%dB#e0?=b0e_QPm$_d0`Bx+N1dbz9Y~J7~>A zX}f7q?cYg}cp49FSt3TxQBuIzDqS zLLOYgoLR*};y0hJyfxZT`&0>RbpZ8yqFdb#P{GMtn2z1HGJ>ox zduyX#cP|!F!KS)nNIBlLBtx2{zg_oc5kSM0U@67Y(Isd8#C(qKjPiba@39u-de8#F z@*c;Um?~0Jnj(rO&wDBIhF)`KOc-N3=#7oQ7Kdfv_n$f}bd+hQM(tys?B*sI-JWV= z;hU}L)!^1DE{d89|}dfQLa*n2l_TEY>D&VXvt4Zh*2 z2R7FBhyApn7A@u6Mb!$82YGU#j8do)?iIP8bsDq3>Tz9XocTTVOHCK{s3?woQxU$`{*ji?Pf zzjDehpGaVee@tKz>=wQLPn_^_MY-URt`rFua{yM^U{25b;>@OvQ9Lwcs;B&9eh(javxn^8^E6?o2p$RC+gkvKsIv45dI^>{cE*c63ZjdV10Ch;KZ zV)&IrXaZ%937WeUad#ZlZk{BaG^7ZMEnAS zr7fRNp$&=_V=#h(8|y3k{~ReO`Xhg#!MOl!^w(7S6B^b zbs`zXO`RU~5QUfw#9YS#iLLe%s;1X{xoWmwQ9?zp%WtHBC`_|Vk(4O^VF_)g;J!^;BTGm&k@8$)b4I1*$`hub?JxpmZ;kay46p{ojWjZi$ z9~Y;4uo|DRK#x*>7)@4K=kKchK9c?wgcvqt+mGYLJf-YBpJ>I^Uv+M2E#V_-On~AA zYlsFC1PnU=wJF#lQY&mF;Er6?AmE+fuE&a7U5T1N5SP3@1n>wHexCg~VZ zi+ua^@ZS_Bc%4|PQw`=>T5_h8d8=j^zp>HJ+Tn19(Ip8S1L(D4*Awoh`$n+g8JYGv4Gg*vk~!? zo%pftFAZr(P*(==mCfdc2^T!e{B!};F`If;r~i(WZb6)QFI`HqX>QzKMH?S&0#Gfk zL7GOZ4c#{6F{pCi;%|M@4h{`7L)dJL9|K4?Hgp46AZtFG*;5owWpO%=15|FAA<&`q z2_@?XB-Ar_{aU@Y)NneQ01rX~RYEnhurvpdxccq;?dl+DKB&m-H*Dzf$CbR~d~gP8 zP2F*)aYS&17~zDXo&04{J5El{x_XBa4Js+ya%8~i0I)H?8k$36N)r;ZlH@(+^ z`TzW|J8WEB9mybZ`>8qd?e<EZ-N}|Jam0- zO{%>tBezG?0fR{wHbr4=URo{CY}4OUo@FL6uE{A zGd10lV0tMXH8MA$(9Lk|@rQ~cC1nDL!vh$JX?(?1)IzHc^-)hi%#HAs4%aU?GT0Cs zdxmW?u%Wx60s7dVnvYZ4MGwD6n{9k=mdHKnquL$kFYp>RTp`Wfs_zl0&AiEZu@Zm&mFKUK z`^t)?NsEeLvE%py3;K=h0f`ARzsmeCru^T9;@|V=wAit26(VkZcHZhR zX{XHl>wEoizkj+2O!E`H^o374xij>uDWSJD&;KPOPp~CM+pKj;(ug&kRiC49`#fa)3dEyyi2yw6d5_5C&8|vy2 z0Iyfo*MF)1_D=ELCyN8D#YZ(SKvsG+ z1?h$Qg_B0B`Ns8w)CGx_$qLhsu(?%ieR}ZVZs6<};rX7W&|X|~neYB+pFqg(Qy860 z-PxmRSs4B=;x1!@Onk_fn3!2UP1}SOi`MD>{Y#npt%$j)2}(2Lm10@|BhPh zWirG=DJD&Qbl#1C>5so@wJ&(!ARb;0r8?~Hq4-_HagILV+-w#$>)?Af$Zma`ud+D} zLjNC1qyMfh!h7mxwVClkJ+w^m9bk$52Om}bXP)ezS))I$iFnWh*h|E#OC?kPzp?k* zXpA`{ykL!fzp(wWU-pa#;7Ah7Y9~Mey#>%YqOIn5eq+A#jt;Y}jrURxM;r0&oNxn; z{PzTzqgCMp@X|}7qM~8X+18uR8plvGk>DT=4pmjv&w01#0sm#3#g&|>zL9@U!dUnSP*gKbYegc8f^w>R>Spci) z;U~7G!Ph$NUVTDid~^_*eGG2R?$G_{Ya{2KE&vMLbUhpuz>r-Xt>TYiU}okA%ow`g zU|i0)*q|&=x7f(=&9?^6st6iEg(MMcHcBpl8dvbB;P_AW(h6IC5FLJS5k%9#5iJh< zhu)57_F#)2bMEFP2Uv#1LVtR3Q_C|S7u-1K|Hl0N_6&(=&}_T|$Yx0(UXn`77QDcH zN1dFUe8<_;(GfKKwQFuFZrA*W;rP@1^&1LAB6zFaG0DA#Km-yd462zIz$>Xaz)0T}1z+jd( zrSGqG?Ace1a{y96kCYx71f&JE=Xo`KGDmhN_LWU7$}d>@ll}|SeSZ5HB^05BS1;_9 zi7kXUYJd9n{L)SH>Gr2|j|f;_=V_+%ohLdqV$cB_IyCwz0Nh~b;8;c;pPl}cm2kxP z-8t(OoBRO&6dpJLYO@8IBMK`w^E(FQ<(DUeN5K2=VIWgcG&Zg`1iSy-D*(b5%wFsp zVL>yRqz)IeohRHEmUb(qVM!$Yl#P=evJ4KAg@vQRjbZzO^!}%OBQ?Q zN}+Y&sVPBDzmLA_2;KWtP=v;x3p14I2A}#+J)l|(;Dof6?=F4e zG(0l|+Os@E_qn(vg0=U1pgpKKK?vTjw0mv4UVkv3e7n-l;EG;Bti8-6F)Q{?To@%c zIMje~$K&FTS&2jw>Hc`o!+^GRq(D8g2t1mKg3a9lhGh%INj*SkKf&ZMg!qOm(c z)u+<7kMyIz1LCrF3*A9(&TNWHU#arfHk^P}fgTza!*s8$rzCdSd_63k>f68o3h8pZ zct+tTz76K#O&|_xyGq46Q1b8^eg$h$GbR^L8<#kjp!nHrA=G;npp!%B6%hBf%m?M$ z@Ic=k0zWA->A|$%VH3E0w|=!G8JGz$p_}1Uyf`Oa!5U>J)T{%DFruuBt2r+;o;P^@ zT>j1iRknanUJTp|kjL_GE_Yebp!Cz??8?+^10a3w`f;%%dGoyo-3k1|GPtaGrmRC* zHPd0-;DffroPs6Y=ImRo>zn$Mz0qaC`Bj~G5~auAuDnXgYD%wU9y#&)yCC!zgXn)F z;{&k>LP6jQGaE7f87pvgIfrGJ6SF3w|U6|%eok-2e>;4+c5<$DxFI7q+3F$i3_`b4%qZ!Wk9-Frt=4WLTx0e?fKfpV^x0xoI3LlVy%?qf zYm*&S>Q=|>n!O5)1`aQFP5^=wbJy5gFX;M?@t{c)?gu}Y4mqhq!N4nl4-~(Npq5~C zA~%9mJ!Eqf@8!!hZuf)oypwr=YU z(`KaU0Ud& ziIv%o5#0q75K^z3WzCCUvPtyyu0os&O)1DGyvS0SQRq@KN)(gXWqFlXm!<%7_?DAo z`F-t8L@1*5t#OkN*E~{kj8w;RwC(sH1gm`stP@&6R;c&Qo-k9!!ydo{<}evP@uY6HPra%R4V1BCRco;7B?H+MGPx^-F581ELOg zwcCui+xq&aVD4Ywc^B2QflQ7hlu94O2J}Y+ z%I=8m2=0iY9^=suHJ5q|e52^xJJIqdUY1WquzxZx<$Ep~sL5?uTduU+bhX$!WKjY{ z?@2QU>K=g#31`7(d!qqE(d==)PH8*51TY((29zm)f2)5oY^-JA}n;A z$5uasODdId;iXeE0aOvQ*D5p6p1 zJmURvfNdMIRl!1&gzC7PUe zWdM(7^W{hFngan|uB}Uk)HkbHM(k+XYPl0oCVhUb!^zkaQB=_rXAI3Cm3!&OiLDSM z?>Jg+{i29;8|X5+TLK7NF|@3`!7$pa1A!0H07N<2l2oRK*^K%;>?id;&+>V8F0cck zm#@9aO-ZHT7}p?XM4@cJ3A8uFr^b0sZB6IS*a6@V${B{@o#^oUON-!L=d|y4-%<^d z?`}XhVfoEho7xAfv{_Q=`p%i`*|83(Nt1Bjj8h%>hQBLTR|37I>JuUTGU6p-YCiP@ zre1>MzD7{I(Q%!t&JUxUpS9v&*Wus(yC&sdotqI^^GoMDypk4W9zFgK__(KGebh50 zmy?t4%noM|iZzGGyS@?%?$1?=`)rG@xG({#*=AQXbqm;B^`y`ttz$JVb00a=b)Dv4 zze@XDVO|nXfZxCA&pJjiq+Jq>-q13I*Yjd(uv$8oT9DnF4M;@3b*o^@3giTbwXx8| zK9Yp-T0SR{QbeX=2)@+!q^tG?Y{ZAuFmHtIAcirx<?1gx|AiuX&RIzdgAw`A5C^)*IYpZ~pq*dbS!!fa`9{g4^Vy6^ZZ>E}Xu8V_(v z)T)X*&_4tbHxM{pNP-7e1o(|t*bZm6Q|yEQy#38xnxQ*HKSym%AZJVpEbYX>>&+Fz zM%_RG2=4{yHGHTAzV+Cs9BAToEPUSx6&Qv~_>-yb)rKWdIoBW- z>Y{ahWij2Ru0{2=XkPrLRGg>0Au{1Up)>cL2##W+8mV3zf3Fq(;=AUwNuo(tH*`b$ zSP^61vQ|h|G6_d<-OCiR6{Y-fd!sh6imu&|{uD7O;k8bjWx{wBl|cel?fxsDCCoV- zay+(1WsYSO7mdmvK0B=hy~6J8FVM4~ub7jkP1K8R$DZ2y#X|kR^@4vWII+lV3R!R8 zx_F?Jkw6!o#(q3NXZ`c`Qomu`N>Cm)1IV+JkT8)O98O(FC5C51|E zu&Ki`GnzMq6{~p5JI$lGWh<$~9##uT8S_Qb@=r*Znb(B!%XZu90b!bQsiMceZ z!ko#y7B;_@xf}7HPH$MS_9!izX=uMU(5!xJl@_t=HV?0WZ*o4zx=;Z7a-&(5MRF2u zB|X;nvYCL{i)G_+*9R7cF%Z5=Cj{)Pm2`QZ3@kwn=&u!}Z$&Kj%z*4Eg2@?V`gx?A zN)x4aIzGA>XqsRDk^$`)i6X5$j|qHKyOf8SeQ;zq>K~KviHUl`O#jKpdnOH50Rp(| z8ik&g@QHY6nE`^C!SU@>9FdcI&r3n)t}Cw=tw+{-LH)Z^Yq`@PcM34G<)5L` znEqktY;`HXuC9*`t0Xl!sy2+)^V`Tkuh?|kb)fIYl+gHm9gA+HSQGZXS$lvJ~gvv>iMp? zn6>=%4fBfGHi`^dZab_YDT-e^@adOn$dQK-iT^Q2^#z3O7C4?xNJ`{Ubv%y&8}$Q6 z^t@q>v&Dy?9fS{jf(7vy31TN>&j6i$J5QbVq#h`o+<_{{e0#o^ZbFBG@htPW+G%D` z2Vcwv^cSBJg`X*ym`wU%y0)Tk6yPZQaH z0sBR^SeBzh=RNcF#-pN%>a2T#S64ue!FhLbu2FXfG1GA`Ql-sDWKVX!cmzD9l>?gc ztQ1g6*X~jGN#3Sqz_pb$c@F(HZAm{?c%u^8g7em)KTW!}2_#l8PbJ6M`kP6>#TH-I zK%UtHk{X+}ZNBAn>B}o_(y=Nwo^`OET!PbyXt^zlT_#{{FB-9G-CvHpc1Zu@m5>c* zA1SG_V?w95;E=r{GkQSW=NR=`zcj0o*v`=be_asDi=t@`!1*3zHVGKe!wq!heO3}9 zO+<#Hp-GF8@Kg~elUGSF{W7|fGD_NMTFE>AQ`kQ9U!l1`))Jo+5AR?(NC>h`7(Y!L zb1PuMS?bG-#3@+gzgXF+*B*ni9`a%yw7OE18bIEi+pmeW;YRsStOj%N>{!Rd6sMdO6u8H5b5;w$_)(O)I71 z`8iIaiYjc$6jxiys3){ZrrHGD&@L?!Ds;?_&dt@3_mV79nG7kHFqciz(W&k(E~5bi zcl7Z{k&G(dR&2KZvZq{%dB?VF+nfv&3DCj!s)j%CqUx5 zyaU~o2_iBRhCjp#$$;T^**8>g!oKpA3*W9QqwpbK@K3&*LeLIQY`_6o6ZkLG;<&6) z{Idav6AOJlo#piJ8o%NSISa-7yZdTuU zqP(%!W{?+91vyVY79#j}>~ABw3{-wy!8@){L1V*HXFR}gxF z``}a2N>QE0_#$gNhPBN@D3Vd~4l_+r%{(2YeVY3`?Jj#8j|dHuk`}3{%tc*BOCM32 z!5d%fB`+HGQ)r0Gw;|5;-nDLP1qvK(()XPIbJ0e(Dzc0LN8(DK`FzA$dx@5R8|Y1T z66yLmQB*;!QolcD7^OVRe1<)0qQ%`*9V>Wd0wlFw+eFy**6)12c}=1ez@D{N zxzk=;d%T$8zIK}wVYR_Strqb)g7vgbLLxgj!~UV!Wh2(G1^wp)g7a$~U8VO7$bQWV zxo0eX^a*gwW;)!_^E`_L`VD*Alia+D|n&al3Xb5VlG1uDxUbMxvE51?IeMKIg?e|w6koVc+THuwn2 zGBiK}5QIu&DuLpavI0{XMUU(;_m_W(o9sB; zK9PTsznwgV0=6_{t47h|3mDU~+Ry#TVww$M0+K|gbb)k*ybx{pgdVDDt<2(LLqJf& z`^@i8LkOTE;%7jnv2>ugsK6YI3~YoacosejaE%-IVbT_CE!zqcM2$APGS3N0!dP*p z+(@J89}r7nnr{-F5TB3=*T)Pexvr49N^cIJV;lgx`6(nXV#ws3>sIPt!9bRX=8YZ- zyN%mu7XN(6kbpd*pI@WQ6;p$->%#f)bAdTui6drtm@BoQ)m6|Jhj}dR!e|cYjeKC zg{1w3?Vwv#v4mB#2%UnHFZP(r=f48moSMTo0W8vDdX%ZY;tehuDceGm$|%t~=76c8 zRXC%}=*c-qgRy{jwyvf}z}DLQJb@7bm$))J;;mS=ThQpbR1srDl{Mlm#x3<3>z!gz zD7(NyBOWoQL~#9$f4Z@9~(1EJ#qLpJ>)}PT!FFrGAN#L$msc7b2`y{k(I6&0^@- zAXFEW^!aEWg*pb9Ikd(EI~oX7^WAsp>nmNE0yop15S01I)=joG#-umChep9!o<$tE zZ*qZCaw*L6yabTs4zMB}axXRFxnt)vPgucd4`PZPH-lRg-(Oi}>D_vNtGMA)uNv*3 z1zfDJ+CAh$0KFZmM8CU7^jsY#(oWr#rSVGKY^6<735wEc8E0T4=_Bk6`G8Xj?W_`; z*O14zn3}^;q~&>N&3jwY{^Vr|WRrdWJ;M=MEllhfoWKAr91XP%CLj)!^PGLA^7*Nv zcv0EP6^wEJaFc>inEqw6C6z?`vCCdih7=g7`)yD| z%H_D_^xc)naSipnb|p=zpKIV0tvk=AB-3_%fqlawAoK!io@j6OW65)d4{~S@0+x5b zh``J05X^x;B0o_ywxt)|^S-2liSb52iC;1!&x7ceCol~&IR-I#1YHdEDD+|w8WoD& z=cpxm>-b)*3TCqcW;6w$!49t`zM= z5iwu{)eMPwyn#S3lCO!j!15f+Ub0w%LxRBZEFQNodn69@q2kqzh*d`~D66<8N>Kt2 zAsJu;=mxG+ObgC|Hq)5UvZVeFx$P>9S>^$rRG>YsPT-QFq5c4M zZfH~R$fJFj10BPY4NMD_F@}+0iY&0gF@CT)JBx6 zy1A8S&8`0=@3aJ<7=t~~i6j6z=po7`Fh6*z2ImOTg0+%q`t}x0J8fX?dl{NElhv|2 z)r4+In;X6;4gXArp7^h%QD*|Ir2Tgg)*Jq;Tp^+hPWFbNN3xd^W{aieXq<9~^#?)8 z9>M$XA|7Q47LpM;L19{A94_BoqVv2o<}&48=&&1*{yx_)kmDp$TG`PEja@b=Vd7sI znX0d8k&|pyG+~SaMamW4>Wk3~5<(?RF#He99*e9E@~v>1v68i3s(MOH>bVCGzbl(O z^g@ZFScID&y?Y5q)E5C%bn?51?{S}etWs(1LuyCL#Zoi7ra7pOT5$2Pp)l4v&1Ja3 zE>$ag;)yF7+s1dg#77C&qX81#1;7&vfJ5DTOBupuNa1-LG2!$`G!lzo*s~sq*uiW? ztxUYR%fFk^vfHNq8c7BdP8Yz1F^MfQF9zr{khc=`UCeF_5Op2UbTo_V;4xVo#@JXP zO`0eV1+rnKk)_v3+G(vq#h{{nD^Dx+1M{f=fKD`f8>>CmMtIkTnB z|Jx=MQjEy-`5=-sI}A@DUwwV`0_AsFyA7&l z9-8t=iGj7AkS%fkF|DBPnI7gfw;Z?3sE9ok&y}7;hZDvPi{JsJH0M+U+8I0*m+>NNk_B%lW}k#q8R@_aSpR zmO)G%uRrMV_AVMR-Tz|MI^q4p1fMh|S}&O$SwVwb-@MC$qO!P`bYg zslTg2e*NUKDMU)oHYJ02wp!Xrd3Dge_OEyGcb!X&7=&h;$Dw*V|9`$#`8+3u&1=(r z`G3CD{XBeS!f`?SANkW?7+#nFHj#+({`B_X|9t6IFnIs^slM#L8$AC4D_<}HCrO5F zbS%97r(^m4_y@Ca@c!;?sVe{TSjfw*5Gm#Tu|Gb&)@8!giH7^nU%WWa%>XI@z?WYi zd(`*wax8T+=O07k&tJI~l29w-N=ZT#{CBq7mKx?IvB<}n5U3Ge-*b|u>`bAxBd>|ARF@OVl)PkAk6deV z`nq?jX)e`i zdbF%9eRS^mx0?i^3#~F!H|e!GTu*J;2!4jYa;4rZI=+3Ix{*`R%8h*3Nv|y-yoA$! zJUY)#)G1C{!zH&Tk!NCECN8klrCqO9A@KRwZ{Im0su|!Yd_Y1#TvG?iw$CLdZ81ke z$<>yMy7i-f6d=NKO}Ii1tmn4_&NHBrISew9<|qVHtol-MWMNAr2JvLpFpY9(1Y_NEOdU*4zuvFF?)RoQLyqE9zo>fC$Ox7oR zyfe49e!jD_Mm}tnPv(BjmNG5su4)8X!Gqi`tJi8&zpY=0E5?oW<>34c5Zqxg^3I~x z@+JtnJP%^w0<^kWsHvG*1s_AmKc&UU;jmzqMz+-eK|fFyvQ5u+_vz>9(K?;mGAIMn zx(U?d?FjuBLf(Zrd_p);f=?Rq$bDC4o^PEJzaO2YP^CGkyy4}f>fsiXbFx{+4)Zm5 z5T_9nTa_;TPBt^J$;dbE z`54R)K;&y>`afPWl@f_O0cwqNya^n+rHC0kS3ZFgvQmMJNaZ|YC6Z3``FVuFxtAPZ zt)IOBwh{vt|AJ~<%|uYDzHXio!jawr@se{%r0G^bG}?5sm;C96;kigk0=Rr|JE#9> zlm5a4%6($POn_s@dG3{$$fZ;q&CX93c=;1D?-d~7@yMI*R`p@RX_X0Ts}{JI)_yWy zvi|JN)w;j8_PjiDrQHe=US2U7{!-LJWGsoywEm{dg)7=!sM-s}x=W)?cfBO5k{oSb z5ws^ZYm`kH?nbg}((@KMQ(oE&8&wb6{E;Z2{`u)uBjvi6^Po$vwXsevfK@ zcB1RIdFoZ&_uL|)v#(;lx+3zWbA5H!ep{0K@zWsAXzMo)laDsnzUP@tvQ^+ss6g=u zK6Q=nGhoxF-0d2jiL#;pS58<)KKa0;+O}t6Qj*VG0k2>Lu2UWe;JDr7<87z{)F#r- zQ$f^fAhp5*aG^rJr(S54W#CKY1xJE?2F0TFSam5oJA3Hp+Rrp~;fWX!`Z4Sb*gGL# zPwJ1A_VZ6t{%I&{u@j=Er>D&Uo@^QKFvCC)v(wJ&Bgn4Q8}jHHw_$-t9n4+E@ZJVN zAUwRzCy+g|u@ChkB6eRg@`E43Q~=5=>&^RI>w)R_#}orTO#(2LaP^*CXupsDR9b!#ydX0 zEd%1}SQC8bN7wMv5nF%?7v~v%aYs1yO*OSJBH*ShiKLdY(i)$S$>2v>E(V^{(s?QuV|+{==c@`}=uy?IN~T zLoeMmhRd81U9HBxYN{m|uY`w-8A;p}NlAL6Fn94NvKc#?Iq>>6;}~brbBkMxf3 zR3y+zpFeSJVx2nd)r=mwPSsOXQyTY2%Sie{;(%R;cIcI~tA<~OXD(ciSWxe=wr{Un z6;@3XdSF^N&eJ%~QJ!ef&++4s;MP(9?S7WQC2QXxj6SqwzUZD#gWc^WAtpstf)2x~ zrDtXteGf-XZzc-;);pjV3BNVH7{bNBRJzv#1b&al>2gLzA0~;~=9%=I*HZcmz1EdX zR`-k^nHe&qbfR$yh{hV^jGSjm|4aRnkwMX{fBh->r5un9hu(F`mUvZX$^5+PPTq%! z9DlMn?j)BkjkNN1!)c8Tm60wr=`^q(&nFSwxuk0><+qmWc~G$8>7ds z$gGY9PiKl6Ri6J`(|(4p0Pk^;XZC(FY?rap_wohhleJ(U`32pJi!`O|c?O=n6vxa; z6UWJpV>@H4rY!KgseH|R^i+5QZ$98l;A^$#b|_8o)3j7ZPdU>A{`!f zg2C9gb3OV9P`U9vWKd4Dv-tQFVMTkDWhtg;n~jNroF^m@I@vfr-HMe;7!uzXF$}AB zM3BRcq_pW<<4x=nX>_a{7llSo_or1LOgg0wP1d-d{kKO0EGu{`rG8&3v)E!%QVO$R zkYr>4=dA*R&O+4YG*=g7y??D=^2v`_AUItDy*n$2+Y4fO656B&;EyK48i zholSlKd>G-4f4lse9tUcWxE$O`hlAu!Pw`@ z$B!THVX+oHi>K{YOY9l!um6vZUTY9q4bHCajXcD$9xgJ}mVA=-$DonpLx_WPJ4{?s z65TsD7zxLvOBNnT0douW9y)B{%PWazVeO=AJU^J5wuYZ2@s#TPh}*B@Cm)_*GnTvv#LWy+M$q<(7>TVI(l zSZz@$`yyI3dfJ1}`N+U|C5*RX(hK7SCBQgpyB$j1xOZFq24Ffv06f5zvzV@Ua-czO zkrenyy8KwJEHI1MIoc5PmZHJGy(!sk06H7LLG*OZaM_{%Uc$7rvsu zoDv#622y~8cI>(Vh~@c#3B@jtZmtFDA!*B3_~1HSj&N> zltVe(`OiBg7zY8uKt#1QTU**((tK$ObnC%7t-~`wT>%9Um;@m3Q-2Q_f5nYJ?&94h zgh3(%3K)21XFq7MacS4G##f#0Q+D$X6!)IiVVKa>idIXKk1wts#X9|oz z46>se2f5xbJg;ASei+A);UmMTLjH`(W*D2`@WL-5wfNfF!sd{s+$LJJG$~u#x*GR$ zZjjj`3vPnk>k?;Csl3GD+yEgiK{Oy>cml|nIJL7aFIRwi_X)SzL@e+eBzrqTbq1X@ z8=0TIFs(&+_v)F^6HLxAl=tF6Wj(#W!}Z&kXQcg>KMZlNZ7nU|>SjLBr!MRZ)KEC9 zhDkqU%(DQbpQy+DAb)jjOfg|yEpm)uF znZz-~^s9zFYQGJY664xmMwwO%S^eC)NX;PAq+T1TMGWBSULy=rU}PP_l>s1@mG4De zxpV6Nxqu*$lwJUx<(IyFrazs*;Qr-{M|ze{Mn@_`NtcjDz{;!;8mC&Vm*W_Mlw^dI zLSlF`tY1G(VZ|bxY*aI!&TnUwA{I?LD8DE+U2kV>)X({+wp0|C%F7#CI>`uj%29+J zV9ZBDDLvbnh62jg57$6om3(WweG0H+sMX-Rb0xi1NAIUnmE7^X*aaiNH^i*p|G@D2iAv;QP4gSU3=;PB zGG3qkbCP9?>F1iWJeRXZL;INl6r&zfpum)u5b>+^B7Yn}I0&F~U)?7SXEt8&=w;(3 zHf|(c2S_FA7~ur!5e!F7sjs|=J&G&dG*-V+CZO7MNXVm+{vw}~0| z-Y0}?q~#&Dzwt6z77KDU^*T2l$3GhmIUy=YyA0|Nq+`z*92wS$xW=i{yemnl``#gm zm5ig7n?}zYiue8`=5ZqQL2lZdxOF;%f6_3K4(L0~+ACfFl}T8zhQn2Iu+jXT@nwYu zGTx&Xp}gobdw3n2*<-)SeC{Mf9PVzLPQ@m}Jtdbze${VrvnO?EvmTIne!IB+h!e52 z!MTY4J}%j1r>@6p+#}j;pX+-*<;*EuMgmCA*+2;Wsag!~IN=ARHbY=XO2&GX00i4x zR?A-^guk{#z+FWOWv)O0jGV6hMJ6I1oPwVFJ$PiWIg73Sy9Sqx}lw{EP4^=3A8A+TG5@5a8k{ zD60oT9*}cxa60nR@Ft)O0F%w2j%$MN&b^NB z0J*QK_#CSg&_66e4LsfGT-kWk$7l=Z(v3WbkiWKQ092V1!9!wvWYrSQ0&}bSH3B#m zbfN{wRU*lNT1p?zo(X;j%IZloL;{CVF>9^F5wFDr;9E8&J*>sIfJ1Ii5;JGuK{AY- znsV3jQc1vx^NYO|a}wDd3YDmbi!teRvR;k+wM|pC;MLvC>EW4CNMX}nt>#$a<){_O zJ>vz?N?P3J1vipscs9yeY!;CC)Wa4kqrU$Bl+5(FTcysyDxuW$2`;!o!p zRRToEi=Vd`AXrAhn2MaL^WMYo0Ph`syY)>kJd}7g2&4lx-Wvr~>HP)D;}TadF+p?v zNOZ*T33w>(yzhZ>skYD~o1NzNOCWN}*k;;L+?cE~QGV1ezS|#F<%_d7CoOA3(wetPOs`c;P zdQ@Vj-pUlnh#kScN6(Z;I0?I{M~69L`64}6h_177lzlfp_VR+RtI4$7$w(-#r_IZA zXWX@`+O9DBx2NW+7g`&V7?RI2E*L_xEObzRvSu>-9z??~BizB5D;We`V8OvQBe5dh zRsy|S6mn7j-h0Pg!@7AawqmB|X+xCLE& zNbXc1WBA@7{{bkBi?ry!`_L6QA!c^Jt(f~Urz!2lrY*MD;=!G+Aie2b#(5$fq<$IY zG~6JW;&;XcZR}#YQWLbJ4XS;v0c}Y1yuy@$Ha^BqGMB8T>^fdRvJ0(<7Tn~wvAUHI zv=U)92^)&9O3wRQ8#8I{8b*gVLwt6f7R@#48_KysCq~AM{@%@IzSEJcbV^NR`$L^! zLn(7IJ-eK+;vqV!U#pMsrciBwu1f-Lb@Ykg5c^lPbFP8OBe<-AlCYf;M6%ALpFS)2xL?nqT^nE+#aek+f~`K@CWT z9|pRxZ5;ZXs}C1j&NtBWLg)YkAZDi8tRK*m=ng)6DIUT3u5}0q%K&}(Q_&5l_V5aw zmGGLyac@pt$}$iaf`1A$eT968`!d%Q5?tl}wm6T$EFT4j3zXWlz+aVTK2!BP1HBZm zt{;UQXzU+4Rei8df-5JGaUhHz=)$#t^-4^QrHXu69LOftl8n=%K*G745W9```JmN& zL-jSdE*acDAL$U_=pG39wfe3sBOiWPXmn;c2fE*mEnMFg&eLxg?2wJH1|7avRmd5K z?d8tH7R29asSBll?0D{dtALwPaDYLTE%_4&kE{9hOjUw|+*>i3Rmm4;Y;6#0#%Psh4O)TpD#7n*5>g>uYi*3^n zfgR(iF3t!E$|xHopb^O66ZU#|P&(Mo$;}<^G4tJeSU0yH5ocB@2p)ERQ9w1BN-#6> z#X1Wf9?U<*EpM+j{)VPhJs@(=3fsI|-Kk06mjcReaSCkd5GbNHPxDv%fe&@FPD`oz+C(_)u&^wj>FzHm=_m&aK^K;clmC_v41!CA zwTL>qKC>F7>r-MZa01C_v@a4=lbC>AgynZ>rQQ%CL39%C%SL1EhdM9|$9gnUeMO(O&bx!FF2;~Kf;yv*0F^bc_zd$$s#B-cppLc;zX+BWyQ)!%(9 z2Qe&9*}Wq#>JZB$%Uo}8rPHUT6~Zd9Q4yjXOKgk65IYQ@JAzvnxVUvF4b z%AC%)Fg9Z_73 zZ{x(PGfEn5Rt>2aR+Zf{F+8_sO4af7M9^ka`&jx$=a;NOQ`kz_g0(PmZ*NyKoO>>( zI|X{wf^uI~3Ud+997HZH!5Ni{T>BBu6RHv5Q3Ym3rG2769Ju`V0s6s$*8yah_P5ol z9f?QA;cVzg3=TswuT{wc|AC8Goru$M>+BDcr#}1gEgm;jmSl)M{yi8N3s0PeeDfnJ z&P%9w2>M{xu)|OK;v2?)x}U_d^Hp-!Op|e>6A(yuI`HTL~0Y`T9nQ9WApP7#CAEsK2}~L<-`r{kiSvw5AyRiiDUo@$4$$XX9G%w=VtDwr!LM>$_dKv9n6k zp1!(%`9M>B&da!X9=eOJBxcvK?2qiVZ&<>vem7Q%ah~;ms{a0q1oms{@Y6@Mzn5%F z5FH0rluDo)rX%-i0Jh7I6SZ2CRCQ$SacyiPzSEiH2cqB20Rkv6F4zK$i>- z-}9E*WXB1tn&a|qu-#qA-LBFRownDB=oE5D!^+LmWZ&$~3?meOS1j#4ER28qd@|A- z)oXf#^S(D`1VOI>uqX-_-PrKCCC66^9E6L#nUkU<5QtnY{FsIN@ueCA^kqJAY1&7E z%$E&r=aGtYl(vITo^^LT3$(OIiDv^L_Va0=c$PbwXALfOTN5qUu_DG8*OSL_dekbo zpE6>x==OQx3DHPI=Kx4U4H$e|-ry-W{4;fBY2-ulhfy37^!SOoYNs&kRraM}u(7+` zsz_(EXO6cQO(SOZCzpz=U~;1(bRVB8cUOK7w95Zhw-j;oizoxeih>P|S$tdIUo2hb zq0lL4Cmd2U$Z$rwfRLW z@%oOy!6kd{V&c=R#h64Bl(o?~6~!Cl#BT-SjdqPof~h-B<*;-O zuJ@n^uut1Yn4i!aO?9IBbVX*w{=ij(PLOxI0_+zn)`QO26}kGgzV*Y=CKIU>|6e?Y z5wYRCF+zhgTxFB-v@h;>&z?60vsbvOITyHVhE@TZjhfGa?F>}F&?@?s>YSDW$(vJ1 zqn#qEpqb9`TROQj!qd$e9>$mCPeNKPZTc1k z(K%%`V$h#B2HSBd0Gf451g0t)p+1U2MO0N|yr=yD5HeLVjetUo+jQO@{4~LR9I)iwC8~pHN!A>35i}P>;Mz zS>afqHZ8v;HM~RZxg$#CG3!yY)17tF&QF^D{R<;QK4WBZiF_;5+e5FBzAE+bchu;u z*tRISZeMmGDJ9OQLBPBlLu$uhGHRP85ocaIwYfb|PTsoP2Q3r)B81EZ4qCMPk5QC% z?^U`Kaj|7iYE$S6KCj8I!;en-qA>sQ0TWE$mpP9fnw_$bBAN7NMGOD<#JnF;|5u@?;kt&m~sN`rKm*)SF~QPYA*!0kM3P4Ow8B zghy7yT*ij0XHwCWwkX*JxnLtu#BO|!V|FssE84WZv>-3aBGEYj^2ME@Za>j|a|36+ zNOzv>w~h4}$2v(qu{KFN)96V>9N$9qB1JQS_bSGvZn8QQPJbHBdzIle;_VS&?*{YM z>*(B_t$o$RaR1~P4_hL(w_D#sC{1#Of`o{)*2Jf?*^lGi8*Msbw9$%Q?l>W-{`Tgd z#&$f6=W%p!j?lM@spbf0u*!uKktG?NPFgkAL9zCzs8+wDDzj-K)l+1I#mQ!xYlHub z#lcADVj65@0fI&l4xI_du}~{SS&%A|wHIsj=D$#9iRxQ=H|_Qg;qGr2viM7#>BWNA z$KK>eGe2{3ByRZVHRf7S1}iexvO6ljg>8i1-Cl2T;vup6()#pZSk|XxI61w}SgN#1 zc-uw%`CUD`3NlH^p#GNVEx*VU6TFEUvD+9%a(b&{ihjo`M4G`l1jFBD&f8;lOv9-? ze+s^OdAxyJ5#Hf300{x!)<#4SfhAx3H+5nI?q)>e?K50NRCYt>-O2ZHOmQwzKD|<+ zjI5v@Z1)qa6tvr!*JSnW9&Pg!cxo~6_D}^gxya|_jfZA-IzEo}$1tMG8VvCia>M+} zCH3W`y9$3mpCU_FUqKEj(Z3;ifANCEbf9U8@HkyBTt%+1m4u>Bzgv8F@2A{#X4}B$ z>e4sPLGE@%{sKN!%N*O@k_>ilT(Rh==H9gq*tl_TZHo$B(oWQ>)mdkriWRwkY(pme zpt{o`de}us8bR9Q#XDhy=7dCT{M5+XhrB6j0)~M}xvpI;>DcXNBFWF`m7!XgS@LRy zXKAv`LE+sBkB9E+i1(K|a;@#ChmTyXg%0oE)hk~MkW+BU*aj>fsfavxTz2{5jw3W@ z*4!0X)e`CRrIH|(vfV{YjR*vQ_=mRN=GTFfO_IK|aLN+4tMSky@hr3c_awQP?pU9q zvL6R+FN(--R!3mIY9YjI%-xaWCssX}u`Rt&xXLt)SFKHwY|tJ$t^p(hl+5qG6^NdB ze%k?%MZZXOhj|ta6q@^&w3jrJ1Vtjtnw969x<2~FMUD=>Rc?3At`7#(=;`!WxLQ#AyYGtAwA8B^T`e2DsTMvYdH#TI~sBrW} z&XJBCCfY3iYU~4X_GNR!+pi0?T1Flj278QBLIztZ&o~Z0#Ncvz%x*pMmDIS(xI6jg zTzk543XSo{B4Q=U%eh-!v}3zTCDr zY@ceBveSDgM!4}Pp9bL`Qn)rnbX@E_5zf9`#JxpE{6O?kvRRSZv0`@UlWH^24)@f2 z7fUB|vEHWE*U$siG?nGR$?ZeVgEY4QTrH}5utx@Ji%7a<%jtC&vbP!;s#X}3zT}nR z@;lj?3r>FU)-_dnGxhhkp38JDRoAD-ehZi z&T$K+xS-7Zsx#zmTSc7Ngf$DWA0I_${i)kqnK9h)sfR5=^X10;&W;=lowmku2V!Kz zDX@rLdJ^Mc%6JkPvkdS365-P!!xtCQ(&{C&b9&vOc+i7M|U zC#>*W$(YpDv+>q@r*I;@&)&I*Oc!&ddvOZdSgmo&FR0J%(qIGU8Jc*@lQYkk%iY+~ z=T|I0YxFFj5Sb{Z(-WBieqyU9nla61$=voYh|1ZnPd-`O$0q6PG?5PXls+iQ@&Cao z{^ebwN1fY+?MYwjXSXt4`E2kVnAk>82z|sP*iKp+2pKVe5MT5{BQ~vAPQ}byjjt!P zKmL#+d5!TaFWtpdRISa}>|llz1V_2#H-g4fGDbu^e7?U;B~C&YAS910jaqV59}G2~37LwDbRU_*o-8*s!zVBSPoE;^Ej?}Tqp zKb)rcEfNK*>>aHENhebkvHyt$jw$|$SYrF7kdBX{A)C{$P)ZT)ZOkqkfQ+sHfZj(X zIC_Ke#JrZ8Qw^z0zpA`0ust_mnfL{my<-4K;0)kVDqf!^!!hwq`)4n+oKZmt>!0H= zAduxvO`mpqBo0KAEiEvwXQPpfGwB3#x}Sdz1F!!|OR*W;(tQkM0$@ zV|>A*t=IUU+kAxz^~JN-2vg3h7a8aNp};o6w3u&DaXH&ddq30T@aLZ~@V;jxygySd zw{oK2`he4xUG3ouB=shjYOGZ$JE8%iZXGqi9r$_mVFrL14RO_6!Lc`5k;EBG z&0Yvtg--07~sy5MdEGRMhq|I0=7tnlT#}#fqD!HGe7+^cdoGbm4ZzK>pbT=tk6DV8^ieK-2JLl9&zJG5l19v1T$vrT87Kz^kJ>qAA49u^d0x zOlz|K19R7|g3H2dBcdebkq$)EwY0?$MHAvU zn>}pc|4A_yhVZtfi%bQySe(xy9)19%4eu6v@Q{8<^1)63a;5mYZ1*Wj^Q1wm{s2Zo zmihP&0l^S+;gs-2&TkVD@+*Iv!b(cq8;(gFzz>USn)Q7&grH$5o|N{w3p#t2_r2|5 z#v2xLD2_(W9?(#JaqP2u8z0@Da*3Q`KigQr`h%{Tp|dv}(PXr@i2wNKXIqBG#xsd% zO>T?Qn$4uWCu_NO!9nsVB4tHY#iWJl{4JL9KlNidGsg!kvM@NZhGV-tydQtdi;1t3 zn?qZx(8Lt_P!SrA{Ku5;^CnI!)g(|KLes4QW(E$c^ybF?gnDs#x?30{Ms=z)@$sOQ z7-bPhI4u4Oscg!1>j`hrhEKu${rDpghQ*rgB}`3H#wD^Y@Q4pvhz)3SXHI9;(eomnWp*-RYz7FFTK~$NNlj|Z?8}q0 z(>_xq3!B7{#Ij21e=}ZbI9B{6mb!N5gxhrhb`}}x!?{#>f?}Iq$oal*du?E4PI&HY ze%mAPE9JA08H;Oan$)*X4_nvKPAFJJMmu)VOXy+)@filyS-#v1H982)@JRI;52H1H z8r(xysLg=xMpO;QJ2o5hv@nyFXx4qyz-k|3EN#r9_&W5R5R!m4OlO?T-0?S!SFaV) z?R1P05=qp`3yMRByV)XayE9kRkMc={@|%@>uT1LebQ@FO90pGIvua#Z4@SSf5$3tQ z8_P(O|8~?RR51eKKauaPLWvWK29{3EcTFo1;|a|M3vOAcf7h#1y@jmCqso_X#mB7^ zc@MaZ7&z%*bf+9BE$}2Hb1S_wPD~?~rRqmIDl2ss_K-Iic6h$a{MXtN{_^4$&bB>e}h+y;`X9rlgxTibjul?QD^KtOQh|;<23~&k+-kjSJj5`v!-0Tt*Z>e-C6k`$OpV!~Zgw&st?Z3t-|0)-erD8;SI%42Q z(*NhW|6hL*<$=^GzkY21*bULi$;n}+YO()3P5poW>E9nxy}Q39RteDNhZk-?;&p}p z_cu!AZI1J_ih2n42(av<%|9O!4i>mn%;Tv3$M^d8Clx8 zDKZBLLIYcPYHl2h);oB-BRD5d99V*oySOAyV-$shaDlzZwf=;FBD%;R(m%c(MK3fM z1w4mnZcm*5{g-?1iFy8$F2y$EUN=&aFwWqQ7d_t$tndN5m|;@j!wQ;+WhhQBGK-{tr7sA@*Ip5=tm7ckyieP3t3c*?w$=? zr2MJe`@?RZQ8~v+O8e3#?k@j^gK!vv0~qX?$(GFtPnCadal1IeBPC@7t;#5X6DSB6 zfUB56G(rr(_@OuY4e@V~32lIPBqNcTlbhQI6o=WM9|E7rSuix(q@Iu!o*wi4@$#&h zAYj3l09h?El|eMt5;;l(kn}!?#(k| zlJdmjLZy2X+4WZeyDb(7NczF-3`zWzwb0PBKaG&cN%zE4(wvrqkS`uiIsB)q-CJMw z9!=OI)9@wTL?g@&b_QF#;4c#BJyU;^8rvjOS4I6ELY!ft=dU@Ptf| z*U1WpeGzW8x^Tf`YvCDoLM|UazhjEO?!2DRf;EkXLc!nEPF@j-hk+`i3GXc zWz=r018RJTxV>Hz@!!ts|MHqeZ>b}hu}PqNz3>^nkjw9p5uIs%W>VWC5VZNJEhm-D zX%)6sm6%A>#u%&1#u)4OmF|u3{{f6F%7s+%WCV5(o>B=CPj$=RI zx{B|Wf_16)Bkg~@*p!@&+)IT0Rw7k#1EC#-dBr-G`)jFl)|AT+8^;PTf zaCgfSc6ud%&5e3OtLB&sWb5~%{QqpbMxG%#>@P3_8D4a3>|-=EG|UrW+w7*4NhVC7CACp^JBHa0e2wyV2aIxZsVzi!We?xX;Q+8Zw# zlZI~&u4RYd%74dt|J!H#Q0+d3?0X$n^8cN-tf+|g(8U|Ro+F>kl+FL=Pv>u+lwYi9 z=A#d8!Wr3t)*ys8Gb>Z-Cm;bEzfD1qGl85$E-9ue`q1?=fq@be8}m)~X8pYYzdxC_)ebu8zP0Sdc&VMF!gh zPRPyRu}Cgbuaq7G7$9a4s2T&0Hc9uqb%sZv=70voOctQrJ_nkwY+!L>z-bFX{iFoe zJPaZ>Q7})Wg{PzpqzSw|{x)wZp8;;7bwDFdtoZ^&F1SOH-~QLH`vHfyIQ>Yg{yGrq z#2}(#K^nE&r-4+~0Hl}wLy41zDVewuE}(#dO~C}8V8g{iU|FMMd|MI=mNaDww*#?9 zAHWtx2M0u&0-0rs!o3$SUxJJ7;d41TWt&Z~?#EXSJ(Fv`K9X|=p*mH2@Z@mdf_MrS z5dwnQ*AHWr#;=mt_5B48S`mTZjk4rB9AZhUu*djM7Qo+kf`9-0DH76y98vsTd+*3m zupWVYv(N_&VCk!k1qUX0u+<|+n4ym#TsHxXPsozQPPMK5w9&iMl`>t_PV?{s3y;+T z6TsZQK15!lzO4cQ*YL<%Z$eD*@-~=B%yguA)J)r7-erCX7~nAA_|o}JT8Pzv4I4sR z0G#NXi5cCut3G(7D^cf(b^0DVsn1~G?#uz35nL6N`Zf9_QuGbd0h6~Zf|GVHTF1U9 zkZfJ-MB%fkqyEeFW)LMBMeqSw1;>OmJGPORp9A98o5VH`2oFSQ{kQ3%)2M!jPc(O+ zATYs=9LY)~!V&BR37C>Zf$VR!?Bn2(3OHE1K<$_yuvgbXu<_4|n+*RxxWx1QFFV^{ zj~=l808J*%I|cF>6tWc`c+=|Hk{&n!C}B(Mu|34+wr0I|b=X5`2+U#iG=NCb3y+Nk zm#Pb^-eJ-E7HwGl6_9bQPgQ#xvkgIOk&~+hCgmm_FZ*};>G}?zFvy-I)%LBsYn+Tf z`)R`XD%~-H>1IQw-eaMuB7gWJe~FW`>kR)TRt-e=3M3JsZb{Y6c-&kTjNr+J>LJ=U z0UR-Kpp||+i&T0zc)|quuLYkoG&+QK?I*PeH(Pj`*cw-!g#reX2T;ZW1;yedXv8s$ zEhfPBuaq8b=(d9^^~CXBiwv9K7Z_QwjAwqXbCdRTwON8RYS|S4#eQ8%V<+ALAY=)F3w9xBqykoY&tPwnmQHtZc(ULjdVGknsjb>8&|9Z zCNe_0;o-bZsI4v^*{_@EHf;~pC0D<&ZZXBz}?6w}o zcG(_&Gq!84wQ|$vuQ>{@jw@{TTSo0pshsssH0!_W5*BFeN!EKVadMQ8bu4}DcX+nf z13G-n;JotCH_tB;;RhpQJK^QMy+aPO!rB{_;Zi3p0;_igQo|(+FD*?LWE6_kcq>)> z1|rzL;mlim26>;q)pnMma9+iAe^lct2f=`Ymt68z8g0@Y=j{_paMAeG`4?gPuCl$l zcy`A#dRashjXNOO*JVLM_LHM9t(O#s7Xt46F@j9v|&4noYfoP9)eMw`WN*Q=G8fsv zIk87TI`I#CeV|IrZOSyCuJ~jDSXG8QMH+L~Gi(M2kx3w(cn&bmll@6w6Im)8!U7eI z00VB43*$h?Xa^Ag#u-tA@(Y31vVju&1ss3~XP}LOJw$%R^PlAuDH0p~J#l2C6kfB5 zmvDkCKE#>{qb8J?Q<@FhBJTI#(hacg0B@{$N1%t-0m@Q%2=LFbuEn3X zH&>{)3q}D5f^=*Wji5BF06f2XpyRB5mWqw?)Q08!3X}Gn6qEHW#wIWX_EZ4YmXOSW zm>RV&i#vc4e)M|yX)w)kfgUiwZ5@&mN{Yr92?cg;{0DvQ;NyFHHVmJ`+Bw^h8uV*x z5rETt=D5cMa$F~mh(_E2?hR>y4^+OB$DP~(OQ~?gWD>t!u17Nvy55Hb-*>% z!k3%Sv>?eR6L!K+bWdVR^%Ikr?|upd{od>MXk)AeYkXScld1dfx9plAD} z6BH!C_A=YMhxiix@)`(nx!n6=>ES}#-AUP2(Px)qK%>VD@P#9Sry>}zvvj+>$GH@^ zOTvo>VZq#-4Z?9y8q6Wwvy#nzCoQnwdfRn6;x0KKl55qNC#6$fzREYFoZGNoI8O%{ z$#;ItWQc;`Tv~F{A%{3{Fpr`?1rYv>FvM@LPv~#h8h`@V_6>$tnHS+qKy#q}?3Fbc z@0=s@BCcD2B9{M^r(3O0%RUSzwbcFU^j-JGd&~D4MR`HZev3~6eVlY@61g?sUTX`K zBA!xXZh#meUP$xgCgM4B-EPfn)#9>?SZ9X-4RR=j&^ra8lQ%c>JvvuF8CXD}b}rw( zzXT$*i>Xs^aqn5&y8oM6> zK2O;X6JpH9 z*((TGh-6N^-aol~@SJ43gy+1TQJUgz?cD98p*5=LpY=>O&CjNyk19=ZrdBz3qB`^4v?98dU?PxWx2Iiq<~piq;3 z{@qWL;68(bHe+Q~OBdRi@4Z7+Iot69nFa10^Tn?Zc&@XlDtyO{pXeUIgHTev+8Rki zeJ+sITfjQha}vrq#HO9QREKBcC6T<##t-vCoRi~8oh5@}L~R39ehr0?i)8qtofK}f znrWQCRJ0Jz=6l;yJo!Dl9xCO{H_q};LPKu#$pn0P!Ws1?41yeYa6zhD;5XpBnuhC$ zYFQ^SCh@W`R|2L%cEw(a-^KBUfQ`Q_1=+3SmW?O93HtkpdfSb z96SE>u>Jl)5C?tAu37}Yel#H93mu^znWO9t4i|LRh0_&MEcu=gjcEqn2$Rq=W52|EsBnE z&NN;OKHGDg*r1%s-Pq&m@2iR>srIqBGcG8ev7CqtokV#zG0v`fvNy?O*7u?R!mJ=C zo#47f@6AnI1DQqKFhqMDU9v6IF}-F$X?T4?r8h8rIh@gMj*Zun#CV_$0kT(JuTE4! zI8ZK+t*OUv_I54&A~=Al+WAQPF{I;o18?KOq`(FiD~EMD)u!XG(I=Xk{WMhf|NLQx zWT(0A@$+n!<6$j^|FAaEezfn&bnS9WmmnhCv0m#pd9kK8t5^KRt?{Ip#XZCe(O}GW#&`4uwf8Yb6a1k0A zn}>PKf{(Uyb?nsQa8k*EMP$bm(0cvUMws15C0r zDYh5pqbMF5<*AW&h@;}&D|v@&8pt8u8%X!&u_@Gr^gd>klv}%WEQ)_+$Ql4SKQjUZ zQoC^FOQ|=IB3w$GSJtigOQMTG_uA;?xE@k2n66J%P%0s2J9J$HF)U8>UO!g2a#Ecu-pwgEY#$Ln9HKAhisB%A2;Y;BZ|cikX)UVUhu4bi&AZQx7SF> z&47xvsl(3%tMiXOk08Xw#cLu5Tk;;?vcdW_tWHmo{Z%s8_q0OE`>jFwjFQYWF*qh{ zr%QsOWlJuwYbn^$To6yj1e1$U3%*5&itxg-q1^)IyatbDDyh#)B+v$5cI(ify~CCj z@+-Cbm=-icFt08r^&m3}Z~q2LrIc@w?7%svjr}5qMwmQ2i97-gYO)VXNZwQJ+;;SX;iX=*N$3F?{vhQ-wunRj06#ZV4y11cc z-BJ1C%05Khe%*B#_5RSd>P40-Q?;)0?D;un&EW!i@z$;c(^Z0%#n@QwyUX$o^_(FY zWctDMvaqw0%CBcleXSV-UY1auVjg$>v7Yuc)^0H>PV{OLfs1k>>fZ&BaR zh=xT{R88F^Ooy^@A#CgAmunt(mE*L&+td%77;SM^iTg37 zJ_#aD+WM&*b1hFLe5X2t@S(GO!}XIFEp~xO>-#9(-+4Nen zm_Kht5QM%&K3#B{U!`5Ca-iP}kzS;O7@w!xN5A)s5;Z465-ovoi$g(W;l-JP{+8Iz zCuDzp2sNfsKdF1GiQjSHLt{R5TwR=o%@ogX{7h6z5xitYd|MYzP(#uaSgRDqku2MSGM0xZq^ZSX- zPC*_rm210}GzNhz%TCgH2I2?pupav*a`&R+AH!~TLmgN~fLCNkgJM{dYooO!x;nF_ z*~;hmOW%p?F#HDaFjlktry}$f9wSeFdUet55^CrBQFQ?p_yzwsD|eB=a+w(aQ3 zkjSfFX6ev|mN86X%n0`RUW#}KCFbQ8=9n+G|BPzrUx?>+b&+K2Gjcw=xeeO1$$4=4 zDyoOu-EFTfyjOlw{bEbTydzGg;ljDXGu?#1pjTF)$kE!?K>b}LUU={4RFS)~Sm{CZ ziE^6Q2O{Y8Lwu|^_QQ*45GujabI%W1N*5a5u1{GFs7kDdpZGoYM5Pk6^=v=CLPS8- zjoQHNpZikR9b_q0muXpoyFR{>x+aA~gMvHWVNdm&>Uu;t3z~)F8B-=QERaR>i+A>B zQ34>vtMM6dL(XRdjCVmHvb=>Q@>WaYQU*y<^pBrlS*l{&wcoIaNN2nc4-g{n#KLYf z#>IC^vi{hb%6_83km^C*L1PtoiM1pvmfD6O_LY`~!@91EbJTF@@zSnksx2HZpUzup zgW=k7xRl1?k#^TzFR@hu`eXfr5)v-i@ZOBteaMhx>PGg>kskI+2Ym#T*cjIze?eoa zoQNRz+TqT>^iFGZap`muyB#lltxO5!c7Z<_8ejF1$Rq$3e*8Uxw~@6SS&86>68>Ro zJ>NxA3JM)D{g|~Ht|5NEhsMRXHKaT0(a7bU8HJ5{Y(rNpfnh9*<_tLtY2w@2=Lq86jUx?HR!ydG3%KBJ~1hLRO3))6Dk|gM=U=c&YwHxZ>sCm-CCt|I5$?tnq z$hlB%JTSoPOMU7@XI3ZNR~Girb!j_*LvrMtcfRAw{QH{IWbi&>PpJNI{mVY7^~B)j z1zd})e(K-;`?%w^Pr{4$c@;j;v;<00J;On@$y$M(t)%G3XZZE|?juNUQ@7(kZ{;2q z>JV71M+p>`z@S^BoD?nM^1P@fw{lg8OlC4(ap_k-PRKEjVdS$^tx;|2{TfT+UAMLW z3nKN?cK^6U^gtlTM9d%E#qFueqKt3 z8Q#CurZV*pBp9Nkq~_G`#&B%LY5IQSOGuu07V0kB@mzh#kj9a{g|goqN)uaEKx7C~ z)X3@D4>%C-KiH+T8(=TN91@@HWM6(7R2?3OB8gAN*!UyZM=(lXP?h}0wsjFq zr_E%L^v2D}mCzWQzKAWAbJ6WMOd}V&Q6+@kBb8fnRuOfr#@{b(@i+MfuT`0yxsx&x zDxrdk@b4s#<)76Lv8^I?qjRP{=ev@6ZCA9wX!#v8lDjZ5T2+jp9G2`UA@9s8(j zdFt=jREJnE(Rs5vb)*=`vJkDQmPb0AQb-Q(%aB=F*rmZz0f~$(lL;5KvC2oZ`})b! zN|%Ebp(Ee?*IboD5$Ix(zD5Pp1UGr%o@7rt`#~;$hT5;0{0Mz)>^x%BlC=0$Xs(0#yXObmRN+-4cU1X_mIdn+LLL}K!Bh*C}^Z3Kg1Q~q%z&r zldo8%@$x~XaJ7=sKLu`lABd{t%;rxT4orKE*Si(Nx!h5#mpFANCX7 zy#Bps<4I_y*>;gsd7HtW_vX@g;f*h)oSfrL@Nee>V^x!gA)Bk{P|QGT$&!PY%MElc z$Wiz!sjy`6SU^Tdys}<|3HNgb+Yz_Xx94Pebb3uDQ$2_oXe&^(Saks?Ec9%ja2m zF3yM@7QXS;XZ;*2oZhArGz~LbYeo=0IV)ur;M2b1y`Ur4E_xPH8Q#k*`B0cBTw=6( ziA+)vMs`}Tsv{ALx54QEkZm)c#eB##I$6kmEbv31M7Fn3r0&Xj(yAKjCJHXv*t6qk z9A|I442$niD%=B2S z?B;2#qe>j-GN2EIybvQNhGtZcYdx!b_+h$b@IjWXDRI=ZX%Y8F5X!cjkG0{_A{Hu^ z!7Z|DXavFBC7+uHbS%?$D4Rdt^by@cs9(4FrJEKEI@q(mw|u~tok13;fKa*f-U#Jp zryVWhvy9YYCx833hM)-UF^)RUhoF+KsUf@2-P?sA(e|5QyVy;l_23mlv{O1ibucP} z-hY*qesS4u*m%C&(lM*Yj9)%R;}m{$AL$d6)s$-CQkE^9aBk5}LzDFO0;f>882^9z zMDSKoJDzO8>nB1Rn_t4Q#64VR>b()g|GH%y&>|zqH|X>b72X`>Llq?j*p5l5vG~Be zsgy|peZfC0y$9NIlT~{>!9L>b$C*}@>) zM9r=JVwYUEd}{+!mrQTT-HktEA&86hqI|fNeCx5{^mi+5OKyg@wHx6>_FE-WWgMID zslwkB?xxtUxiq?z?2<^%3C^9Y_g)s4!-jp-Hr{7hNCtuf2+%F{Cd^L|z4fplSfA?o z+P_c9jCFDBdj&bB1?e?+b|#^Loy)hei_Qdv#qA|>AB(LqrQRRMV!z)HrB;UH>qh#Eqo)Ysz0_LAGmF>DKdst&*)# zE)j%i`G`@%*WzMD_Tz&>UV+NKYZ6%eojKw@ey_@R-P>s8$t@NtoY1pW1lpry4|(A2Q@Fy;}>c= zWiVH#zJI@b!g<-qJkXR@PJIM@9kmdVPd?;K<6_<0fzD{2W|`N1jW5ZRonf1kbJjd= zJ5gL7`Da>Pci6|vnh`Sz=+M|zieUOO|)Y!-bkvy`iZe8CZ?tKf{hpR z*Z0O8@`>4VonLt<%l^EQ*jGB(&kARpDm_vclhhj- zR?}CQua?-G(%h=mX{1&~-q-vghZoj;ZIwMMIYqS)*QFSaFSO5X@ySj0Um7WORbsdRF8gdQ5ISP5SrV;5;5cU4PF6ImO!htiR@}CPy*$E_8h3;Sinj2qB;2m$DJT-7^w|Edf8K5ArW4NdtnL@*; zoitjP-V&4)qXPt+6W<)I%odwhM=RsH4+(Q$%~jS+(Ofw)*nLL6Aw%X-K_9Xx!lzOE4S0mV4Ii6Oy-C7FAK9%~EIF zTfCEwzZaim-_ZyW(O?wxtI;BpoXKTjHaR2saiuKP56f0=Dz8z; z9F}@Rq=%#TXsGaqK+H~ULB1GA1ALtSvFVpk~atws5ZG4gVe~f*1Ak}^Ueo1y5 zRLDLm71`OlV?>CSSs^o9WQ6Qv9f?q8QWDA@Av0uVZyDJuWUt@rbKiH}JrC7i#^XPvdnQR-U!S&-{W@S)UTpNiRkF*Pg?rLm(BV1j013U=r(dzpE{ZZ zrO*$Tds!Orr#8$+J-bk zMUq_Hl*?!*qaIh+qSIMCH!ZYP;MKDC3SWOgt7ue^G?QnIm{a9p!;F=FYReU)ALoCa z&wV^B*HU~ztJmccJ2`7T*TA2R1-874^>J93zLu7jPKs^bxt~A5f~SK;j#of8KOC^9 zQlvS%v$u8p&x>}DQjz>1988w%f&!^fR{hBo!nbmNpKQLkC(xnYO)@#-IyZv;{(_m~ zMBgY)E0dIEA`5hdI-mUY7+=%H+k3{K<`Z!I-Z`$bA-X>glvsj7EGV4m0&7_R{#Lfc za101eqbf)g{JU%VbvFO_aMB;n;x#17eonof_v;5Nj^RNdaNoK4x9UId#m`ein4CXv zO&!~pqgFSc{yOP1A^3s=51+hx{py37>Z@zY%9pY_BHDV|M^{t(!ove^danKYuYVon zuR~=hvUC=d8aJ7jz4>+P|LxcP{zrsIqBM)IBy$;gQ;8^9}NB7O;5{@6rzy5a* ze&p_xo4*&&$0i!6;}d@1%R>u^Ntb8>xVo~pFf|W zOCs=U1NG+KU$^|qX<~3>u7hmfYp1v0ss4Q;(po1Uf6yNz*#6NT(Dka}pi$QTOwP|m z?Z2OBga}^hSgnUe@#0X;OjoMjgy?(nzYqG~eh&F%TMNaDTA>RP^#)%%P5#Rb|LqQ( z3CGvS$>vK3b+b|S%1rM6;j78vrAmZ~zvW!har@5?!Ec90dGaZJlqhfuq*)AI{End8@6mZJpRAO@;rZ88c&7t(R;0&8YDMwd%1x-D6_!v zESYC;Fqs$#W-LHf?%SJj#fY?@z^hSYGS(16&a;qIytUo+uQYEZj{T2I=b5`4IQ;bH ztt?>XwL#(*E?EsqaV-$u!NWH&sw|q5bR+u769W*9+m{4sE4XhT%MSsKe=CT6Ef9{) zvUonfW|IY}!#$UkSU5Nu1TA`o{ceJz$Fk_E|ur8yE#NzI(|MC zWkGOMp&dl&N7$)xn_ef>`A6K|q=0t9Y8~Wo+VGAErqvf*&oORl%Foa%?wSuL3-e#2 zvxg}pp?fKr97(lwLsG4R^i$N%F4rzGKN$Npvb z$_Mc2(eN2%&; zk^b230!cq2uXkzUv)adp1Gn+xU>YZ=Iq{L(zj{;m+;d0O&P1t#Ze zSj@g{sWE$Oj1U$au|n|PAjiN@b8tZ%nx%2$Os;byHIEDzKoS$su{-Qdp$%lhU_M-) z{yaoutw7se3sHg^|`6JRg2jm?B?>@QY@BYujxsy~X5kniG;_8)7+aPQE&Y{<%d_~ds`Z1%y8 znoOcCc9=C*j^wm$IkZqA-d>nuePm_DJ~ucn+&3mQ%Lmm#+0SKwh#5lJc8pHP{CifS0|4 z1;--E;QTGj5k?C7cQ#itKo%HgxV_qaTTGq6b{H;1L5$Uh&$nFXF9_L+J5X-Q?0$9M z&ag)tnV5*lcz4{xDhe0xtt9Lhikh)~py*7yS&;VbL%rOgIEOB+cY`gG+l|=R9-}C| zIgp1rHuG&M{~+_CmLICw)H}Y&r*BW;6Wm z$d-0|?s2-fS-L)CKPG$h1vAXK*!3e3sr`^24T@gl1i zR*=t(|G1FRh~Z9@HqYSnuT{m1I%MWqR1Ll9py4Bm83El;ug=3MKx?oAtZPpFyzX-)|5`DusFag-TVC|`C zxr|4!u8H<7Twf8wU+)C-=+^TTOyWJIKgx4fKwxVVmK*G8!n&?oQyp}?c;Q1DFTt%h z8}MN3-E@{;o$cm6+nUj8_;)q@FI(iYOrq`tUd375Rf0)H&`-qK;}h<|P|7?$5n38$ zZIh44(ZbjQULH0<;T{f=8PkWMu>u`(M^)ioqQ@z3+U#^^=yZ2ZUR4dV@)demwlPXN z*(0(s?5!7vpbO+I3{se!F9hVA7<_lb5lI27fga+EMD!gBbTyti#PrNW$5LYL0`D=i zj$_$yJopqEA)olGiO}jcCs>cNvZzG!ROAJPllmX;x5X_@2#~Xr-3U$tA=ZM>^iW9^ zj)<}vKlYw*0V>sT$k~J)-MV;)T)4b5h&p&_7Z#@GC56DumpIXS^X_-^Gq_KU1u`#_ z=PRfEoG{Fk4X6R(E+TQ-v5V2H^#NmK)HT^K*IppeB{d?roOb9|NtVh9#%5xi8`}Nj z0uLMQ2PM!bbkkx~o3q!LF>8SdRsp+bLWg zQv2}Q9?|!qp`wEFanEI}=#%{p|=sXj=IlbZaV!pV#lL9v!>j!%8)!SNH+;)G%aXz>n@R>RvnF+@ILsf}Ss z;wqsG@hbNmBMifPaq;0o`l4HMBJ0x=s%$e!WaRt|7ng$fT3a$% zXJWxiN7d=O>M67ow&YGc4O$W_egg1v*BCmPM{dKY;~8(L6}mvvYKS;t+P7s%ji*u& z(vHgS@9hqLBr=+eceT#@vR#zRFn*q+Y@Gay^+SaR9mKlvQ#yA!o4;psO~752n43qG+I?INxFu;AVphD zQcR^7dxnd1nMqx6uCk{KBdqW<;*Rv`PZr;9C1_D!=q`zsF5|7K;SW|+iA<%S^i$MZ zsM-@U+Fc1elUPL|HBgGGbomWY`hA<9Po;^i~mHkz_n|KLwJg z&V^SY7G*>3d!F-yPdCk7x~x0ZSjuKf2e8=`3-f(L-Y5G_IumQu>Xyh*NrWcDBQv|^ zMA|F%WM@Y-YbLOuIkdsLHdQTx*!rNq+`%$N1v+c-@Mhm@T$ny5% zAXNjFWweUEzA;VqXe+Bf!zg~FsWC{roYT*tXHon%cu(w%?`fgkB`@eiQ<6(d%857z z^KLOLjj47}+?M~Ng}#Eq8+ggH&p6~isa7(1Z=+XwODnpf!p5ixd zv{`vi`Rd}2JDNP}4&R$3Lf4%9Q0FggUATqM5_i_^hx{{}vP#QkGQ>kw@2%o($uesn zrmrTqGNm8CaGwx$1x0L}Y4w05X5hQ5T;crAOqb(-s;NB#vd5DtuQ0zY2feq5pze$&`#7!J_9+g!l0GN{H+}^FCm5Y`gp+ z6!lyoN4&y4#Kf3#Z0NCy$t$uw!x*>QLK>%QZ>!{U%z&R~@#)j{va z7_lF`Wl?xZ9HdgD&aZh>j@V~sT|S9wk^Zkw^kYH8ErENj*}ms@4Ao2cPG!dzX;k7! zhn}DErS_7`4a>im;I-fRDlAvCgpxjv zAVkRU(se(U@{)73vMqEbu_Hc*6S#a=M~u)6mGi@I^zB@SgpHT)w#Df|u`)L?=Sj$9 z^EGR$oS>QzpQ>6QVaAvDmP67e z$4uW6Z0pz0a*mL98fr$z>*YuDFQY?Va=JWp-*>Why&AwhR;&J4^vu|KN~tZTg)(dI z)~p~0CZFvN=pdqIa4Y@&66*tF6NG^e7~Dku5bH_H5ZO=|?{T0Ri)x|5zknXHZ~5lF za74!UzF||0(1Ww}J1!HRBYO(>PuDE}m~NkkULlm;vuZcuOe;`yMJ9IWB?->l4Z+>g z_{me;_}}^D(JSN5@4lxSetfQ3v*K&(fB2b=NEbDu@|Hk+H3DoVV6!*i@Cv!=h`Ay%_I z%WFijk}}+sDRjzlM^d=C3b#3B*IMR=PqI1tGGx=l<_4?J#z(oRvs2cmC2HPKm{2>c z1PBVFe4o^zZtMu#3M1_Uv828XF<}=ecgb+uyH;ayZ+dcp8k&g-=eBNBbxz708?oSL zAF65vOC-AZQo9XAexMGNhI4hf`)bil+0O#zo1K+P`iIO0A8S)TVO8+U!>QjW5bktmsp45AQNi3!icDM*=74O)y}J(H#1<~lgm=miM}B@j{!oHgSyBR#vtBE zDw@AZ>Ja5KaAq366+FC2GkCgHrbIyc8iyZCbr3$ZkssY$k-lp?#*>DF)^$TjJm6>? z%)`-BPd7c(7Me-g$EtsFtGOEWlOWDNdTx#XNY{}jhz0UPS z{X#@h*?M$Goz}7>>>LN}LG*1KtQMus|p?~ROK+fJs@r@=YSgTPM8urtM-+&t!&Dzs?7PU56wZUETX9H?7v{32thn5 zQ>A`Nb&p@gOTCz9L`pnEYNKp-mvvpPoHS{r6o{B?($+|>=4&(i zoG*TG=;WH&r-!r2r`R%1tjyN^c^D-c&%2}o?)NbjKNi1!m7C-d($?1q-4dm83x8q( zvy)t-zDM<=bnPxZb0%hKEzDK<3%0ALI!G*FG;Nyt{x8^#1#jF_T6<;Uyw`muDH?N5 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 Date: Fri, 5 Jun 2020 19:01:22 +0530 Subject: [PATCH 011/329] python shebang, Readme for circleci and local execution --- .circleci/README.md | 33 +++++++++++++++++++++++++++++++++ run_circleci_tests.py | 6 ++++-- 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 .circleci/README.md mode change 100644 => 100755 run_circleci_tests.py diff --git a/.circleci/README.md b/.circleci/README.md new file mode 100644 index 000000000..15d9b770d --- /dev/null +++ b/.circleci/README.md @@ -0,0 +1,33 @@ +# Multi Model Server CircleCI build +Model Server uses CircleCI builds. This folder contains the config and scripts that are needed for CircleCI. + +## config.yml +config.yml contains MMS build logic which will be used by CircleCI. + +## Docker images +MMS uses customized docker image for its CircleCI build. To make sure MMS is compatible with + both Python2 and Python3, we use two build projects. We published two code build docker + images on docker hub: +* --------/mms-build:python2.7 +* --------/mms-build:python3.6 + +Following files in the _images_ folder are used to create the docker images +* Dockerfile.python2.7 - Dockerfile for --------/mms-build:python2.7 +* Dockerfile.python3.6 - Dockerfile for --------/mms-build:python3.6 + +## CircleCI cli local +To make it easy for developers to debug build issues locally, MMS supports CircleCI cli for running a job in a container on your machine. + +#### Dependencies +1. CircleCI cli from - https://circleci.com/docs/2.0/local-cli/#installation +2. PyYAML (pip install PyYaml) +3. docker + +#### Command +Developers can use the following command to build MMS locally: +```bash +$ cd multi-model-server +$ ./run_circleci_tests.py +``` + +> To avoid Pull Request build failure on github, developer should always make sure local build can pass. diff --git a/run_circleci_tests.py b/run_circleci_tests.py old mode 100644 new mode 100755 index 960d0abe5..31fdd4381 --- a/run_circleci_tests.py +++ b/run_circleci_tests.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + # Make sure you have following dependencies installed on your local machine # 1. PyYAML (pip install PyYaml) # 2. CircleCI cli from - https://circleci.com/docs/2.0/local-cli/#installation @@ -13,9 +15,9 @@ job = 'python_tests' circleci_config_file = '.circleci/config.yml' -processed_file = 'processed.yml' +processed_file = '.circleci/processed.yml' processed_config = {} -xformed_file = 'xformed.yml' +xformed_file = '.circleci/xformed.yml' xformed_config = {} xformed_job_name = 'mms_xformed_job' blacklisted_steps = ['persist_to_workspace', 'attach_workspace', 'store_artifacts'] From b71f6627b89d6cc8b330d00e72bbb610ca59cb33 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 5 Jun 2020 20:02:10 +0530 Subject: [PATCH 012/329] workflow name and job name from cmd line args --- .circleci/README.md | 3 ++- run_circleci_tests.py | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index 15d9b770d..ccf29d277 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -25,9 +25,10 @@ To make it easy for developers to debug build issues locally, MMS supports Circl #### Command Developers can use the following command to build MMS locally: +**_./run_circleci_tests.py _** ```bash $ cd multi-model-server -$ ./run_circleci_tests.py +$ ./run_circleci_tests.py build_and_test python_tests ``` > To avoid Pull Request build failure on github, developer should always make sure local build can pass. diff --git a/run_circleci_tests.py b/run_circleci_tests.py index 31fdd4381..d623b0e7f 100755 --- a/run_circleci_tests.py +++ b/run_circleci_tests.py @@ -8,16 +8,22 @@ import subprocess import yaml import copy +import argparse from collections import OrderedDict from functools import reduce -workflow = 'build_and_test' -job = 'python_tests' +parser = argparse.ArgumentParser(description='Execute circleci jobs in a container on local machine') +parser.add_argument('workflow', type=str, help='Workflow name from config.yml') +parser.add_argument('job', type=str, help='Job name from config.yml') +args = parser.parse_args() + +workflow = args.workflow +job = args.job circleci_config_file = '.circleci/config.yml' processed_file = '.circleci/processed.yml' -processed_config = {} xformed_file = '.circleci/xformed.yml' +processed_config = {} xformed_config = {} xformed_job_name = 'mms_xformed_job' blacklisted_steps = ['persist_to_workspace', 'attach_workspace', 'store_artifacts'] From fb14f40a811c6ae220f86994c01313e0db995a8f Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 5 Jun 2020 21:28:49 +0530 Subject: [PATCH 013/329] Updated readme --- .circleci/README.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index ccf29d277..40bbe99b8 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -1,10 +1,22 @@ # Multi Model Server CircleCI build -Model Server uses CircleCI builds. This folder contains the config and scripts that are needed for CircleCI. +Model Server uses CircleCI for builds. This folder contains the config and scripts that are needed for CircleCI. ## config.yml config.yml contains MMS build logic which will be used by CircleCI. -## Docker images +## Jobs +Currently following jobs are executed under "**build_and_test**" workflow +1. **build** - Builds a frontend/model-server.jar and executes gradle tests +2. **python_tests** - Executes pytests from mms/tests/unit_tests/ +3. **api_tests** - (NEW!) Executes newman test suite for API testing +4. **benchmark** - Executes latency benchmark using resnet-18 model +5. **taurus** - (NEW!) Executes taurus tests for performance + +## scripts +Instead of using inline commands inside config.yml, job steps are configured as shell scripts. +This is easier for maintenance and reduces chances of error in config.yml + +## images MMS uses customized docker image for its CircleCI build. To make sure MMS is compatible with both Python2 and Python3, we use two build projects. We published two code build docker images on docker hub: @@ -15,7 +27,7 @@ Following files in the _images_ folder are used to create the docker images * Dockerfile.python2.7 - Dockerfile for --------/mms-build:python2.7 * Dockerfile.python3.6 - Dockerfile for --------/mms-build:python3.6 -## CircleCI cli local +## Local CircleCI cli To make it easy for developers to debug build issues locally, MMS supports CircleCI cli for running a job in a container on your machine. #### Dependencies @@ -31,4 +43,4 @@ $ cd multi-model-server $ ./run_circleci_tests.py build_and_test python_tests ``` -> To avoid Pull Request build failure on github, developer should always make sure local build can pass. +> To avoid Pull Request build failures on github, developers should always make sure that their local builds pass. From 48392d1eb482f76ad632b2fc6bf8a76829320898 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 5 Jun 2020 21:35:31 +0530 Subject: [PATCH 014/329] Update README.md --- .circleci/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index 40bbe99b8..d6f6308f4 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -2,24 +2,24 @@ Model Server uses CircleCI for builds. This folder contains the config and scripts that are needed for CircleCI. ## config.yml -config.yml contains MMS build logic which will be used by CircleCI. +_config.yml_ contains MMS build logic which will be used by CircleCI. ## Jobs Currently following jobs are executed under "**build_and_test**" workflow -1. **build** - Builds a frontend/model-server.jar and executes gradle tests -2. **python_tests** - Executes pytests from mms/tests/unit_tests/ +1. **build** - Builds _frontend/model-server.jar_ and executes tests from gradle +2. **python_tests** - Executes pytests from _mms/tests/unit_tests/_ 3. **api_tests** - (NEW!) Executes newman test suite for API testing 4. **benchmark** - Executes latency benchmark using resnet-18 model 5. **taurus** - (NEW!) Executes taurus tests for performance ## scripts -Instead of using inline commands inside config.yml, job steps are configured as shell scripts. +Instead of using inline commands inside _config.yml_, job steps are configured as shell scripts. This is easier for maintenance and reduces chances of error in config.yml -## images -MMS uses customized docker image for its CircleCI build. To make sure MMS is compatible with - both Python2 and Python3, we use two build projects. We published two code build docker - images on docker hub: +## images (WIP) +MMS uses customized docker image for its CircleCI build. +To make sure MMS is compatible with both Python2 and Python3, we use two build projects. +We have published two docker images on docker hub for code build * --------/mms-build:python2.7 * --------/mms-build:python3.6 @@ -36,7 +36,7 @@ To make it easy for developers to debug build issues locally, MMS supports Circl 3. docker #### Command -Developers can use the following command to build MMS locally: +Developers can use the following command to build MMS locally: **_./run_circleci_tests.py _** ```bash $ cd multi-model-server From e82aa406ad6abe951de436a447cc55063da86c4e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 8 Jun 2020 14:13:55 +0530 Subject: [PATCH 015/329] integrated model archiver tests --- .circleci/config.yml | 21 +++++++- .circleci/images/Dockerfile.python2.7 | 51 +++++++++++++++++++ .../linux_test_modelarchiver_integration.sh | 9 ++++ ...linux_test_modelarchiver_lint_and_units.sh | 9 ++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 .circleci/images/Dockerfile.python2.7 create mode 100644 .circleci/scripts/linux_test_modelarchiver_integration.sh create mode 100644 .circleci/scripts/linux_test_modelarchiver_lint_and_units.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 006a7d8e8..6624c847b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -119,6 +119,21 @@ jobs: path: /tmp/cci-taurus/ destination: taurus-results + modelarchiver_lint_and_unit_tests: + executor: docker-python-3 + steps: + - attach-mms-workspace + - run: + name: Execute lint and unit tests + command: .circleci/scripts/linux_test_modelarchiver_lint_and_units.sh + + modelarchiver_integration_tests: + executor: docker-python-3 + steps: + - attach-mms-workspace + - run: + name: Execute integration tests + command: .circleci/scripts/linux_test_modelarchiver_integration.sh workflows: version: 2 @@ -136,4 +151,8 @@ workflows: - build - taurus: requires: - - build \ No newline at end of file + - build + - modelarchiver_lint_and_unit_tests + - modelarchiver_integration_tests + requires: + - modelarchiver_lint_and_unit_tests \ No newline at end of file diff --git a/.circleci/images/Dockerfile.python2.7 b/.circleci/images/Dockerfile.python2.7 new file mode 100644 index 000000000..4d86a0490 --- /dev/null +++ b/.circleci/images/Dockerfile.python2.7 @@ -0,0 +1,51 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. +# + +FROM awsdeeplearningteam/mms-build:python2.7@sha256:2b743d6724dead806873cce1330f7b8a0197399a35af47dfd7035251fdade122 + +# 2020 - Updated Build and Test dependencies + +# Python packages for MMS Server +RUN pip install psutil \ + && pip install future \ + && pip install Pillow \ + && pip install wheel \ + && pip install twine \ + && pip install requests \ + && pip install mock \ + && pip install numpy \ + && pip install Image \ + && pip install mxnet==1.5.0 \ + && pip install enum34 + +# Python packages for pytests +RUN pip install pytest==4.0.0 \ + && pip install pytest-cov \ + && pip install pytest-mock + +# Python packages for benchmark +RUN pip install pandas + +# Install NodeJS and packages for API tests +RUN curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - \ + && sudo apt-get install -y nodejs \ + && sudo npm install -g newman newman-reporter-html + +# Install jmeter for benchmark +RUN cd /opt \ + && wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.3.tgz \ + && tar -xzf apache-jmeter-5.3.tgz \ + && cd apache-jmeter-5.3 \ + && ln -s /opt/apache-jmeter-5.3/bin/jmeter /usr/local/bin/jmeter \ + && wget https://jmeter-plugins.org/get/ -O lib/ext/jmeter-plugins-manager-1.4.jar \ + && wget http://search.maven.org/remotecontent?filepath=kg/apc/cmdrunner/2.2/cmdrunner-2.2.jar -O lib/cmdrunner-2.2.jar \ + && java -cp lib/ext/jmeter-plugins-manager-1.4.jar org.jmeterplugins.repository.PluginManagerCMDInstaller \ + && bin/PluginsManagerCMD.sh install jpgc-synthesis=2.1,jpgc-filterresults=2.1,jpgc-mergeresults=2.1,jpgc-cmd=2.1,jpgc-perfmon=2.1 \ No newline at end of file diff --git a/.circleci/scripts/linux_test_modelarchiver_integration.sh b/.circleci/scripts/linux_test_modelarchiver_integration.sh new file mode 100644 index 000000000..4187a1bbe --- /dev/null +++ b/.circleci/scripts/linux_test_modelarchiver_integration.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd model-archiver/ + +# Install model archiver module +pip install . + +# Execute integration tests +pytest model_archiver/tests/integ_tests \ No newline at end of file diff --git a/.circleci/scripts/linux_test_modelarchiver_lint_and_units.sh b/.circleci/scripts/linux_test_modelarchiver_lint_and_units.sh new file mode 100644 index 000000000..5c8c74b7a --- /dev/null +++ b/.circleci/scripts/linux_test_modelarchiver_lint_and_units.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd model-archiver/ + +# Lint test +pylint -rn --rcfile=./model_archiver/tests/pylintrc model_archiver/. + +# Execute python unit tests +python -m pytest model_archiver/tests/unit_tests \ No newline at end of file From edd3e9df5fdf44afe681b6be05c087df0dd73ab2 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 8 Jun 2020 14:26:51 +0530 Subject: [PATCH 016/329] fixed --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6624c847b..873ef4d33 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -153,6 +153,6 @@ workflows: requires: - build - modelarchiver_lint_and_unit_tests - - modelarchiver_integration_tests + - modelarchiver_integration_tests: requires: - modelarchiver_lint_and_unit_tests \ No newline at end of file From c0dcb1e1a5a577017cc1d7a0d10a17b014869f1c Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 8 Jun 2020 14:30:09 +0530 Subject: [PATCH 017/329] execute permission --- .circleci/scripts/linux_test_modelarchiver_integration.sh | 0 .circleci/scripts/linux_test_modelarchiver_lint_and_units.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .circleci/scripts/linux_test_modelarchiver_integration.sh mode change 100644 => 100755 .circleci/scripts/linux_test_modelarchiver_lint_and_units.sh diff --git a/.circleci/scripts/linux_test_modelarchiver_integration.sh b/.circleci/scripts/linux_test_modelarchiver_integration.sh old mode 100644 new mode 100755 diff --git a/.circleci/scripts/linux_test_modelarchiver_lint_and_units.sh b/.circleci/scripts/linux_test_modelarchiver_lint_and_units.sh old mode 100644 new mode 100755 From cb57c51d5c671cd978c6c91bc816f80d2683a3b4 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 8 Jun 2020 14:33:24 +0530 Subject: [PATCH 018/329] modelarchiver-unified lint, units and integration tests --- .circleci/config.yml | 17 +++-------------- .circleci/scripts/linux_test_modelarchiver.sh | 16 ++++++++++++++++ .../linux_test_modelarchiver_integration.sh | 9 --------- .../linux_test_modelarchiver_lint_and_units.sh | 9 --------- 4 files changed, 19 insertions(+), 32 deletions(-) create mode 100755 .circleci/scripts/linux_test_modelarchiver.sh delete mode 100755 .circleci/scripts/linux_test_modelarchiver_integration.sh delete mode 100755 .circleci/scripts/linux_test_modelarchiver_lint_and_units.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 873ef4d33..ab5051d3f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -119,21 +119,13 @@ jobs: path: /tmp/cci-taurus/ destination: taurus-results - modelarchiver_lint_and_unit_tests: + modelarchiver_tests: executor: docker-python-3 steps: - attach-mms-workspace - run: name: Execute lint and unit tests - command: .circleci/scripts/linux_test_modelarchiver_lint_and_units.sh - - modelarchiver_integration_tests: - executor: docker-python-3 - steps: - - attach-mms-workspace - - run: - name: Execute integration tests - command: .circleci/scripts/linux_test_modelarchiver_integration.sh + command: .circleci/scripts/linux_test_modelarchiver.sh workflows: version: 2 @@ -152,7 +144,4 @@ workflows: - taurus: requires: - build - - modelarchiver_lint_and_unit_tests - - modelarchiver_integration_tests: - requires: - - modelarchiver_lint_and_unit_tests \ No newline at end of file + - modelarchiver_tests \ No newline at end of file diff --git a/.circleci/scripts/linux_test_modelarchiver.sh b/.circleci/scripts/linux_test_modelarchiver.sh new file mode 100755 index 000000000..fc309aac4 --- /dev/null +++ b/.circleci/scripts/linux_test_modelarchiver.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +cd model-archiver/ + +# Lint test +pylint -rn --rcfile=./model_archiver/tests/pylintrc model_archiver/. + +# Execute python unit tests +python -m pytest model_archiver/tests/unit_tests + + +# Install model archiver module +pip install . + +# Execute integration tests +pytest model_archiver/tests/integ_tests \ No newline at end of file diff --git a/.circleci/scripts/linux_test_modelarchiver_integration.sh b/.circleci/scripts/linux_test_modelarchiver_integration.sh deleted file mode 100755 index 4187a1bbe..000000000 --- a/.circleci/scripts/linux_test_modelarchiver_integration.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -cd model-archiver/ - -# Install model archiver module -pip install . - -# Execute integration tests -pytest model_archiver/tests/integ_tests \ No newline at end of file diff --git a/.circleci/scripts/linux_test_modelarchiver_lint_and_units.sh b/.circleci/scripts/linux_test_modelarchiver_lint_and_units.sh deleted file mode 100755 index 5c8c74b7a..000000000 --- a/.circleci/scripts/linux_test_modelarchiver_lint_and_units.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -cd model-archiver/ - -# Lint test -pylint -rn --rcfile=./model_archiver/tests/pylintrc model_archiver/. - -# Execute python unit tests -python -m pytest model_archiver/tests/unit_tests \ No newline at end of file From e3aceee2efe56dd90759cbf3a27a7f0e4d301f81 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 8 Jun 2020 14:38:51 +0530 Subject: [PATCH 019/329] model archive job independent checkout step --- .circleci/config.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ab5051d3f..3a31dbfb2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -122,26 +122,26 @@ jobs: modelarchiver_tests: executor: docker-python-3 steps: - - attach-mms-workspace + - checkout - run: - name: Execute lint and unit tests + name: Execute lint, unit and integration tests command: .circleci/scripts/linux_test_modelarchiver.sh workflows: version: 2 build_and_test: jobs: - - build - - python_tests: - requires: - - build - - api_tests: - requires: - - build - - benchmark: - requires: - - build - - taurus: - requires: - - build +# - build +# - python_tests: +# requires: +# - build +# - api_tests: +# requires: +# - build +# - benchmark: +# requires: +# - build +# - taurus: +# requires: +# - build - modelarchiver_tests \ No newline at end of file From decd8a1d723c4bdd6238c9202cdec64dedc5cddf Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 8 Jun 2020 15:37:57 +0530 Subject: [PATCH 020/329] save test results --- .circleci/config.yml | 8 ++++++++ .circleci/scripts/linux_test_modelarchiver.sh | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3a31dbfb2..36c7e7e21 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -126,6 +126,14 @@ jobs: - run: name: Execute lint, unit and integration tests command: .circleci/scripts/linux_test_modelarchiver.sh + - store_artifacts: + name: Store unit tests results from model archiver tests + path: model-archiver/results_units + destination: units + - store_artifacts: + name: Store integration tests results from model archiver tests + path: model-archiver/results_integration + destination: integration workflows: version: 2 diff --git a/.circleci/scripts/linux_test_modelarchiver.sh b/.circleci/scripts/linux_test_modelarchiver.sh index fc309aac4..f6ebb3c8f 100755 --- a/.circleci/scripts/linux_test_modelarchiver.sh +++ b/.circleci/scripts/linux_test_modelarchiver.sh @@ -6,11 +6,11 @@ cd model-archiver/ pylint -rn --rcfile=./model_archiver/tests/pylintrc model_archiver/. # Execute python unit tests -python -m pytest model_archiver/tests/unit_tests +python -m pytest --cov-report html:results_units --cov=./ model_archiver/tests/unit_tests # Install model archiver module pip install . # Execute integration tests -pytest model_archiver/tests/integ_tests \ No newline at end of file +python -m pytest --cov-report html:results_integration --cov=./ model_archiver/tests/integ_tests \ No newline at end of file From b67cb32b6cc010f823d88273ff64b2dc226cd648 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 8 Jun 2020 15:42:44 +0530 Subject: [PATCH 021/329] no results for integration tests --- .circleci/config.yml | 4 ---- .circleci/scripts/linux_test_modelarchiver.sh | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 36c7e7e21..3d4f6c31c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -130,10 +130,6 @@ jobs: name: Store unit tests results from model archiver tests path: model-archiver/results_units destination: units - - store_artifacts: - name: Store integration tests results from model archiver tests - path: model-archiver/results_integration - destination: integration workflows: version: 2 diff --git a/.circleci/scripts/linux_test_modelarchiver.sh b/.circleci/scripts/linux_test_modelarchiver.sh index f6ebb3c8f..bdae61a65 100755 --- a/.circleci/scripts/linux_test_modelarchiver.sh +++ b/.circleci/scripts/linux_test_modelarchiver.sh @@ -13,4 +13,5 @@ python -m pytest --cov-report html:results_units --cov=./ model_archiver/tests/u pip install . # Execute integration tests -python -m pytest --cov-report html:results_integration --cov=./ model_archiver/tests/integ_tests \ No newline at end of file +python -m pytest model_archiver/tests/integ_tests +# ToDo - Report for Integration tests ? \ No newline at end of file From d7106d78d7d381467e5d3817db4d3ba0a4e418ad Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 8 Jun 2020 15:46:26 +0530 Subject: [PATCH 022/329] trigger all --- .circleci/config.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3d4f6c31c..d8a4c88a7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -119,7 +119,7 @@ jobs: path: /tmp/cci-taurus/ destination: taurus-results - modelarchiver_tests: + modelarchiver: executor: docker-python-3 steps: - checkout @@ -135,17 +135,17 @@ workflows: version: 2 build_and_test: jobs: -# - build -# - python_tests: -# requires: -# - build -# - api_tests: -# requires: -# - build -# - benchmark: -# requires: -# - build -# - taurus: -# requires: -# - build - - modelarchiver_tests \ No newline at end of file + - build + - modelarchiver + - python_tests: + requires: + - build + - api_tests: + requires: + - build + - benchmark: + requires: + - build + - taurus: + requires: + - build \ No newline at end of file From 85aed62dca1435a0f343f70c7b0c70f75eab045d Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 8 Jun 2020 17:19:06 +0530 Subject: [PATCH 023/329] lint tests on mms --- .circleci/scripts/linux_test_python.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.circleci/scripts/linux_test_python.sh b/.circleci/scripts/linux_test_python.sh index 15e19c185..9af6d1e5b 100755 --- a/.circleci/scripts/linux_test_python.sh +++ b/.circleci/scripts/linux_test_python.sh @@ -1,3 +1,7 @@ #!/bin/bash +# Lint Test +pylint -rn --rcfile=./mms/tests/pylintrc mms/. + +# Execute python tests python -m pytest --cov-report html:htmlcov --cov=mms/ mms/tests/unit_tests/ \ No newline at end of file From f8b2216603a6858c880175a20dfd69d4e927331e Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 9 Jun 2020 12:28:00 +0530 Subject: [PATCH 024/329] test suite runner and report --- benchmarks/monitoring/metrics_collector.py | 65 ++++--- .../monitoring/metrics_monitoring_server.py | 34 +++- .../monitoring/metrics_monitoring_taurus.py | 11 +- benchmarks/monitoring/process.py | 3 +- benchmarks/monitoring/requirements.txt | 3 +- benchmarks/monitoring/run_perfomance_suite.py | 163 ++++++++++++++++++ benchmarks/monitoring/tests/call_jmx.yaml | 11 ++ benchmarks/monitoring/tests/inference.jmx | 84 --------- benchmarks/monitoring/tests/inference.yaml | 24 --- .../tests/inference_server_monitoring.yaml | 28 +-- .../inference_server_monitoring_criteria.yaml | 33 ++-- .../inference_taurus_local_monitoring.yaml | 40 +++-- ...ence_taurus_local_monitoring_criteria.yaml | 37 ++-- 13 files changed, 335 insertions(+), 201 deletions(-) create mode 100644 benchmarks/monitoring/run_perfomance_suite.py delete mode 100644 benchmarks/monitoring/tests/inference.jmx delete mode 100644 benchmarks/monitoring/tests/inference.yaml diff --git a/benchmarks/monitoring/metrics_collector.py b/benchmarks/monitoring/metrics_collector.py index 4f85a42d1..a14c94999 100755 --- a/benchmarks/monitoring/metrics_collector.py +++ b/benchmarks/monitoring/metrics_collector.py @@ -37,33 +37,58 @@ MONITOR_INTERVAL = 1 -def store_metrics_collector_pid(): - """ Stores the process id of metrics collector process""" - metrics_collector_process = psutil.Process(METRICS_COLLECTOR_PID_FILE) - pid_file = os.path.join() +def store_pid(pid_file): + """ Store the current process id to pid_file""" + process = psutil.Process() + pid_file = os.path.join(pid_file) with open(pid_file, "w") as pf: - pf.write(str(metrics_collector_process.pid)) + pf.write(str(process.pid)) -def stop_metrics_collector_process(): - """This will stop already running metrics collector process. +def stop_process(pid_file): + """This will stop already running process . Note at a time only one pid file will be available. """ - - pid = get_process_pid_from_file(METRICS_COLLECTOR_PID_FILE) + pid = get_process_pid_from_file(pid_file) if pid: try: process = psutil.Process(pid) if process.is_running(): - logging.info(logging.INFO, "Process with pid {} is running. Killing it.".format(process.pid)) + logger.info("Process with pid {} is running. Killing it.".format(process.pid)) process.kill() except Exception as e: pass else: - logging.info(logging.INFO, "Dead process with pid {} found in '{}'.".format(process.pid, METRICS_COLLECTOR_PID_FILE)) + logger.info("Dead process with pid {} found in '{}'.".format(process.pid, pid_file)) - logging.info(logging.INFO, "Removing pid file '{}'.".format(METRICS_COLLECTOR_PID_FILE)) - os.remove(METRICS_COLLECTOR_PID_FILE) + logger.info("Removing pid file '{}'.".format(pid_file)) + os.remove(pid_file) + + +def check_is_running(pid_file): + pid = get_process_pid_from_file(pid_file) + if pid: + try: + perf_mon_process = psutil.Process(pid) + except Exception as e: + stop_process(pid_file) + else: + if perf_mon_process.is_running(): + logger.error("Performance monitoring script already running. " + "Stop it using stop option.") + sys.exit() + + +def store_metrics_collector_pid(): + """ Store the process id of metrics collector process""" + store_pid(METRICS_COLLECTOR_PID_FILE) + + +def stop_metrics_collector_process(): + """This will stop already running metrics collector process. + Note at a time only one pid file will be available. + """ + stop_process(METRICS_COLLECTOR_PID_FILE) def monitor_processes(server_process, metrics, interval, socket): @@ -88,23 +113,15 @@ def start_metric_collection(server_process, metrics, interval, socket): if bad_metrics: raise Exception("Metrics not available for monitoring {}.".format(bad_metrics)) - logging.log(logging.INFO, "Started metric collection for target server processes.....") + logger.info("Started metric collection for target server processes.....") thread = gevent.spawn(monitor_processes, server_process, metrics, interval, socket) gevent.joinall([thread]) def start_metric_collector_process(): """Spawn a metric collection process and keep on monitoring """ - metric_collector_pid = get_process_pid_from_file(METRICS_COLLECTOR_PID_FILE) - if metric_collector_pid: - try: - perf_mon_process = psutil.Process(metric_collector_pid) - except Exception as e: - stop_metrics_collector_process() - else: - if perf_mon_process.is_running(): - raise Exception("Performance monitoring script already running. " - "Stop it using stop option.") + + check_is_running(METRICS_COLLECTOR_PID_FILE) store_metrics_collector_pid() server_pid = get_process_pid_from_file(get_server_pidfile()) server_process = get_server_processes(server_pid) diff --git a/benchmarks/monitoring/metrics_monitoring_server.py b/benchmarks/monitoring/metrics_monitoring_server.py index e4267777b..98fd73155 100644 --- a/benchmarks/monitoring/metrics_monitoring_server.py +++ b/benchmarks/monitoring/metrics_monitoring_server.py @@ -20,30 +20,38 @@ from gevent import socket from gevent import select from gevent import monkey +import argparse +import tempfile monkey.patch_select() monkey.patch_socket() -from metrics_collector import start_metric_collection +logger = logging.getLogger(__name__) +from metrics_collector import start_metric_collection, stop_process, store_pid, check_is_running from process import find_procs_by_name, get_process_pid_from_file, \ get_child_processes, get_server_processes, get_server_pidfile import configuration +TMP_DIR = tempfile.gettempdir() +METRICS_MON_SERVER_PID_FILE = "{}/.metrics_monitoring_server.pid".format(TMP_DIR) + HOST = str(configuration.get('monitoring','HOST')) PORT = int(configuration.get('monitoring','PORT', 9009)) + SOCKET_LIST = [] RECV_BUFFER = 4096 interval = 1 + def perf_server(): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((HOST, PORT)) server_socket.listen(10) SOCKET_LIST.append(server_socket) - logging.log(logging.INFO, "Started metrics monitoring server on port {}".format(PORT)) + logger.info("Started metrics monitoring server on port {}".format(PORT)) while True: ready_to_read, ready_to_write, in_error = select.select(SOCKET_LIST, [], [], 0) @@ -53,7 +61,7 @@ def perf_server(): if sock == server_socket: sockfd, addr = server_socket.accept() SOCKET_LIST.append(sockfd) - logging.log(logging.INFO, "client ({}, {}) connected".format(addr[0], addr[1])) + logger.info("client ({}, {}) connected".format(addr[0], addr[1])) # a message from a client, not a new connection else: @@ -85,7 +93,7 @@ def perf_server(): if sock in SOCKET_LIST: SOCKET_LIST.remove(sock) except Exception as e: - logging.warning("Error {}".format(str(e))) + logger.warning("Error {}".format(str(e))) continue server_socket.close() @@ -95,7 +103,7 @@ def send_message(socket, message): try: socket.send(message.encode("latin-1")) except Exception as e: - logging.warning("Error while sending the message {}. Closing the socket.".format(str(e))) + logger.warning("Error while sending the message {}. Closing the socket.".format(str(e))) close_socket(socket) @@ -107,6 +115,20 @@ def close_socket(socket): if __name__ == "__main__": logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) - sys.exit(perf_server()) + parser = argparse.ArgumentParser(prog='perf-mon-script', description='System Performance Monitoring') + sub_parse = parser.add_mutually_exclusive_group(required=True) + sub_parse.add_argument('--start', action='store_true', help='Start the perf-mon-script') + sub_parse.add_argument('--stop', action='store_true', help='Stop the perf-mon-script') + + args = parser.parse_args() + + if args.start: + check_is_running(METRICS_MON_SERVER_PID_FILE) + store_pid(METRICS_MON_SERVER_PID_FILE) + perf_server() + elif args.stop: + stop_process(METRICS_MON_SERVER_PID_FILE) + + diff --git a/benchmarks/monitoring/metrics_monitoring_taurus.py b/benchmarks/monitoring/metrics_monitoring_taurus.py index 6985edb15..ac402c7db 100644 --- a/benchmarks/monitoring/metrics_monitoring_taurus.py +++ b/benchmarks/monitoring/metrics_monitoring_taurus.py @@ -21,12 +21,16 @@ from bzt.modules import monitoring from bzt import TaurusConfigError from bzt.utils import dehumanize_time -from bzt.six import PY3 +import sys + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 from metrics import get_metrics, AVAILABLE_METRICS as AVAILABLE_SERVER_METRICS from process import get_process_pid_from_file, get_server_processes, \ get_child_processes, get_server_pidfile + class Monitor(monitoring.Monitoring): def __init__(self): super(Monitor, self).__init__() @@ -40,7 +44,10 @@ class ServerLocalClient(monitoring.LocalClient): def __init__(self, parent_log, label, config, engine=None): super(ServerLocalClient, self).__init__(parent_log, label, config, engine=engine) - self.label = 'ServerLocalClient' + if label: + self.label = label + else: + self.label = 'ServerLocalClient' def connect(self): exc = TaurusConfigError('Metric is required in Local monitoring client') diff --git a/benchmarks/monitoring/process.py b/benchmarks/monitoring/process.py index 641e24a1c..c8f84bfba 100644 --- a/benchmarks/monitoring/process.py +++ b/benchmarks/monitoring/process.py @@ -38,11 +38,10 @@ def find_procs_by_name(name): def get_process_pid_from_file(file_path): """Get the process pid from pid file. """ + pid = None if os.path.isfile(file_path): with open(file_path, "r") as f: pid = int(f.readline()) - else: - raise Exception("Invalid file {}".format(file_path)) return pid diff --git a/benchmarks/monitoring/requirements.txt b/benchmarks/monitoring/requirements.txt index 53e3cabc4..3bd78d855 100644 --- a/benchmarks/monitoring/requirements.txt +++ b/benchmarks/monitoring/requirements.txt @@ -1 +1,2 @@ -gevent==20.5.2 \ No newline at end of file +gevent==20.5.2 +junitparser==1.4.1 \ No newline at end of file diff --git a/benchmarks/monitoring/run_perfomance_suite.py b/benchmarks/monitoring/run_perfomance_suite.py new file mode 100644 index 000000000..08d18d360 --- /dev/null +++ b/benchmarks/monitoring/run_perfomance_suite.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Run Tarus test cases and generate the Junit XML report +""" +# pylint: disable=redefined-builtin + +import os +import sys +import time +import logging +import argparse +import glob +import pathlib +import subprocess +from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure + +logger = logging.getLogger(__name__) +code = 0 + + +def run_process(cmd, wait=True): + print("running command : {}".format(cmd)) + + if wait: + try: + output = subprocess.check_output( + cmd, stderr=subprocess.STDOUT, shell=True, timeout=30*60, + universal_newlines=True) + except subprocess.CalledProcessError as exc: + print("Status : FAIL", exc.returncode, exc.output) + return exc.returncode, exc.output + else: + print("Output: \n{}\n".format(output)) + return 0, output + + else: + p = subprocess.Popen(cmd, shell=True) + return 1, '' + + +def get_test_yamls(dir_path=None, pattern="*.yaml"): + if not dir_path: + path = pathlib.Path(__file__).parent.absolute() + dir_path = str(path) + "/tests" + + path_pattern = "{}/{}".format(dir_path, pattern) + return glob.glob(path_pattern) + + +def get_options(artifacts_dir): + options = [] + options.append('-o settings.artifacts-dir={}'.format(artifacts_dir)) + options.append('-o modules.console.disable=true') + options.append('-o settings.env.BASEDIR={}'.format(artifacts_dir)) + + options_str = ' '.join(options) + + return options_str + + +def run_test_suite(artifacts_dir, test_dir, pattern): + if os.path.exists(artifacts_dir): + artifacts_dir = "{}_{}".format(artifacts_dir, str(int(time.time()))) + path = pathlib.Path(__file__).parent.absolute() + start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) + run_process(start_monitoring_server, wait=False) + + junit_xml = JUnitXml() + pre_command = 'export PYTHONPATH={}:$PYTHONPATH; '.format(str(path)) + + for test_file in get_test_yamls(test_dir, pattern): + suite_start = int(time.time()) + suite_name = os.path.basename(test_file).rsplit('.', 1)[0] + suit_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) + options_str = get_options(suit_artifacts_dir) + code, err = run_process("{} bzt {} {} ".format(pre_command, options_str, test_file)) + suite_end = int(time.time()) + suite_time = suite_end - suite_start # context manager to do timing + + # Assumes default file name + xunit_file = "{}/xunit.xml".format(suit_artifacts_dir) + + tests, failures, skipped, errors = 0, 0, 0, 0 + err_txt = "" + ts = TestSuite(suite_name) + if os.path.exists(xunit_file): + xml = JUnitXml.fromfile(xunit_file) + for i, suite in enumerate(xml): #tqqdm + for case in suite: + name = "scenario_{}: {}".format(i, case.name) + result = case.result + if isinstance(result, Error): + errors += 1 + err_txt = err + elif isinstance(result, Failure): + failures += 1 + err_txt = err + elif isinstance(result, Skipped): + skipped += 1 + else: + tests +=1 + + tc = TestCase(name) + tc.result = result + tc.system_err = err_txt[:-4] + ts.add_testcase(tc) + else: + tc = TestCase(suite_name) + if code: + tc.result = Error("Suite run failed", "Error") + tc.system_err = err[:-4] + else: + tc.result = Skipped() + tc.system_out = err[:-4] + ts.add_testcase(tc) + + ts.hostname = "localhost" #config.ini? + ts.timestamp = suite_start + ts.time = suite_time + ts.tests = tests + ts.failures = failures + ts.skipped = skipped + ts.errors = errors + junit_xml.add_testsuite(ts) + + junit_xml.update_statistics() + junit_xml_path = '{}/junit.xml'.format(artifacts_dir) + junit_html_path = '{}/junit.html'.format(artifacts_dir) + junit_xml.write(junit_xml_path) + run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) + + stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) + run_process(stop_monitoring_server) + + if junit_xml.errors or junit_xml.failures or junit_xml.skipped: + sys.exit(3) + + +if __name__ == "__main__": + logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + parser = argparse.ArgumentParser(prog='run_perf_suite', description='Perf Suite Runner') + parser.add_argument('-a', '--artifacts-dir', nargs=1, type=str, dest='artifacts', required=True, + help='A artifacts directory') + + parser.add_argument('-d', '--test-dir', nargs=1, type=str, dest='test_dir', default=[None], + help='A test dir') + + parser.add_argument('-p', '--pattern', nargs=1, type=str, dest='pattern', default=["*.yaml"], + help='Test case file name pattern. example *.yaml') + + args = parser.parse_args() + run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0]) diff --git a/benchmarks/monitoring/tests/call_jmx.yaml b/benchmarks/monitoring/tests/call_jmx.yaml index d75695e49..7973ee521 100644 --- a/benchmarks/monitoring/tests/call_jmx.yaml +++ b/benchmarks/monitoring/tests/call_jmx.yaml @@ -14,5 +14,16 @@ modules: management_port : 8081 protocol : http input_filepath : kitten.jpg +reporting: +- module: passfail # this is to enable passfail module +- module: junit-xml + data-source: pass-fail +- module: junit-xml + data-source: sample-labels +- module: final-stats + dump-csv : ${BASEDIR}/final_stats.csv +setting: + env: + BASEDIR : '.' diff --git a/benchmarks/monitoring/tests/inference.jmx b/benchmarks/monitoring/tests/inference.jmx deleted file mode 100644 index 909c03cfd..000000000 --- a/benchmarks/monitoring/tests/inference.jmx +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - model - squeezenet - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - ${__P(loops,2)} - - ${__P(threads,4)} - ${__P(rampup,1)} - false - - - true - - - - - - - ${__P(input_filepath)} - data - image/jpeg - - - - - - - - - - - /predictions/${model} - POST - true - false - true - true - - - - - - - - - diff --git a/benchmarks/monitoring/tests/inference.yaml b/benchmarks/monitoring/tests/inference.yaml deleted file mode 100644 index 3fb71b303..000000000 --- a/benchmarks/monitoring/tests/inference.yaml +++ /dev/null @@ -1,24 +0,0 @@ - -execution: -- concurrency: 4 - ramp-up: 1s - hold-for: 20s - scenario: Inference -scenarios: - Inference: - requests: - - follow-redirects: true - label: Inference Request - method: POST - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - model: ${__P(model_name,squeezenet_v1.1)} - -modules: - jmeter: - properties: - input_filepath : kitten.jpg - model_name : squeezenet diff --git a/benchmarks/monitoring/tests/inference_server_monitoring.yaml b/benchmarks/monitoring/tests/inference_server_monitoring.yaml index 1e3b9f205..d23af46b1 100644 --- a/benchmarks/monitoring/tests/inference_server_monitoring.yaml +++ b/benchmarks/monitoring/tests/inference_server_monitoring.yaml @@ -6,22 +6,15 @@ execution: scenario: Inference scenarios: Inference: - requests: - - follow-redirects: true - label: Inference Request - method: POST - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - model: ${__P(model_name,squeezenet_v1.1)} + script: register_and_inference.jmx modules: jmeter: properties: + port : 8080 + management_port : 8081 + protocol : http input_filepath : kitten.jpg - model_name : squeezenet services: @@ -36,3 +29,16 @@ services: - sum_memory_percent - sum_num_handles - server_workers # no of mms workers + +reporting: +- module: passfail # this is to enable passfail module +- module: junit-xml + data-source: pass-fail +- module: junit-xml + data-source: sample-labels +- module: final-stats + dump-csv : ${BASEDIR}/final_stats.csv + +setting: + env: + BASEDIR : '.' \ No newline at end of file diff --git a/benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml b/benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml index 5f08515b8..ace86a732 100644 --- a/benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml +++ b/benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml @@ -6,22 +6,15 @@ execution: scenario: Inference scenarios: Inference: - requests: - - follow-redirects: true - label: Inference Request - method: POST - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - model: ${__P(model_name,squeezenet_v1.1)} + script: register_and_inference.jmx modules: jmeter: properties: + port : 8080 + management_port : 8081 + protocol : http input_filepath : kitten.jpg - model_name : squeezenet services: @@ -37,13 +30,27 @@ services: - sum_num_handles - server_workers # no of mms workers + reporting: - module: passfail criteria: + - fail of s2_fail >50%, stop as failed + - p90 of s2_p90 >250ms , stop as failed + - avg-rt of s2_avg_rt >1s , stop as failed - class: bzt.modules.monitoring.MonitoringCriteria subject: mms-inference-server/sum_num_handles condition: '>' - threshold: 180 + threshold: 80 timeframe: 1s fail: true - stop: true \ No newline at end of file + stop: true +- module: junit-xml + data-source: pass-fail +- module: junit-xml + data-source: sample-labels +- module: final-stats + dump-csv : ${BASEDIR}/final_stats.csv + +setting: + env: + BASEDIR : '.' \ No newline at end of file diff --git a/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml b/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml index 4da037c69..99a7d2910 100644 --- a/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml +++ b/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml @@ -6,27 +6,15 @@ execution: scenario: Inference scenarios: Inference: - default-address: ${__P(protocol,https)}://${__P(hostname,127.0.0.1)}:${__P(port,8443)}/ - requests: - - follow-redirects: true - label: Inference Request - method: POST - upload-files: - - mime-type: image/jpeg - param: data - path: ${__P(input_filepath)} - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - model: ${__P(model_name,squeezenet_v1.1)} + script: register_and_inference.jmx modules: jmeter: properties: + port : 8080 + management_port : 8081 + protocol : http input_filepath : kitten.jpg - model_name : squeezenet server_local_monitoring: # metrics_monitoring_taurus and dependencies should be in python path class : metrics_monitoring_taurus.Monitor # monitoring class. @@ -39,4 +27,22 @@ services: - cpu - disk-space - mem - - sum_memory_percent \ No newline at end of file + - sum_memory_percent + - module: monitoring + ~local: + - interval: 20s # polling interval + logging: True # local monitoring logs will be saved to "local_monitoring_logs.csv" in the artifacts dir + metrics: + - cpu +reporting: +- module: passfail # this is to enable passfail module +- module: junit-xml + data-source: pass-fail +- module: junit-xml + data-source: sample-labels +- module: final-stats + dump-csv : ${BASEDIR}/final_stats.csv + +setting: + env: + BASEDIR : '.' \ No newline at end of file diff --git a/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml b/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml index d27aa86f2..98cb72eb3 100644 --- a/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml +++ b/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml @@ -6,27 +6,15 @@ execution: scenario: Inference scenarios: Inference: - default-address: ${__P(protocol,https)}://${__P(hostname,127.0.0.1)}:${__P(port,8443)}/ - requests: - - follow-redirects: true - label: Inference Request - method: POST - upload-files: - - mime-type: image/jpeg - param: data - path: ${__P(input_filepath)} - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - model: ${__P(model_name,squeezenet_v1.1)} + script: register_and_inference.jmx modules: jmeter: properties: + port : 8080 + management_port : 8081 + protocol : http input_filepath : kitten.jpg - model_name : squeezenet server_local_monitoring: # metrics_monitoring_taurus and dependencies should be in python path class : metrics_monitoring_taurus.Monitor # monitoring class. @@ -44,9 +32,24 @@ services: reporting: - module: passfail criteria: + - fail of s2_fail >50%, stop as failed + - p90 of s2_p90 >250ms , stop as failed + - avg-rt of s2_avg_rt >1s , stop as failed - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_memory_percent condition: '>' - threshold: 14 + threshold: 10 timeframe: 1s + stop : true + fail : true +- module: junit-xml + data-source: pass-fail +- module: junit-xml + data-source: sample-labels +- module: final-stats + dump-csv : ${BASEDIR}/final_stats.csv + +setting: + env: + BASEDIR : '.' \ No newline at end of file From f2f1f77437e6e25870c1d2c3dfd4623ed27b9acd Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 9 Jun 2020 12:31:19 +0530 Subject: [PATCH 025/329] requirement.txt --- benchmarks/monitoring/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/benchmarks/monitoring/requirements.txt b/benchmarks/monitoring/requirements.txt index 3bd78d855..d192628d4 100644 --- a/benchmarks/monitoring/requirements.txt +++ b/benchmarks/monitoring/requirements.txt @@ -1,2 +1,3 @@ gevent==20.5.2 -junitparser==1.4.1 \ No newline at end of file +junitparser==1.4.1 +vjunit==0.6.1 \ No newline at end of file From 98ba862f37076317b57d1e47b3af01521c152d50 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 12:39:09 +0530 Subject: [PATCH 026/329] ignore expired certificate --- .circleci/images/Dockerfile.python2.7 | 3 ++- .circleci/images/Dockerfile.python3.6 | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.circleci/images/Dockerfile.python2.7 b/.circleci/images/Dockerfile.python2.7 index 4d86a0490..c1d6b1d90 100644 --- a/.circleci/images/Dockerfile.python2.7 +++ b/.circleci/images/Dockerfile.python2.7 @@ -40,12 +40,13 @@ RUN curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - \ && sudo npm install -g newman newman-reporter-html # Install jmeter for benchmark +# ToDo: Remove --no-check-certificate; temporarily added to bypass jmeter-plugins.org's expired certificate RUN cd /opt \ && wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.3.tgz \ && tar -xzf apache-jmeter-5.3.tgz \ && cd apache-jmeter-5.3 \ && ln -s /opt/apache-jmeter-5.3/bin/jmeter /usr/local/bin/jmeter \ - && wget https://jmeter-plugins.org/get/ -O lib/ext/jmeter-plugins-manager-1.4.jar \ + && wget --no-check-certificate https://jmeter-plugins.org/get/ -O lib/ext/jmeter-plugins-manager-1.4.jar \ && wget http://search.maven.org/remotecontent?filepath=kg/apc/cmdrunner/2.2/cmdrunner-2.2.jar -O lib/cmdrunner-2.2.jar \ && java -cp lib/ext/jmeter-plugins-manager-1.4.jar org.jmeterplugins.repository.PluginManagerCMDInstaller \ && bin/PluginsManagerCMD.sh install jpgc-synthesis=2.1,jpgc-filterresults=2.1,jpgc-mergeresults=2.1,jpgc-cmd=2.1,jpgc-perfmon=2.1 \ No newline at end of file diff --git a/.circleci/images/Dockerfile.python3.6 b/.circleci/images/Dockerfile.python3.6 index 9105dea95..bcd3b980d 100644 --- a/.circleci/images/Dockerfile.python3.6 +++ b/.circleci/images/Dockerfile.python3.6 @@ -39,12 +39,13 @@ RUN curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - \ && sudo npm install -g newman newman-reporter-html # Install jmeter for benchmark +# ToDo: Remove --no-check-certificate; temporarily added to bypass jmeter-plugins.org's expired certificate RUN cd /opt \ && wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.3.tgz \ && tar -xzf apache-jmeter-5.3.tgz \ && cd apache-jmeter-5.3 \ && ln -s /opt/apache-jmeter-5.3/bin/jmeter /usr/local/bin/jmeter \ - && wget https://jmeter-plugins.org/get/ -O lib/ext/jmeter-plugins-manager-1.4.jar \ + && wget --no-check-certificate https://jmeter-plugins.org/get/ -O lib/ext/jmeter-plugins-manager-1.4.jar \ && wget http://search.maven.org/remotecontent?filepath=kg/apc/cmdrunner/2.2/cmdrunner-2.2.jar -O lib/cmdrunner-2.2.jar \ && java -cp lib/ext/jmeter-plugins-manager-1.4.jar org.jmeterplugins.repository.PluginManagerCMDInstaller \ && bin/PluginsManagerCMD.sh install jpgc-synthesis=2.1,jpgc-filterresults=2.1,jpgc-mergeresults=2.1,jpgc-cmd=2.1,jpgc-perfmon=2.1 \ No newline at end of file From ab8525d9f5e42f2fda63af6015d36a5f4ccc7e9a Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 12:53:51 +0530 Subject: [PATCH 027/329] updated taurus test suite --- .circleci/scripts/linux_test_taurus.sh | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_taurus.sh index 083849355..587dc4f42 100755 --- a/.circleci/scripts/linux_test_taurus.sh +++ b/.circleci/scripts/linux_test_taurus.sh @@ -1,17 +1,12 @@ #!/bin/bash -multi-model-server --start \ - --models squeezenet=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar \ - >> mms.log 2>&1 -sleep 90 +multi-model-server --start >> mms.log 2>&1 +sleep 10 -cd taurus +#cd taurus +cd benchmarks/monitoring curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg -bzt -o modules.jmeter.path=/opt/apache-jmeter-5.3/bin/jmeter \ - -o settings.artifacts-dir=/tmp/cci-taurus/ \ - -o modules.console.disable=true \ - imageInputModelPlan.jmx.yaml \ - -report +./run_performance_suite.py --artifacts-dir=/tmp/cci-taurus/ -multi-model-server --stop \ No newline at end of file +multi-model-server --stop >> mms.log 2>&1 \ No newline at end of file From 82a7e6e52afa6e9f20b7796024c3f6db7356774d Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 9 Jun 2020 13:03:50 +0530 Subject: [PATCH 028/329] add jmeter path --- benchmarks/monitoring/run_perfomance_suite.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/benchmarks/monitoring/run_perfomance_suite.py b/benchmarks/monitoring/run_perfomance_suite.py index 08d18d360..0f24d85a4 100644 --- a/benchmarks/monitoring/run_perfomance_suite.py +++ b/benchmarks/monitoring/run_perfomance_suite.py @@ -58,18 +58,19 @@ def get_test_yamls(dir_path=None, pattern="*.yaml"): return glob.glob(path_pattern) -def get_options(artifacts_dir): - options = [] +def get_options(artifacts_dir, jmeter_path=None): + options=[] + if jmeter_path: + options.append('-o modules.jmeter.path={}'.format(jmeter_path)) options.append('-o settings.artifacts-dir={}'.format(artifacts_dir)) options.append('-o modules.console.disable=true') options.append('-o settings.env.BASEDIR={}'.format(artifacts_dir)) - options_str = ' '.join(options) return options_str -def run_test_suite(artifacts_dir, test_dir, pattern): +def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): if os.path.exists(artifacts_dir): artifacts_dir = "{}_{}".format(artifacts_dir, str(int(time.time()))) path = pathlib.Path(__file__).parent.absolute() @@ -83,7 +84,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern): suite_start = int(time.time()) suite_name = os.path.basename(test_file).rsplit('.', 1)[0] suit_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) - options_str = get_options(suit_artifacts_dir) + options_str = get_options(suit_artifacts_dir, jmeter_path) code, err = run_process("{} bzt {} {} ".format(pre_command, options_str, test_file)) suite_end = int(time.time()) suite_time = suite_end - suite_start # context manager to do timing @@ -159,5 +160,8 @@ def run_test_suite(artifacts_dir, test_dir, pattern): parser.add_argument('-p', '--pattern', nargs=1, type=str, dest='pattern', default=["*.yaml"], help='Test case file name pattern. example *.yaml') + parser.add_argument('-j', '--jmeter-path', nargs=1, type=str, dest='jmeter_path', default=[None], + help='Test case file name pattern. example *.yaml') + args = parser.parse_args() - run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0]) + run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0], args.jmeter_path[0]) From 84143992375307e8f3c0428d4969bcdccc4eff13 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 9 Jun 2020 13:06:12 +0530 Subject: [PATCH 029/329] add jmeter path --- benchmarks/monitoring/run_perfomance_suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/monitoring/run_perfomance_suite.py b/benchmarks/monitoring/run_perfomance_suite.py index 0f24d85a4..39ca55984 100644 --- a/benchmarks/monitoring/run_perfomance_suite.py +++ b/benchmarks/monitoring/run_perfomance_suite.py @@ -161,7 +161,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): help='Test case file name pattern. example *.yaml') parser.add_argument('-j', '--jmeter-path', nargs=1, type=str, dest='jmeter_path', default=[None], - help='Test case file name pattern. example *.yaml') + help='JMeter executable bin path') args = parser.parse_args() run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0], args.jmeter_path[0]) From d7489a7c1319d140ede2625facc361b862314192 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 13:16:02 +0530 Subject: [PATCH 030/329] dockerhub image tag and sha updated --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d8a4c88a7..6f99af5e5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ version: 2.1 executors: docker-python-3: docker: - - image: prashantsail/mms-build:latest + - image: prashantsail/mms-build:python3.6@sha256:ea8ad58cc1740587f062d3b149175563e783d420d61ff242f9280a2550457853 environment: _JAVA_OPTIONS: "-Xmx2048m" From 6aa66b4ec8c584ccb43d349f47ceef7d264ac10a Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 13:21:16 +0530 Subject: [PATCH 031/329] executable py script, jmeter path --- .circleci/scripts/linux_test_taurus.sh | 2 +- benchmarks/monitoring/run_perfomance_suite.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 benchmarks/monitoring/run_perfomance_suite.py diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_taurus.sh index 587dc4f42..2e6144374 100755 --- a/.circleci/scripts/linux_test_taurus.sh +++ b/.circleci/scripts/linux_test_taurus.sh @@ -7,6 +7,6 @@ sleep 10 cd benchmarks/monitoring curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg -./run_performance_suite.py --artifacts-dir=/tmp/cci-taurus/ +./run_perfomance_suite.py --artifacts-dir=/tmp/cci-taurus/ --jmeter-path=/opt/apache-jmeter-5.3/bin/jmeter multi-model-server --stop >> mms.log 2>&1 \ No newline at end of file diff --git a/benchmarks/monitoring/run_perfomance_suite.py b/benchmarks/monitoring/run_perfomance_suite.py old mode 100644 new mode 100755 index 39ca55984..8c93ed844 --- a/benchmarks/monitoring/run_perfomance_suite.py +++ b/benchmarks/monitoring/run_perfomance_suite.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # Licensed under the Apache License, Version 2.0 (the "License"). From 318d5bece6a5907d5bb510a6fba357120b1c7d97 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 13:28:07 +0530 Subject: [PATCH 032/329] performance_regression instead of taurus --- .circleci/README.md | 2 +- .circleci/config.yml | 7 +++---- .circleci/scripts/linux_test_taurus.sh | 4 ++-- .../imageInputModelPlan.jmx.yaml | 0 4 files changed, 6 insertions(+), 7 deletions(-) rename {taurus => performance_regression}/imageInputModelPlan.jmx.yaml (100%) diff --git a/.circleci/README.md b/.circleci/README.md index d6f6308f4..cbacf4179 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -10,7 +10,7 @@ Currently following jobs are executed under "**build_and_test**" workflow 2. **python_tests** - Executes pytests from _mms/tests/unit_tests/_ 3. **api_tests** - (NEW!) Executes newman test suite for API testing 4. **benchmark** - Executes latency benchmark using resnet-18 model -5. **taurus** - (NEW!) Executes taurus tests for performance +5. **performance_regression** - (NEW!) Executes performance tests using taurus/jmeter ## scripts Instead of using inline commands inside _config.yml_, job steps are configured as shell scripts. diff --git a/.circleci/config.yml b/.circleci/config.yml index 6f99af5e5..195f9a519 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -100,7 +100,7 @@ jobs: path: /tmp/MMSBenchmark/out/latency/resnet-18/report/ destination: benchmark-latency-resnet-18 - taurus: + performance_regression: executor: docker-python-3 steps: - attach-mms-workspace @@ -116,8 +116,7 @@ jobs: path: mms.log - store_artifacts: name: Store Taurus results - path: /tmp/cci-taurus/ - destination: taurus-results + path: /tmp/mms-performance-regression/ modelarchiver: executor: docker-python-3 @@ -146,6 +145,6 @@ workflows: - benchmark: requires: - build - - taurus: + - performance_regression: requires: - build \ No newline at end of file diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_taurus.sh index 083849355..788769089 100755 --- a/.circleci/scripts/linux_test_taurus.sh +++ b/.circleci/scripts/linux_test_taurus.sh @@ -5,11 +5,11 @@ multi-model-server --start \ >> mms.log 2>&1 sleep 90 -cd taurus +cd performance_regression curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg bzt -o modules.jmeter.path=/opt/apache-jmeter-5.3/bin/jmeter \ - -o settings.artifacts-dir=/tmp/cci-taurus/ \ + -o settings.artifacts-dir=/tmp/mms-performance-regression/ \ -o modules.console.disable=true \ imageInputModelPlan.jmx.yaml \ -report diff --git a/taurus/imageInputModelPlan.jmx.yaml b/performance_regression/imageInputModelPlan.jmx.yaml similarity index 100% rename from taurus/imageInputModelPlan.jmx.yaml rename to performance_regression/imageInputModelPlan.jmx.yaml From 1e69b37911914bd4d5ca28c47d7212746fe0bb02 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 14:01:11 +0530 Subject: [PATCH 033/329] performce taurus with cci --- .circleci/config.yml | 25 ++++++++++++++----------- .circleci/scripts/linux_test_taurus.sh | 6 ++++++ benchmarks/monitoring/requirements.txt | 3 ++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 195f9a519..abb1e855d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -115,7 +115,10 @@ jobs: name: Store server logs from Taurus tests path: mms.log - store_artifacts: - name: Store Taurus results + name: Store Taurus test results as artifacts + path: /tmp/mms-performance-regression/ + - store_test_results: + name: Store Taurus test results path: /tmp/mms-performance-regression/ modelarchiver: @@ -135,16 +138,16 @@ workflows: build_and_test: jobs: - build - - modelarchiver - - python_tests: - requires: - - build - - api_tests: - requires: - - build - - benchmark: - requires: - - build +# - modelarchiver +# - python_tests: +# requires: +# - build +# - api_tests: +# requires: +# - build +# - benchmark: +# requires: +# - build - performance_regression: requires: - build \ No newline at end of file diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_taurus.sh index 231b68b0f..feaa87803 100755 --- a/.circleci/scripts/linux_test_taurus.sh +++ b/.circleci/scripts/linux_test_taurus.sh @@ -1,11 +1,17 @@ #!/bin/bash +set -e + +# Start MMS server multi-model-server --start >> mms.log 2>&1 sleep 10 #cd taurus cd benchmarks/monitoring +# Install dependencies +pip install requirements.txt + curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg ./run_perfomance_suite.py --artifacts-dir=/tmp/mms-performance-regression/ --jmeter-path=/opt/apache-jmeter-5.3/bin/jmeter diff --git a/benchmarks/monitoring/requirements.txt b/benchmarks/monitoring/requirements.txt index d192628d4..aad459d1e 100644 --- a/benchmarks/monitoring/requirements.txt +++ b/benchmarks/monitoring/requirements.txt @@ -1,3 +1,4 @@ gevent==20.5.2 junitparser==1.4.1 -vjunit==0.6.1 \ No newline at end of file +vjunit==0.6.1 +pathlib \ No newline at end of file From 959253189cf38ccb1fd7f44e07eb07769abbe959 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 14:07:44 +0530 Subject: [PATCH 034/329] quick fix --- .circleci/scripts/linux_test_taurus.sh | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_taurus.sh index feaa87803..215209da8 100755 --- a/.circleci/scripts/linux_test_taurus.sh +++ b/.circleci/scripts/linux_test_taurus.sh @@ -10,7 +10,7 @@ sleep 10 cd benchmarks/monitoring # Install dependencies -pip install requirements.txt +pip install -r requirements.txt curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg ./run_perfomance_suite.py --artifacts-dir=/tmp/mms-performance-regression/ --jmeter-path=/opt/apache-jmeter-5.3/bin/jmeter diff --git a/setup.py b/setup.py index 3c125fff5..079a0e40a 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ def run(self): rmtree('build/lib/') try: - subprocess.check_call('frontend/gradlew -p frontend clean build', shell=True) + subprocess.check_call('frontend/gradlew -p frontend clean assemble', shell=True) except OSError: assert 0, "build failed" copy2(self.source_server_file, self.dest_file_name) From a79c159877b0008a1c5bd8ac7af2fb88b48f0592 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 14:27:29 +0530 Subject: [PATCH 035/329] tweaked for store_test_results --- .circleci/config.yml | 2 +- .circleci/scripts/linux_test_taurus.sh | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index abb1e855d..73ac4fd37 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -119,7 +119,7 @@ jobs: path: /tmp/mms-performance-regression/ - store_test_results: name: Store Taurus test results - path: /tmp/mms-performance-regression/ + path: /tmp/mms-performance-regression/report/ modelarchiver: executor: docker-python-3 diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_taurus.sh index 215209da8..2c48b658d 100755 --- a/.circleci/scripts/linux_test_taurus.sh +++ b/.circleci/scripts/linux_test_taurus.sh @@ -1,6 +1,9 @@ #!/bin/bash set -e +ARTIFACTS_DIR='/tmp/mms-performance-regression/' +RESULT_DIR=$ARTIFACTS_DIR'/report/performance/' +JMETER_PATH='/opt/apache-jmeter-5.3/bin/jmeter' # Start MMS server multi-model-server --start >> mms.log 2>&1 @@ -12,7 +15,13 @@ cd benchmarks/monitoring # Install dependencies pip install -r requirements.txt +# Execute performance test suite curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg -./run_perfomance_suite.py --artifacts-dir=/tmp/mms-performance-regression/ --jmeter-path=/opt/apache-jmeter-5.3/bin/jmeter +./run_perfomance_suite.py --artifacts-dir=$ARTIFACTS_DIR --jmeter-path=$JMETER_PATH -multi-model-server --stop >> mms.log 2>&1 \ No newline at end of file +# Stop server +multi-model-server --stop >> mms.log 2>&1 + +# Collect and store test results in result directory to be picked up by CircleCI +mkdir -p $RESULT_DIR +cp $ARTIFACTS_DIR'/junit.xml' $RESULT_DIR From 61a0cdddd25b06781b63cc9b436b8b4506377e24 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 14:40:14 +0530 Subject: [PATCH 036/329] store test results and exit with exit_code --- .circleci/scripts/linux_test_taurus.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_taurus.sh index 2c48b658d..487f36e85 100755 --- a/.circleci/scripts/linux_test_taurus.sh +++ b/.circleci/scripts/linux_test_taurus.sh @@ -1,6 +1,5 @@ #!/bin/bash -set -e ARTIFACTS_DIR='/tmp/mms-performance-regression/' RESULT_DIR=$ARTIFACTS_DIR'/report/performance/' JMETER_PATH='/opt/apache-jmeter-5.3/bin/jmeter' @@ -15,9 +14,10 @@ cd benchmarks/monitoring # Install dependencies pip install -r requirements.txt -# Execute performance test suite +# Execute performance test suite and store exit code curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg ./run_perfomance_suite.py --artifacts-dir=$ARTIFACTS_DIR --jmeter-path=$JMETER_PATH +EXIT_CODE=$? # Stop server multi-model-server --stop >> mms.log 2>&1 @@ -25,3 +25,6 @@ multi-model-server --stop >> mms.log 2>&1 # Collect and store test results in result directory to be picked up by CircleCI mkdir -p $RESULT_DIR cp $ARTIFACTS_DIR'/junit.xml' $RESULT_DIR + +# Exit with the same error code as that of test execution +exit EXIT_CODE \ No newline at end of file From 5919221c2c6875b44b6ae393d3435ca33827ba1d Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 9 Jun 2020 16:13:50 +0530 Subject: [PATCH 037/329] read me update --- benchmarks/monitoring/README.md | 15 +++++-- benchmarks/monitoring/requirements.txt | 3 +- benchmarks/monitoring/run_perfomance_suite.py | 41 ++++++++++++++----- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/benchmarks/monitoring/README.md b/benchmarks/monitoring/README.md index f3096577d..d57dba8c5 100644 --- a/benchmarks/monitoring/README.md +++ b/benchmarks/monitoring/README.md @@ -3,6 +3,16 @@ This test suite helps in running the load tests and monitoring the process, sub-process and system wide metrics. It allows to specify the pass/fail criteria for metrics in the test case. We use Taurus test automation framework to run the test cases and metrics monitoring. +## How to run the test suite +To run the test suite just invoke the [run_perfomance_suite.py](run_perfomance_suite.py). You will have to provide the artifacts-dir path to store the test case results. +You can specify test cases to be run by providing 'test-dir' (default='monitoring/tests') and 'pattern' (default='*.yaml'). For other options use '--help' option. +The script starts the server monitoring agent, collects all the test cases, executes them and then produces Junit XML and HTML report in artifacts-dir. + +```bash +python run_perfomance_suite.py --artifacts-dir='' +``` + +#### To know more about the Test Suite follow the guide below: ## Taurus @@ -20,9 +30,6 @@ To run a metrics monitoring test case, you need to specify below in Taurus test 2. Metrics to monitor 3. Pass/fail criteria -#### Assumptions made in this guide -1. For some of the test cases, it is assumed that you have started the MMS server with squeezenet model registered - and workers are assigned to it. #### 1. Load test case scenario You can specify the test scenarios, in the scenario section of the yaml. @@ -98,7 +105,7 @@ There are multiple ways you can specify your test scenario. ``` - Use command Taurus command below to run the test yaml. + Use command Taurus command below to run the test yaml. Note this test script assumes squeezenet model is already registered. ```bash bzt inference.yaml diff --git a/benchmarks/monitoring/requirements.txt b/benchmarks/monitoring/requirements.txt index d192628d4..ebf1cd968 100644 --- a/benchmarks/monitoring/requirements.txt +++ b/benchmarks/monitoring/requirements.txt @@ -1,3 +1,4 @@ gevent==20.5.2 junitparser==1.4.1 -vjunit==0.6.1 \ No newline at end of file +vjunit==0.6.1 +tqdm==4.40.0 \ No newline at end of file diff --git a/benchmarks/monitoring/run_perfomance_suite.py b/benchmarks/monitoring/run_perfomance_suite.py index 39ca55984..945f2f001 100644 --- a/benchmarks/monitoring/run_perfomance_suite.py +++ b/benchmarks/monitoring/run_perfomance_suite.py @@ -23,12 +23,29 @@ import glob import pathlib import subprocess +import socket +from tqdm import tqdm from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure logger = logging.getLogger(__name__) code = 0 +class Timer(object): + def __init__(self, description): + self.description = description + + def __enter__(self): + self.start = int(time.time()) + return self + + def __exit__(self, type, value, traceback): + logger.info(f"{self.description}: {self.diff()}s") + + def diff(self): + return int(time.time()) - self.start + + def run_process(cmd, wait=True): print("running command : {}".format(cmd)) @@ -74,20 +91,21 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): if os.path.exists(artifacts_dir): artifacts_dir = "{}_{}".format(artifacts_dir, str(int(time.time()))) path = pathlib.Path(__file__).parent.absolute() - start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) + start_monitoring_server = "python3 {}/metrics_monitoring_server.py --start".format(path) run_process(start_monitoring_server, wait=False) junit_xml = JUnitXml() pre_command = 'export PYTHONPATH={}:$PYTHONPATH; '.format(str(path)) - for test_file in get_test_yamls(test_dir, pattern): - suite_start = int(time.time()) + test_yamls = get_test_yamls(test_dir, pattern) + for test_file in tqdm(test_yamls, desc="Test Suites"): suite_name = os.path.basename(test_file).rsplit('.', 1)[0] - suit_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) - options_str = get_options(suit_artifacts_dir, jmeter_path) - code, err = run_process("{} bzt {} {} ".format(pre_command, options_str, test_file)) - suite_end = int(time.time()) - suite_time = suite_end - suite_start # context manager to do timing + with Timer("Test suit {} execution time".format(suite_name)) as t: + suit_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) + options_str = get_options(suit_artifacts_dir, jmeter_path) + code, err = run_process("{} bzt {} {} ".format(pre_command, options_str, test_file)) + suite_time = t.diff() + suite_start = t.start # Assumes default file name xunit_file = "{}/xunit.xml".format(suit_artifacts_dir) @@ -126,13 +144,14 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): tc.system_out = err[:-4] ts.add_testcase(tc) - ts.hostname = "localhost" #config.ini? + ts.hostname = socket.gethostname() ts.timestamp = suite_start ts.time = suite_time ts.tests = tests ts.failures = failures ts.skipped = skipped ts.errors = errors + ts.update_statistics() junit_xml.add_testsuite(ts) junit_xml.update_statistics() @@ -141,7 +160,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): junit_xml.write(junit_xml_path) run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) - stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python3 {}/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: @@ -150,7 +169,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): if __name__ == "__main__": logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) - parser = argparse.ArgumentParser(prog='run_perf_suite', description='Perf Suite Runner') + parser = argparse.ArgumentParser(prog='run_perfomance_suite.py', description='Performance Test Suite Runner') parser.add_argument('-a', '--artifacts-dir', nargs=1, type=str, dest='artifacts', required=True, help='A artifacts directory') From 26f6be1df30027b165b5ba1ba94d285a377cdd23 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 9 Jun 2020 16:16:13 +0530 Subject: [PATCH 038/329] read me update --- benchmarks/monitoring/run_perfomance_suite.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/monitoring/run_perfomance_suite.py b/benchmarks/monitoring/run_perfomance_suite.py index 945f2f001..143dfd2b7 100644 --- a/benchmarks/monitoring/run_perfomance_suite.py +++ b/benchmarks/monitoring/run_perfomance_suite.py @@ -91,7 +91,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): if os.path.exists(artifacts_dir): artifacts_dir = "{}_{}".format(artifacts_dir, str(int(time.time()))) path = pathlib.Path(__file__).parent.absolute() - start_monitoring_server = "python3 {}/metrics_monitoring_server.py --start".format(path) + start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) run_process(start_monitoring_server, wait=False) junit_xml = JUnitXml() @@ -160,7 +160,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): junit_xml.write(junit_xml_path) run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) - stop_monitoring_server = "python3 {}/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: From 68c9d14c7d0aa42d401bdbaac46a33ef4a889ba1 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 16:17:29 +0530 Subject: [PATCH 039/329] rename to performance regression --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 195f9a519..5d63d698b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -109,13 +109,13 @@ jobs: name: Install Taurus command: pip install bzt - run: - name: Start MMS, Execute Taurus testcases, Stop MMS + name: Start MMS, Execute performance regression testcases, Stop MMS command: .circleci/scripts/linux_test_taurus.sh - store_artifacts: - name: Store server logs from Taurus tests + name: Store server logs path: mms.log - store_artifacts: - name: Store Taurus results + name: Store artifacts from performance regression run path: /tmp/mms-performance-regression/ modelarchiver: From fd78f039ec8adfd1d19fb3f53beb0a07ee8d560e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 16:50:51 +0530 Subject: [PATCH 040/329] execute on python2.7 --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5d63d698b..54a283c89 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,8 @@ version: 2.1 executors: docker-python-3: docker: - - image: prashantsail/mms-build:python3.6@sha256:ea8ad58cc1740587f062d3b149175563e783d420d61ff242f9280a2550457853 +# - image: prashantsail/mms-build:python3.6@sha256:ea8ad58cc1740587f062d3b149175563e783d420d61ff242f9280a2550457853 + - image: prashantsail/mms-build:python2.7@sha256:ea8ad58cc1740587f062d3b149175563e783d420d61ff242f9280a2550457853 environment: _JAVA_OPTIONS: "-Xmx2048m" From a7b2984c6e3417a681bc3c219672bf087aa7891f Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 17:25:50 +0530 Subject: [PATCH 041/329] do not add more children to testcase node --- benchmarks/monitoring/run_perfomance_suite.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmarks/monitoring/run_perfomance_suite.py b/benchmarks/monitoring/run_perfomance_suite.py index fb1d1089e..3f160fd24 100755 --- a/benchmarks/monitoring/run_perfomance_suite.py +++ b/benchmarks/monitoring/run_perfomance_suite.py @@ -132,16 +132,16 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): tc = TestCase(name) tc.result = result - tc.system_err = err_txt[:-4] + # tc.system_err = err_txt[:-4] ts.add_testcase(tc) else: tc = TestCase(suite_name) if code: tc.result = Error("Suite run failed", "Error") - tc.system_err = err[:-4] + # tc.system_err = err[:-4] else: tc.result = Skipped() - tc.system_out = err[:-4] + # tc.system_out = err[:-4] ts.add_testcase(tc) ts.hostname = socket.gethostname() From 67336aa923f8d63de7f506d1802d8960e967c6be Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 18:59:38 +0530 Subject: [PATCH 042/329] parallel workflows on python 2 and 3 --- .circleci/config.yml | 73 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 54a283c89..fd46ce409 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,12 @@ version: 2.1 executors: docker-python-3: docker: -# - image: prashantsail/mms-build:python3.6@sha256:ea8ad58cc1740587f062d3b149175563e783d420d61ff242f9280a2550457853 + - image: prashantsail/mms-build:python3.6@sha256:ea8ad58cc1740587f062d3b149175563e783d420d61ff242f9280a2550457853 + environment: + _JAVA_OPTIONS: "-Xmx2048m" + + docker-python-2: + docker: - image: prashantsail/mms-build:python2.7@sha256:ea8ad58cc1740587f062d3b149175563e783d420d61ff242f9280a2550457853 environment: _JAVA_OPTIONS: "-Xmx2048m" @@ -45,7 +50,10 @@ commands: jobs: build: - executor: docker-python-3 + parameters: + executor: + type: executor + executor: << parameters.executor >> steps: - checkout - run: @@ -63,7 +71,10 @@ jobs: - . python_tests: - executor: docker-python-3 + parameters: + executor: + type: executor + executor: << parameters.executor >> steps: - attach-mms-workspace - run: @@ -74,7 +85,10 @@ jobs: path: htmlcov api_tests: - executor: docker-python-3 + parameters: + executor: + type: executor + executor: << parameters.executor >> steps: - attach-mms-workspace - install-mms-server @@ -86,7 +100,10 @@ jobs: collection: https benchmark: - executor: docker-python-3 + parameters: + executor: + type: executor + executor: << parameters.executor >> steps: - attach-mms-workspace - install-mms-server @@ -102,7 +119,10 @@ jobs: destination: benchmark-latency-resnet-18 performance_regression: - executor: docker-python-3 + parameters: + executor: + type: executor + executor: << parameters.executor >> steps: - attach-mms-workspace - install-mms-server @@ -120,7 +140,10 @@ jobs: path: /tmp/mms-performance-regression/ modelarchiver: - executor: docker-python-3 + parameters: + executor: + type: executor + executor: << parameters.executor >> steps: - checkout - run: @@ -131,21 +154,51 @@ jobs: path: model-archiver/results_units destination: units + workflows: version: 2 - build_and_test: + build_and_test_python36: + jobs: + - build: + executor: docker-python-3 + - modelarchiver: + executor: docker-python-3 + - python_tests: + executor: docker-python-3 + requires: + - build + - api_tests: + executor: docker-python-3 + requires: + - build + - benchmark: + executor: docker-python-3 + requires: + - build + - performance_regression: + executor: docker-python-3 + requires: + - build + + build_and_test_python27: jobs: - - build - - modelarchiver + - build: + executor: docker-python-2 + - modelarchiver: + executor: docker-python-2 - python_tests: + executor: docker-python-2 requires: - build - api_tests: + executor: docker-python-2 requires: - build - benchmark: + executor: docker-python-2 requires: - build - performance_regression: + executor: docker-python-2 requires: - build \ No newline at end of file From 0a37d39588bf94960480215dca23d9ed57375e16 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 9 Jun 2020 21:07:29 +0530 Subject: [PATCH 043/329] Update Readme --- .circleci/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index cbacf4179..beba9a7d8 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -5,12 +5,13 @@ Model Server uses CircleCI for builds. This folder contains the config and scrip _config.yml_ contains MMS build logic which will be used by CircleCI. ## Jobs -Currently following jobs are executed under "**build_and_test**" workflow +Currently following jobs are executed under "_**build_and_test***_" workflow 1. **build** - Builds _frontend/model-server.jar_ and executes tests from gradle -2. **python_tests** - Executes pytests from _mms/tests/unit_tests/_ -3. **api_tests** - (NEW!) Executes newman test suite for API testing +2. **modelarchiver** - Builds and tests modelarchiver module +3. **python_tests** - Executes pytests from _mms/tests/unit_tests/_ 4. **benchmark** - Executes latency benchmark using resnet-18 model -5. **performance_regression** - (NEW!) Executes performance tests using taurus/jmeter +5. (NEW!) **api_tests** - Executes newman test suite for API testing +6. (NEW!) **performance_regression** - Executes performance tests using taurus/jmeter ## scripts Instead of using inline commands inside _config.yml_, job steps are configured as shell scripts. @@ -20,12 +21,12 @@ This is easier for maintenance and reduces chances of error in config.yml MMS uses customized docker image for its CircleCI build. To make sure MMS is compatible with both Python2 and Python3, we use two build projects. We have published two docker images on docker hub for code build -* --------/mms-build:python2.7 -* --------/mms-build:python3.6 +* prashantsail/mms-build:python2.7 +* prashantsail/mms-build:python3.6 Following files in the _images_ folder are used to create the docker images -* Dockerfile.python2.7 - Dockerfile for --------/mms-build:python2.7 -* Dockerfile.python3.6 - Dockerfile for --------/mms-build:python3.6 +* Dockerfile.python2.7 - Dockerfile for prashantsail/mms-build:python2.7 +* Dockerfile.python3.6 - Dockerfile for prashantsail/mms-build:python3.6 ## Local CircleCI cli To make it easy for developers to debug build issues locally, MMS supports CircleCI cli for running a job in a container on your machine. From d0df04e73deb56785668a5e389d96ebe220cc7c9 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 10 Jun 2020 17:51:20 +0530 Subject: [PATCH 044/329] performance regression suite Signed-off-by: maheshambule --- tests/__init__.py | 13 ++ tests/performance/README.md | 218 ++++++++++++++++++ tests/performance/__init__.py | 15 ++ tests/performance/config.ini | 6 + tests/performance/configuration.py | 26 +++ tests/performance/metrics/__init__.py | 55 +++++ tests/performance/metrics_collector.py | 143 ++++++++++++ .../performance/metrics_monitoring_server.py | 134 +++++++++++ .../performance/metrics_monitoring_taurus.py | 87 +++++++ tests/performance/process.py | 71 ++++++ tests/performance/requirements.txt | 5 + tests/performance/run_perfomance_suite.py | 186 +++++++++++++++ tests/performance/tests/call_jmx.yaml | 29 +++ .../tests/inference_server_monitoring.yaml | 44 ++++ .../inference_server_monitoring_criteria.yaml | 56 +++++ .../inference_taurus_local_monitoring.yaml | 48 ++++ ...ence_taurus_local_monitoring_criteria.yaml | 55 +++++ .../tests/register_and_inference.jmx | 189 +++++++++++++++ 18 files changed, 1380 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/performance/README.md create mode 100644 tests/performance/__init__.py create mode 100644 tests/performance/config.ini create mode 100644 tests/performance/configuration.py create mode 100644 tests/performance/metrics/__init__.py create mode 100755 tests/performance/metrics_collector.py create mode 100644 tests/performance/metrics_monitoring_server.py create mode 100644 tests/performance/metrics_monitoring_taurus.py create mode 100644 tests/performance/process.py create mode 100644 tests/performance/requirements.txt create mode 100755 tests/performance/run_perfomance_suite.py create mode 100644 tests/performance/tests/call_jmx.yaml create mode 100644 tests/performance/tests/inference_server_monitoring.yaml create mode 100644 tests/performance/tests/inference_server_monitoring_criteria.yaml create mode 100644 tests/performance/tests/inference_taurus_local_monitoring.yaml create mode 100644 tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml create mode 100644 tests/performance/tests/register_and_inference.jmx diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..62cf84245 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +MMS Test Suites +""" diff --git a/tests/performance/README.md b/tests/performance/README.md new file mode 100644 index 000000000..d57dba8c5 --- /dev/null +++ b/tests/performance/README.md @@ -0,0 +1,218 @@ +# Performance Regression Suite + +This test suite helps in running the load tests and monitoring the process, sub-process and system wide metrics. It allows to specify the pass/fail criteria for metrics in the test case. +We use Taurus test automation framework to run the test cases and metrics monitoring. + +## How to run the test suite +To run the test suite just invoke the [run_perfomance_suite.py](run_perfomance_suite.py). You will have to provide the artifacts-dir path to store the test case results. +You can specify test cases to be run by providing 'test-dir' (default='monitoring/tests') and 'pattern' (default='*.yaml'). For other options use '--help' option. +The script starts the server monitoring agent, collects all the test cases, executes them and then produces Junit XML and HTML report in artifacts-dir. + +```bash +python run_perfomance_suite.py --artifacts-dir='' +``` + +#### To know more about the Test Suite follow the guide below: + +## Taurus + +[Taurus](https://gettaurus.org) is Apache 2.0 Licenced, test automation framework which extends and abstracts +different popular load testing frameworks such as JMeter, Locust etc. Taurus simplifies creating, running and analyzing the +performance tests. + +## Installation +Refer the Taurus installation guide [here](https://gettaurus.org/install/Installation/). + + +## How to create and run tests +To run a metrics monitoring test case, you need to specify below in Taurus test case yaml. +1. Load test case scenario +2. Metrics to monitor +3. Pass/fail criteria + + +#### 1. Load test case scenario +You can specify the test scenarios, in the scenario section of the yaml. + +There are multiple ways you can specify your test scenario. +1. Use existing JMeter/locust test script + + Taurus provides support for different executors such as JMeter. You can use test script written in those frameworks as it is. + Details about executor types are provided [here](https://gettaurus.org/docs/ExecutionSettings/). + Details about how to run an existing JMeter script are provided [here](https://gettaurus.org/docs/JMeter/). + + To get you started quickly, we have provided a sample JMeter script and a Taurus yaml file [here](tests/register_and_inference.jmx) and [here](tests/call_jmx.yaml) . + + Here is how the call_jmx.yaml looks like. Adjust the module/jmeter/properties section as per your environment. + + ```yaml + execution: + - concurrency: 1 + ramp-up: 1s + hold-for: 40s + scenario: Inference + scenarios: + Inference: + script: register_and_inference.jmx + modules: + jmeter: + properties: + hostname : 127.0.0.1 + port : 8080 + management_port : 8081 + protocol : http + input_filepath : kitten.jpg + + ``` + + Use Taurus command below to run the test case: + + ```bash + bzt call_jmx.yaml + ``` + +2. Write a Taurus script + + You can also write a Taurus script for a specific executor type. + For quick reference, we have provided a Taurus script with JMeter executor [here](tests/inference.yaml). + + Below is the yaml. + + ```yaml + execution: + - concurrency: 4 + ramp-up: 1s + hold-for: 20s + scenario: Inference + scenarios: + Inference: + requests: + - follow-redirects: true + label: Inference Request + method: POST + url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} + store-cache: false + store-cookie: false + use-dns-cache-mgr: false + variables: + model: ${__P(model_name,squeezenet_v1.1)} + + modules: + jmeter: + properties: + input_filepath : kitten.jpg + model_name : squeezenet + + ``` + + Use command Taurus command below to run the test yaml. Note this test script assumes squeezenet model is already registered. + + ```bash + bzt inference.yaml + ``` + +#### 2. Metrics to monitor +You can specify the different metrics to monitor in services/monitoring section of the yaml. + +Metrics can be monitored in two ways: +1. Standalone monitoring server + + If your server is hosted on different machine, you will be using this method. Before running the test case + you have to start a [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will be communicating with Taurus test client over sockets. + The address and port(default=9009) of the monitoring script should be specified in test case yaml. + + To install monitoring server dependencies, use the following command + ```bash + pip install -r requirements.txt + ``` + + To start monitoring script on server use command below: + + ```bash + python benchmarks/monitoring/metrics_monitoring_server.py + ``` + + Test yaml with monitoring section config. Complete yaml can be found [here](tests/inference_server_monitoring.yaml) + + ```yaml + + services: + - module: monitoring + server-agent: + - address: localhost:9009 # metric monitoring service address + label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port + interval: 1s # polling interval + logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir + metrics: # metrics should be supported by monitoring service + - sum_cpu_percent # cpu percent used by all the mms server processes and workers + - sum_memory_percent + - sum_num_handles + - server_workers # no of mms workers + ``` + + Use command Taurus command below to run the test yaml and observe the Metrics widget on CLI live report. + + ```bash + bzt inference_server_monitoring.yaml + ``` + + +2. Taurus local monitoring plugin + + If your test client is running on the server itself, you may want to use this method. + We have provided a custom Taurus plugin as [metrics_monitoring_taurus.py](metrics_monitoring_taurus.py). Make sure that the benchmarks/monitoring folder + is in PYTHONPATH. You need to specify the monitoring class. + + Use command below to add update PYTHONPATH. + + ```bash + export MMS_HOME= + export PYTHONPATH=$MMS_HOME/benchmarks/monitoring:$PYTHONPATH + ``` + + Relevant test yaml sections. Test yaml can be found [here](tests/inference_taurus_local_monitoring.yaml) + + ``` + modules: + server_local_monitoring: + # metrics_monitoring_taurus and dependencies should be in python path + class : metrics_monitoring_taurus.Monitor # monitoring class. + + services: + - module: server_local_monitoring # should be added in modules section + ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + - interval: 1s + metrics: + - cpu + - disk-space + - mem + - sum_memory_percent + + ``` + +#### 3. Pass/Fail criteria +You can specify the pass/fail criteria for the test cases. +Read more about it [here](https://gettaurus.org/docs/PassFail/) + +Relevant test yaml section: +```yaml +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: mms-inference-server/sum_num_handles + condition: '>' + threshold: 180 + timeframe: 1s + fail: true + stop: true + +``` + +Test yamls can be found [here](tests/inference_server_monitoring_criteria.yaml) and [here](tests/inference_taurus_local_monitoring_criteria.yaml). +Use command below to run the test case + +```bash +bzt inference_server_monitoring_criteria.yaml +bzt inference_taurus_local_monitoring_criteria.yaml +``` diff --git a/tests/performance/__init__.py b/tests/performance/__init__.py new file mode 100644 index 000000000..88db7ad29 --- /dev/null +++ b/tests/performance/__init__.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Perfomance Metrics Monitoring suite +""" diff --git a/tests/performance/config.ini b/tests/performance/config.ini new file mode 100644 index 000000000..27bc4c37b --- /dev/null +++ b/tests/performance/config.ini @@ -0,0 +1,6 @@ +[server] +pid_file = model_server.pid + +[monitoring] +HOST = +PORT = 9009 \ No newline at end of file diff --git a/tests/performance/configuration.py b/tests/performance/configuration.py new file mode 100644 index 000000000..e5ebeb998 --- /dev/null +++ b/tests/performance/configuration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Read configuration file +""" +# pylint: disable=redefined-builtin +import configparser + +config = configparser.ConfigParser() +config.read('./config.ini') + +def get(section, key, default=''): + try: + return config[section][key] + except: + return default diff --git a/tests/performance/metrics/__init__.py b/tests/performance/metrics/__init__.py new file mode 100644 index 000000000..637857180 --- /dev/null +++ b/tests/performance/metrics/__init__.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Custom metrics +""" +# pylint: disable=redefined-builtin + +AVAILABLE_METRICS = ["sum_cpu_percent", + "sum_memory_percent", + "sum_num_handles", + "server_workers"] + + +def get_metrics(server_process, child_processes): + """ Get Server processes specific metrics + """ + + # TODO - make this modular may be a diff function for each metric + # TODO - allow users to add new metrics easily + # TODO - make sure available metric list is maintained + + sum_cpu_percent = 0 + sum_memory_percent = 0 + sum_num_handles = 0 + server_workers = 0 + metrics = {} + for process in [server_process] + child_processes: + try: + process.cpu_percent() # to warm-up + except: + pass + else: + cpu_percent = process.cpu_percent() + memory_percent = process.memory_percent() + sum_cpu_percent += cpu_percent + sum_memory_percent += memory_percent + sum_num_handles += process.num_fds() + server_workers += 1 + + metrics["sum_cpu_percent"] = sum_cpu_percent + metrics["sum_memory_percent"] = sum_memory_percent + metrics["sum_num_handles"] = sum_num_handles + metrics["server_workers"] = server_workers + + return metrics \ No newline at end of file diff --git a/tests/performance/metrics_collector.py b/tests/performance/metrics_collector.py new file mode 100755 index 000000000..a14c94999 --- /dev/null +++ b/tests/performance/metrics_collector.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Server metrics collector +""" +# pylint: disable=redefined-builtin + +import os +import sys +import time +import logging +import psutil +import gevent +import argparse +import tempfile + +logger = logging.getLogger(__name__) + +from process import find_procs_by_name, get_process_pid_from_file, get_child_processes, \ + get_server_processes, get_server_pidfile +from metrics import AVAILABLE_METRICS, get_metrics + +TMP_DIR = tempfile.gettempdir() +METRICS_LOG_FILE = "{}/server_metrics_{}.log".format(TMP_DIR, int(time.time())) +METRICS_COLLECTOR_PID_FILE = "{}/.metrics_collector.pid".format(TMP_DIR) + +MONITOR_INTERVAL = 1 + + +def store_pid(pid_file): + """ Store the current process id to pid_file""" + process = psutil.Process() + pid_file = os.path.join(pid_file) + with open(pid_file, "w") as pf: + pf.write(str(process.pid)) + + +def stop_process(pid_file): + """This will stop already running process . + Note at a time only one pid file will be available. + """ + pid = get_process_pid_from_file(pid_file) + if pid: + try: + process = psutil.Process(pid) + if process.is_running(): + logger.info("Process with pid {} is running. Killing it.".format(process.pid)) + process.kill() + except Exception as e: + pass + else: + logger.info("Dead process with pid {} found in '{}'.".format(process.pid, pid_file)) + + logger.info("Removing pid file '{}'.".format(pid_file)) + os.remove(pid_file) + + +def check_is_running(pid_file): + pid = get_process_pid_from_file(pid_file) + if pid: + try: + perf_mon_process = psutil.Process(pid) + except Exception as e: + stop_process(pid_file) + else: + if perf_mon_process.is_running(): + logger.error("Performance monitoring script already running. " + "Stop it using stop option.") + sys.exit() + + +def store_metrics_collector_pid(): + """ Store the process id of metrics collector process""" + store_pid(METRICS_COLLECTOR_PID_FILE) + + +def stop_metrics_collector_process(): + """This will stop already running metrics collector process. + Note at a time only one pid file will be available. + """ + stop_process(METRICS_COLLECTOR_PID_FILE) + + +def monitor_processes(server_process, metrics, interval, socket): + """ Monitor the metrics of server_process and its child processes + """ + while True: + message = [] + collected_metrics = get_metrics(server_process, get_child_processes(server_process)) + for metric in metrics: + message.append(str(collected_metrics.get(metric, 0))) + message = "\t".join(message)+"\n" + + if socket: + socket.send(message.encode("latin-1")) + + # TODO - log metrics to a file METRICS_LOG_FILE if METRICS_LOG_FILE is provided + gevent.sleep(interval) + + +def start_metric_collection(server_process, metrics, interval, socket): + bad_metrics = set(metrics) - set(AVAILABLE_METRICS) + if bad_metrics: + raise Exception("Metrics not available for monitoring {}.".format(bad_metrics)) + + logger.info("Started metric collection for target server processes.....") + thread = gevent.spawn(monitor_processes, server_process, metrics, interval, socket) + gevent.joinall([thread]) + + +def start_metric_collector_process(): + """Spawn a metric collection process and keep on monitoring """ + + check_is_running(METRICS_COLLECTOR_PID_FILE) + store_metrics_collector_pid() + server_pid = get_process_pid_from_file(get_server_pidfile()) + server_process = get_server_processes(server_pid) + start_metric_collection(server_process, AVAILABLE_METRICS, MONITOR_INTERVAL, None) + + +if __name__ == "__main__": + logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + parser = argparse.ArgumentParser(prog='perf-mon-script', description='System Performance Monitoring') + sub_parse = parser.add_mutually_exclusive_group(required=True) + sub_parse.add_argument('--start', action='store_true', help='Start the perf-mon-script') + sub_parse.add_argument('--stop', action='store_true', help='Stop the perf-mon-script') + + args = parser.parse_args() + + if args.start: + start_metric_collector_process() + elif args.stop: + stop_metrics_collector_process() diff --git a/tests/performance/metrics_monitoring_server.py b/tests/performance/metrics_monitoring_server.py new file mode 100644 index 000000000..98fd73155 --- /dev/null +++ b/tests/performance/metrics_monitoring_server.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Remote server monitoring script +""" +# pylint: disable=redefined-builtin + +import logging +import sys +from gevent import socket +from gevent import select +from gevent import monkey +import argparse +import tempfile + +monkey.patch_select() +monkey.patch_socket() + +logger = logging.getLogger(__name__) +from metrics_collector import start_metric_collection, stop_process, store_pid, check_is_running +from process import find_procs_by_name, get_process_pid_from_file, \ + get_child_processes, get_server_processes, get_server_pidfile +import configuration + +TMP_DIR = tempfile.gettempdir() +METRICS_MON_SERVER_PID_FILE = "{}/.metrics_monitoring_server.pid".format(TMP_DIR) + +HOST = str(configuration.get('monitoring','HOST')) +PORT = int(configuration.get('monitoring','PORT', 9009)) + + +SOCKET_LIST = [] +RECV_BUFFER = 4096 + +interval = 1 + + +def perf_server(): + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server_socket.bind((HOST, PORT)) + server_socket.listen(10) + SOCKET_LIST.append(server_socket) + logger.info("Started metrics monitoring server on port {}".format(PORT)) + + while True: + ready_to_read, ready_to_write, in_error = select.select(SOCKET_LIST, [], [], 0) + + for sock in ready_to_read: + # a new connection request recieved + if sock == server_socket: + sockfd, addr = server_socket.accept() + SOCKET_LIST.append(sockfd) + logger.info("client ({}, {}) connected".format(addr[0], addr[1])) + + # a message from a client, not a new connection + else: + try: + # receiving data from the socket. + data = sock.recv(RECV_BUFFER).decode() + if data: + if data == 'test\n': + send_message(sock, "Yep\n") + elif data == 'exit\n': + close_socket(sock) + elif data.startswith('interval'): + try: + global interval + interval = int(data.split(":")[1][:-1]) + except Exception: + send_message(sock, "In-correct interval data") + elif data.startswith('metrics'): + metrics = data[:-1].split("metrics:")[1].split("\t") + server_pid = get_process_pid_from_file(get_server_pidfile()) + server_process = get_server_processes(server_pid) + start_metric_collection(server_process, metrics, interval, sock) + else: + # TODO - decide what to do here + pass + + else: + # remove the socket that's broken + if sock in SOCKET_LIST: + SOCKET_LIST.remove(sock) + except Exception as e: + logger.warning("Error {}".format(str(e))) + continue + + server_socket.close() + + +def send_message(socket, message): + try: + socket.send(message.encode("latin-1")) + except Exception as e: + logger.warning("Error while sending the message {}. Closing the socket.".format(str(e))) + close_socket(socket) + + +def close_socket(socket): + socket.close() + if socket in SOCKET_LIST: + SOCKET_LIST.remove(socket) + + +if __name__ == "__main__": + logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + parser = argparse.ArgumentParser(prog='perf-mon-script', description='System Performance Monitoring') + sub_parse = parser.add_mutually_exclusive_group(required=True) + sub_parse.add_argument('--start', action='store_true', help='Start the perf-mon-script') + sub_parse.add_argument('--stop', action='store_true', help='Stop the perf-mon-script') + + args = parser.parse_args() + + if args.start: + check_is_running(METRICS_MON_SERVER_PID_FILE) + store_pid(METRICS_MON_SERVER_PID_FILE) + perf_server() + elif args.stop: + stop_process(METRICS_MON_SERVER_PID_FILE) + + + + diff --git a/tests/performance/metrics_monitoring_taurus.py b/tests/performance/metrics_monitoring_taurus.py new file mode 100644 index 000000000..ac402c7db --- /dev/null +++ b/tests/performance/metrics_monitoring_taurus.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Taurus Local plugin for server monitoring. +Should be used when server and Taurus are running on same machine. +This file should be placed in Python Path along with monitoring package. +""" +# pylint: disable=redefined-builtin + +import csv +from bzt.modules import monitoring +from bzt import TaurusConfigError +from bzt.utils import dehumanize_time +import sys + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +from metrics import get_metrics, AVAILABLE_METRICS as AVAILABLE_SERVER_METRICS +from process import get_process_pid_from_file, get_server_processes, \ + get_child_processes, get_server_pidfile + + +class Monitor(monitoring.Monitoring): + def __init__(self): + super(Monitor, self).__init__() + self.client_classes.update({'ServerLocalClient': ServerLocalClient}) + + +class ServerLocalClient(monitoring.LocalClient): + AVAILABLE_METRICS = monitoring.LocalClient.AVAILABLE_METRICS + \ + AVAILABLE_SERVER_METRICS + + def __init__(self, parent_log, label, config, engine=None): + + super(ServerLocalClient, self).__init__(parent_log, label, config, engine=engine) + if label: + self.label = label + else: + self.label = 'ServerLocalClient' + + def connect(self): + exc = TaurusConfigError('Metric is required in Local monitoring client') + metric_names = self.config.get('metrics', exc) + + bad_list = set(metric_names) - set(self.AVAILABLE_METRICS) + if bad_list: + self.log.warning('Wrong metrics found: %s', bad_list) + + good_list = set(metric_names) & set(self.AVAILABLE_METRICS) + if not good_list: + raise exc + + self.metrics = list(set(good_list)) + + self.monitor = ServerLocalMonitor(self.log, self.metrics, self.engine) + self.interval = dehumanize_time(self.config.get("interval", self.engine.check_interval)) + + if self.config.get("logging", False): + if not PY3: + self.log.warning("Logging option doesn't work on python2.") + else: + self.logs_file = self.engine.create_artifact("local_monitoring_logs", ".csv") + with open(self.logs_file, "a", newline='') as mon_logs: + logs_writer = csv.writer(mon_logs, delimiter=',') + metrics = ['ts'] + sorted([metric for metric in good_list]) + logs_writer.writerow(metrics) + + +class ServerLocalMonitor(monitoring.LocalMonitor): + + def _calc_resource_stats(self, interval): + result = super()._calc_resource_stats(interval) + server_pid = get_process_pid_from_file(get_server_pidfile()) + server_process = get_server_processes(server_pid) + result.update(get_metrics(server_process, get_child_processes(server_process))) + return result diff --git a/tests/performance/process.py b/tests/performance/process.py new file mode 100644 index 000000000..c8f84bfba --- /dev/null +++ b/tests/performance/process.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Utility methods for process related information +""" +# pylint: disable=redefined-builtin + +import os +import psutil +import tempfile +import configuration + +def find_procs_by_name(name): + """Return a list of processes matching 'name'.""" + ls = [] + for p in psutil.process_iter(["name", "exe", "cmdline"]): + if name == p.info['name'] or \ + p.info['exe'] and os.path.basename(p.info['exe']) == name or \ + p.info['cmdline'] and p.info['cmdline'][0] == name: + ls.append(p) + + if len(ls) > 1: + raise Exception("Multiple processes found with name {}.".format(name)) + + return ls[0] + + +def get_process_pid_from_file(file_path): + """Get the process pid from pid file. + """ + pid = None + if os.path.isfile(file_path): + with open(file_path, "r") as f: + pid = int(f.readline()) + + return pid + + +def get_child_processes(process): + """Get all running child processes recursively""" + child_processes = [] + for p in process.children(recursive=True): + if p.status() == 'running': + child_processes.append(p) + return child_processes + + +def get_server_processes(server_process_pid): + """ It caches the main server and child processes at module level. + Ensure that you call this process so that MMS process + """ + try: + server_process = psutil.Process(server_process_pid) + except Exception as e: + print("Server process not found. Error: {}".format(str(e))) + raise + return server_process + + +def get_server_pidfile(): + return "{0}/.{1}".format(tempfile.gettempdir(), configuration.get('server', 'pid_file', 'model_server.pid')) diff --git a/tests/performance/requirements.txt b/tests/performance/requirements.txt new file mode 100644 index 000000000..2c29825ff --- /dev/null +++ b/tests/performance/requirements.txt @@ -0,0 +1,5 @@ +gevent==20.5.2 +junitparser==1.4.1 +vjunit==0.6.1 +tqdm==4.40.0 +pathlib diff --git a/tests/performance/run_perfomance_suite.py b/tests/performance/run_perfomance_suite.py new file mode 100755 index 000000000..fb1d1089e --- /dev/null +++ b/tests/performance/run_perfomance_suite.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Run Tarus test cases and generate the Junit XML report +""" +# pylint: disable=redefined-builtin + +import os +import sys +import time +import logging +import argparse +import glob +import pathlib +import subprocess +import socket +from tqdm import tqdm +from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure + +logger = logging.getLogger(__name__) +code = 0 + + +class Timer(object): + def __init__(self, description): + self.description = description + + def __enter__(self): + self.start = int(time.time()) + return self + + def __exit__(self, type, value, traceback): + logger.info(f"{self.description}: {self.diff()}s") + + def diff(self): + return int(time.time()) - self.start + + +def run_process(cmd, wait=True): + print("running command : {}".format(cmd)) + + if wait: + try: + output = subprocess.check_output( + cmd, stderr=subprocess.STDOUT, shell=True, timeout=30*60, + universal_newlines=True) + except subprocess.CalledProcessError as exc: + print("Status : FAIL", exc.returncode, exc.output) + return exc.returncode, exc.output + else: + print("Output: \n{}\n".format(output)) + return 0, output + + else: + p = subprocess.Popen(cmd, shell=True) + return 1, '' + + +def get_test_yamls(dir_path=None, pattern="*.yaml"): + if not dir_path: + path = pathlib.Path(__file__).parent.absolute() + dir_path = str(path) + "/tests" + + path_pattern = "{}/{}".format(dir_path, pattern) + return glob.glob(path_pattern) + + +def get_options(artifacts_dir, jmeter_path=None): + options=[] + if jmeter_path: + options.append('-o modules.jmeter.path={}'.format(jmeter_path)) + options.append('-o settings.artifacts-dir={}'.format(artifacts_dir)) + options.append('-o modules.console.disable=true') + options.append('-o settings.env.BASEDIR={}'.format(artifacts_dir)) + options_str = ' '.join(options) + + return options_str + + +def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): + if os.path.exists(artifacts_dir): + artifacts_dir = "{}_{}".format(artifacts_dir, str(int(time.time()))) + path = pathlib.Path(__file__).parent.absolute() + start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) + run_process(start_monitoring_server, wait=False) + + junit_xml = JUnitXml() + pre_command = 'export PYTHONPATH={}:$PYTHONPATH; '.format(str(path)) + + test_yamls = get_test_yamls(test_dir, pattern) + for test_file in tqdm(test_yamls, desc="Test Suites"): + suite_name = os.path.basename(test_file).rsplit('.', 1)[0] + with Timer("Test suit {} execution time".format(suite_name)) as t: + suit_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) + options_str = get_options(suit_artifacts_dir, jmeter_path) + code, err = run_process("{} bzt {} {} ".format(pre_command, options_str, test_file)) + suite_time = t.diff() + suite_start = t.start + + # Assumes default file name + xunit_file = "{}/xunit.xml".format(suit_artifacts_dir) + + tests, failures, skipped, errors = 0, 0, 0, 0 + err_txt = "" + ts = TestSuite(suite_name) + if os.path.exists(xunit_file): + xml = JUnitXml.fromfile(xunit_file) + for i, suite in enumerate(xml): #tqqdm + for case in suite: + name = "scenario_{}: {}".format(i, case.name) + result = case.result + if isinstance(result, Error): + errors += 1 + err_txt = err + elif isinstance(result, Failure): + failures += 1 + err_txt = err + elif isinstance(result, Skipped): + skipped += 1 + else: + tests +=1 + + tc = TestCase(name) + tc.result = result + tc.system_err = err_txt[:-4] + ts.add_testcase(tc) + else: + tc = TestCase(suite_name) + if code: + tc.result = Error("Suite run failed", "Error") + tc.system_err = err[:-4] + else: + tc.result = Skipped() + tc.system_out = err[:-4] + ts.add_testcase(tc) + + ts.hostname = socket.gethostname() + ts.timestamp = suite_start + ts.time = suite_time + ts.tests = tests + ts.failures = failures + ts.skipped = skipped + ts.errors = errors + ts.update_statistics() + junit_xml.add_testsuite(ts) + + junit_xml.update_statistics() + junit_xml_path = '{}/junit.xml'.format(artifacts_dir) + junit_html_path = '{}/junit.html'.format(artifacts_dir) + junit_xml.write(junit_xml_path) + run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) + + stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) + run_process(stop_monitoring_server) + + if junit_xml.errors or junit_xml.failures or junit_xml.skipped: + sys.exit(3) + + +if __name__ == "__main__": + logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + parser = argparse.ArgumentParser(prog='run_perfomance_suite.py', description='Performance Test Suite Runner') + parser.add_argument('-a', '--artifacts-dir', nargs=1, type=str, dest='artifacts', required=True, + help='A artifacts directory') + + parser.add_argument('-d', '--test-dir', nargs=1, type=str, dest='test_dir', default=[None], + help='A test dir') + + parser.add_argument('-p', '--pattern', nargs=1, type=str, dest='pattern', default=["*.yaml"], + help='Test case file name pattern. example *.yaml') + + parser.add_argument('-j', '--jmeter-path', nargs=1, type=str, dest='jmeter_path', default=[None], + help='JMeter executable bin path') + + args = parser.parse_args() + run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0], args.jmeter_path[0]) diff --git a/tests/performance/tests/call_jmx.yaml b/tests/performance/tests/call_jmx.yaml new file mode 100644 index 000000000..7973ee521 --- /dev/null +++ b/tests/performance/tests/call_jmx.yaml @@ -0,0 +1,29 @@ +--- +execution: +- concurrency: 1 + ramp-up: 1s + hold-for: 40s + scenario: Inference +scenarios: + Inference: + script: register_and_inference.jmx +modules: + jmeter: + properties: + port : 8080 + management_port : 8081 + protocol : http + input_filepath : kitten.jpg +reporting: +- module: passfail # this is to enable passfail module +- module: junit-xml + data-source: pass-fail +- module: junit-xml + data-source: sample-labels +- module: final-stats + dump-csv : ${BASEDIR}/final_stats.csv + +setting: + env: + BASEDIR : '.' + diff --git a/tests/performance/tests/inference_server_monitoring.yaml b/tests/performance/tests/inference_server_monitoring.yaml new file mode 100644 index 000000000..d23af46b1 --- /dev/null +++ b/tests/performance/tests/inference_server_monitoring.yaml @@ -0,0 +1,44 @@ + +execution: +- concurrency: 4 + ramp-up: 1s + hold-for: 20s + scenario: Inference +scenarios: + Inference: + script: register_and_inference.jmx + +modules: + jmeter: + properties: + port : 8080 + management_port : 8081 + protocol : http + input_filepath : kitten.jpg + + +services: + - module: monitoring + server-agent: + - address: localhost:9009 # metric monitoring service address + label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port + interval: 1s # polling interval + logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir + metrics: # metrics should be supported by monitoring service + - sum_cpu_percent # cpu percent used by all the mms server processes and workers + - sum_memory_percent + - sum_num_handles + - server_workers # no of mms workers + +reporting: +- module: passfail # this is to enable passfail module +- module: junit-xml + data-source: pass-fail +- module: junit-xml + data-source: sample-labels +- module: final-stats + dump-csv : ${BASEDIR}/final_stats.csv + +setting: + env: + BASEDIR : '.' \ No newline at end of file diff --git a/tests/performance/tests/inference_server_monitoring_criteria.yaml b/tests/performance/tests/inference_server_monitoring_criteria.yaml new file mode 100644 index 000000000..ace86a732 --- /dev/null +++ b/tests/performance/tests/inference_server_monitoring_criteria.yaml @@ -0,0 +1,56 @@ + +execution: +- concurrency: 4 + ramp-up: 1s + hold-for: 20s + scenario: Inference +scenarios: + Inference: + script: register_and_inference.jmx + +modules: + jmeter: + properties: + port : 8080 + management_port : 8081 + protocol : http + input_filepath : kitten.jpg + + +services: + - module: monitoring + server-agent: + - address: localhost:9009 # metric monitoring service address + label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port + interval: 1s # polling interval + logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir + metrics: # metrics should be supported by monitoring service + - sum_cpu_percent # cpu percent used by all the mms server processes and workers + - sum_memory_percent + - sum_num_handles + - server_workers # no of mms workers + + +reporting: +- module: passfail + criteria: + - fail of s2_fail >50%, stop as failed + - p90 of s2_p90 >250ms , stop as failed + - avg-rt of s2_avg_rt >1s , stop as failed + - class: bzt.modules.monitoring.MonitoringCriteria + subject: mms-inference-server/sum_num_handles + condition: '>' + threshold: 80 + timeframe: 1s + fail: true + stop: true +- module: junit-xml + data-source: pass-fail +- module: junit-xml + data-source: sample-labels +- module: final-stats + dump-csv : ${BASEDIR}/final_stats.csv + +setting: + env: + BASEDIR : '.' \ No newline at end of file diff --git a/tests/performance/tests/inference_taurus_local_monitoring.yaml b/tests/performance/tests/inference_taurus_local_monitoring.yaml new file mode 100644 index 000000000..99a7d2910 --- /dev/null +++ b/tests/performance/tests/inference_taurus_local_monitoring.yaml @@ -0,0 +1,48 @@ +--- +execution: +- concurrency: 1 + ramp-up: 5s + hold-for: 20s + scenario: Inference +scenarios: + Inference: + script: register_and_inference.jmx + +modules: + jmeter: + properties: + port : 8080 + management_port : 8081 + protocol : http + input_filepath : kitten.jpg + server_local_monitoring: + # metrics_monitoring_taurus and dependencies should be in python path + class : metrics_monitoring_taurus.Monitor # monitoring class. + +services: + - module: server_local_monitoring # should be added in modules section + ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + - interval: 1s + metrics: + - cpu + - disk-space + - mem + - sum_memory_percent + - module: monitoring + ~local: + - interval: 20s # polling interval + logging: True # local monitoring logs will be saved to "local_monitoring_logs.csv" in the artifacts dir + metrics: + - cpu +reporting: +- module: passfail # this is to enable passfail module +- module: junit-xml + data-source: pass-fail +- module: junit-xml + data-source: sample-labels +- module: final-stats + dump-csv : ${BASEDIR}/final_stats.csv + +setting: + env: + BASEDIR : '.' \ No newline at end of file diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml new file mode 100644 index 000000000..98cb72eb3 --- /dev/null +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml @@ -0,0 +1,55 @@ +--- +execution: +- concurrency: 1 + ramp-up: 5s + hold-for: 20s + scenario: Inference +scenarios: + Inference: + script: register_and_inference.jmx + +modules: + jmeter: + properties: + port : 8080 + management_port : 8081 + protocol : http + input_filepath : kitten.jpg + server_local_monitoring: + # metrics_monitoring_taurus and dependencies should be in python path + class : metrics_monitoring_taurus.Monitor # monitoring class. + +services: + - module: server_local_monitoring # should be added in modules section + ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + - interval: 1s + metrics: + - cpu + - disk-space + - mem + - sum_memory_percent + +reporting: +- module: passfail + criteria: + - fail of s2_fail >50%, stop as failed + - p90 of s2_p90 >250ms , stop as failed + - avg-rt of s2_avg_rt >1s , stop as failed + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_memory_percent + condition: '>' + threshold: 10 + timeframe: 1s + stop : true + fail : true + +- module: junit-xml + data-source: pass-fail +- module: junit-xml + data-source: sample-labels +- module: final-stats + dump-csv : ${BASEDIR}/final_stats.csv + +setting: + env: + BASEDIR : '.' \ No newline at end of file diff --git a/tests/performance/tests/register_and_inference.jmx b/tests/performance/tests/register_and_inference.jmx new file mode 100644 index 000000000..8f7b21e6d --- /dev/null +++ b/tests/performance/tests/register_and_inference.jmx @@ -0,0 +1,189 @@ + + + + + + false + true + false + + + + + + + + + + cnn_url + https://s3.amazonaws.com/model-server/models/squeezenet_v1.1/squeezenet_v1.1.model + = + The url from where to fetch noop model from + + + scale_up_workers + ${__P(min_workers,1)} + = + The workers to scale No op model to + + + scale_down_workers + 0 + Offload the No-Op Model + = + + + model + squeezenet_v1.1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${cnn_url} + POST + true + false + true + false + + + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_up_workers} + PUT + true + false + true + false + + + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_down_workers} + DELETE + true + false + true + false + + + + + + + + + From 101323afe7eca1d2b522ad8752c700350e6052d3 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 13:39:49 +0530 Subject: [PATCH 045/329] small fixes --- tests/performance/README.md | 3 + tests/performance/metrics_collector.py | 6 +- tests/performance/new_passfail.py | 180 ++++++++++++++++++ tests/performance/run_perfomance_suite.py | 35 ++-- tests/performance/tests/call_jmx.yaml | 2 +- .../tests/inference_server_monitoring.yaml | 2 +- .../inference_server_monitoring_criteria.yaml | 2 +- .../inference_taurus_local_monitoring.yaml | 2 +- ...ence_taurus_local_monitoring_criteria.yaml | 8 +- 9 files changed, 218 insertions(+), 22 deletions(-) create mode 100644 tests/performance/new_passfail.py diff --git a/tests/performance/README.md b/tests/performance/README.md index d57dba8c5..7ca527782 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -7,6 +7,9 @@ We use Taurus test automation framework to run the test cases and metrics monito To run the test suite just invoke the [run_perfomance_suite.py](run_perfomance_suite.py). You will have to provide the artifacts-dir path to store the test case results. You can specify test cases to be run by providing 'test-dir' (default='monitoring/tests') and 'pattern' (default='*.yaml'). For other options use '--help' option. The script starts the server monitoring agent, collects all the test cases, executes them and then produces Junit XML and HTML report in artifacts-dir. +Note: It assumes that Model Server is already started. The different JMeter test case parameters such as Model Server Host, Port, image path are specified in test yamls. Modify as per your setup. +In future a global config file will be provided for commonly used parameters. + ```bash python run_perfomance_suite.py --artifacts-dir='' diff --git a/tests/performance/metrics_collector.py b/tests/performance/metrics_collector.py index a14c94999..5fa7269e5 100755 --- a/tests/performance/metrics_collector.py +++ b/tests/performance/metrics_collector.py @@ -102,7 +102,11 @@ def monitor_processes(server_process, metrics, interval, socket): message = "\t".join(message)+"\n" if socket: - socket.send(message.encode("latin-1")) + try: + socket.send(message.encode("latin-1")) + except BrokenPipeError: + logger.info("Stopping monitoring as socket connection is closed.") + break # TODO - log metrics to a file METRICS_LOG_FILE if METRICS_LOG_FILE is provided gevent.sleep(interval) diff --git a/tests/performance/new_passfail.py b/tests/performance/new_passfail.py new file mode 100644 index 000000000..d9ee91392 --- /dev/null +++ b/tests/performance/new_passfail.py @@ -0,0 +1,180 @@ +from bzt.modules.passfail import FailCriterion, PassFailStatus +from abc import abstractmethod + + +class FailCriterion1(object): + def __init__(self, config, owner): + self.owner = owner + self.config = config + self.agg_buffer = OrderedDict() + if not 'threshold' in config: + raise TaurusConfigError("Criteria string is malformed in its threshold part.") + self.percentage = str(config['threshold']).endswith('%') + if not 'subject' in config: + raise TaurusConfigError("Criteria string is malformed in its subject part.") + if config['subject'] == 'bytes': + self.threshold = get_bytes_count(config.get('threshold')) + else: + self.threshold = dehumanize_time(config.get('threshold')) + + self.get_value = self._get_field_functor(config['subject'], self.percentage) + self.window_logic = config.get('logic', 'for') + self.agg_logic = self._get_aggregator_functor(self.window_logic, config['subject']) + if not 'condition' in config: + raise TaurusConfigError("Criteria string is malformed in its condition part.") + self.condition = self._get_condition_functor(config.get('condition')) + + self.stop = config.get('stop', True) + self.fail = config.get('fail', True) + self.message = config.get('message', None) + self.window = dehumanize_time(config.get('timeframe', 0)) + self._start = sys.maxsize + self._end = 0 + self.is_candidate = False + self.is_triggered = False + + self.get_threshold = config.get('get_threshold', None) + if self.get_threshold: + self.threshold == 'init' + self.prev_data_str = None + + def __repr__(self): + if self.is_triggered: + if self.fail: + state = "Failed" + else: + state = "Notice" + else: + state = "Alert" + + if self.message is not None: + return "%s: %s" % (state, self.message) + else: + data = (state, + self.config['subject'], + self.config['condition'], + self.config['threshold'], + self.window_logic, + self.get_counting()) + return "%s: %s%s%s %s %d sec" % data + + def process_criteria_logic(self, tstmp, get_value): + value = self.agg_logic(tstmp, get_value) + if self.threshold == 'init': + self.threshold = value + state = self.condition(value, self.threshold) + + if self.window_logic == 'for': + if state: + self._start = min(self._start, tstmp) + self._end = tstmp + else: + self._start = sys.maxsize + self._end = 0 + + if self.get_counting() >= self.window: + self.trigger(value) + elif self.window_logic == 'within' and state: + self._start = tstmp - self.window + 1 + self._end = tstmp + self.trigger(value) + elif self.window_logic == 'over' and state: + min_buffer_tstmp = min(self.agg_buffer.keys()) + self._start = min_buffer_tstmp + self._end = tstmp + if self.get_counting() >= self.window: + self.trigger(value) + + self.owner.log.debug("%s %s: %s", tstmp, self, state) + + def trigger(self, value): + print("triggered value is "+str(value)) + if self.get_threshold: + self.threshold = value + data_search = [self.config['subject'], self.config['condition'], + self.config['threshold'], self.window_logic] + data_search_str = ','.join(data_search) + data_str = ','.join(data_search + [value]) + replaced = False + + with open(self.get_threshold, "r+") as f: + lines = f.readlines() + for i, line in enumerate(lines): + if line.startswith(data_search_str): + lines[i] = data_str + replaced = True + break + + if not replaced: + lines.append(data_str) + + f.seek(0) + f.writelines(lines) + + else: + if not self.is_triggered: + self.owner.log.warning("%s", self) + self.is_triggered = True + + def check(self): + """ + Interrupt the execution if desired condition occured + :raise AutomatedShutdown: + """ + if self.stop and self.is_triggered: + if self.fail: + self.owner.log.info("Pass/Fail criterion triggered shutdown: %s", self) + raise AutomatedShutdown("%s" % self) + else: + return True + return False + + @abstractmethod + def _get_field_functor(self, subject, percentage): + pass + + def _get_condition_functor(self, cond): + if cond == '=' or cond == '==': + return lambda x, y: x == y + elif cond == '>': + return lambda x, y: x > y + elif cond == '>=': + return lambda x, y: x >= y + elif cond == '<': + return lambda x, y: x < y + elif cond == '<=': + return lambda x, y: x <= y + else: + raise TaurusConfigError("Unsupported fail criteria condition: %s" % cond) + + def _get_aggregator_functor(self, logic, _subject): + if logic == 'for': + return lambda tstmp, value: value + elif logic in ('within', 'over'): + return self._within_aggregator_avg # FIXME: having simple average for percented values is a bit wrong + else: + raise TaurusConfigError("Unsupported window logic: %s" % logic) + + def _get_windowed_points(self, tstmp, value): + self.agg_buffer[tstmp] = value + keys = list(self.agg_buffer.keys()) + for tstmp_old in keys: + if tstmp_old <= tstmp - self.window: + del self.agg_buffer[tstmp_old] + continue + break + + return viewvalues(self.agg_buffer) + + def _within_aggregator_sum(self, tstmp, value): + return sum(self._get_windowed_points(tstmp, value)) + + def _within_aggregator_avg(self, tstmp, value): + points = self._get_windowed_points(tstmp, value) + return sum(points) / len(points) + + def get_counting(self): + return self._end - self._start + 1 + + +FailCriterion = FailCriterion1 diff --git a/tests/performance/run_perfomance_suite.py b/tests/performance/run_perfomance_suite.py index fb1d1089e..9d213d243 100755 --- a/tests/performance/run_perfomance_suite.py +++ b/tests/performance/run_perfomance_suite.py @@ -19,11 +19,12 @@ import sys import time import logging +import socket import argparse import glob import pathlib import subprocess -import socket +from subprocess import PIPE, STDOUT from tqdm import tqdm from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure @@ -50,16 +51,18 @@ def run_process(cmd, wait=True): print("running command : {}".format(cmd)) if wait: - try: - output = subprocess.check_output( - cmd, stderr=subprocess.STDOUT, shell=True, timeout=30*60, - universal_newlines=True) - except subprocess.CalledProcessError as exc: - print("Status : FAIL", exc.returncode, exc.output) - return exc.returncode, exc.output - else: - print("Output: \n{}\n".format(output)) - return 0, output + os.environ["PYTHONUNBUFFERED"] = "1" + p = subprocess.Popen(cmd, stdout=PIPE, stderr=STDOUT, + shell=True) + lines =[] + while True: + line = p.stdout.readline().decode('utf-8').rstrip() + if not line: break + lines.append(line) + print(line) + + return p.returncode, ''.join(lines) + else: p = subprocess.Popen(cmd, shell=True) @@ -91,7 +94,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): if os.path.exists(artifacts_dir): artifacts_dir = "{}_{}".format(artifacts_dir, str(int(time.time()))) path = pathlib.Path(__file__).parent.absolute() - start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) + start_monitoring_server = "python3 {}/metrics_monitoring_server.py --start".format(path) run_process(start_monitoring_server, wait=False) junit_xml = JUnitXml() @@ -100,7 +103,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): test_yamls = get_test_yamls(test_dir, pattern) for test_file in tqdm(test_yamls, desc="Test Suites"): suite_name = os.path.basename(test_file).rsplit('.', 1)[0] - with Timer("Test suit {} execution time".format(suite_name)) as t: + with Timer("Test suite {} execution time".format(suite_name)) as t: suit_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) options_str = get_options(suit_artifacts_dir, jmeter_path) code, err = run_process("{} bzt {} {} ".format(pre_command, options_str, test_file)) @@ -109,7 +112,6 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): # Assumes default file name xunit_file = "{}/xunit.xml".format(suit_artifacts_dir) - tests, failures, skipped, errors = 0, 0, 0, 0 err_txt = "" ts = TestSuite(suite_name) @@ -160,7 +162,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): junit_xml.write(junit_xml_path) run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) - stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python3 {}/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: @@ -182,5 +184,8 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): parser.add_argument('-j', '--jmeter-path', nargs=1, type=str, dest='jmeter_path', default=[None], help='JMeter executable bin path') + parser.add_argument('-t', '--taurus-console', nargs=1, type=bool, dest='taurus_console', default=[None], + help='JMeter executable bin path') + args = parser.parse_args() run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0], args.jmeter_path[0]) diff --git a/tests/performance/tests/call_jmx.yaml b/tests/performance/tests/call_jmx.yaml index 7973ee521..d1d84f195 100644 --- a/tests/performance/tests/call_jmx.yaml +++ b/tests/performance/tests/call_jmx.yaml @@ -23,7 +23,7 @@ reporting: - module: final-stats dump-csv : ${BASEDIR}/final_stats.csv -setting: +settings: env: BASEDIR : '.' diff --git a/tests/performance/tests/inference_server_monitoring.yaml b/tests/performance/tests/inference_server_monitoring.yaml index d23af46b1..fd32cd9ff 100644 --- a/tests/performance/tests/inference_server_monitoring.yaml +++ b/tests/performance/tests/inference_server_monitoring.yaml @@ -39,6 +39,6 @@ reporting: - module: final-stats dump-csv : ${BASEDIR}/final_stats.csv -setting: +settings: env: BASEDIR : '.' \ No newline at end of file diff --git a/tests/performance/tests/inference_server_monitoring_criteria.yaml b/tests/performance/tests/inference_server_monitoring_criteria.yaml index ace86a732..2d580fab3 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_server_monitoring_criteria.yaml @@ -51,6 +51,6 @@ reporting: - module: final-stats dump-csv : ${BASEDIR}/final_stats.csv -setting: +settings: env: BASEDIR : '.' \ No newline at end of file diff --git a/tests/performance/tests/inference_taurus_local_monitoring.yaml b/tests/performance/tests/inference_taurus_local_monitoring.yaml index 99a7d2910..2a54d8b31 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring.yaml @@ -43,6 +43,6 @@ reporting: - module: final-stats dump-csv : ${BASEDIR}/final_stats.csv -setting: +settings: env: BASEDIR : '.' \ No newline at end of file diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml index 98cb72eb3..bac2be21d 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml @@ -18,6 +18,10 @@ modules: server_local_monitoring: # metrics_monitoring_taurus and dependencies should be in python path class : metrics_monitoring_taurus.Monitor # monitoring class. + new_passfail: + class: new_passfail.PassFailStatus + + services: - module: server_local_monitoring # should be added in modules section @@ -38,7 +42,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_memory_percent condition: '>' - threshold: 10 + threshold: 4 timeframe: 1s stop : true fail : true @@ -50,6 +54,6 @@ reporting: - module: final-stats dump-csv : ${BASEDIR}/final_stats.csv -setting: +settings: env: BASEDIR : '.' \ No newline at end of file From 28a4a066d9278178e9c6f41adc90559a41afe314 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 13:48:45 +0530 Subject: [PATCH 046/329] changes --- tests/performance/run_perfomance_suite.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/performance/run_perfomance_suite.py b/tests/performance/run_perfomance_suite.py index 9d213d243..35da48343 100755 --- a/tests/performance/run_perfomance_suite.py +++ b/tests/performance/run_perfomance_suite.py @@ -61,7 +61,7 @@ def run_process(cmd, wait=True): lines.append(line) print(line) - return p.returncode, ''.join(lines) + return p.returncode, '\n'.join(lines) else: @@ -94,7 +94,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): if os.path.exists(artifacts_dir): artifacts_dir = "{}_{}".format(artifacts_dir, str(int(time.time()))) path = pathlib.Path(__file__).parent.absolute() - start_monitoring_server = "python3 {}/metrics_monitoring_server.py --start".format(path) + start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) run_process(start_monitoring_server, wait=False) junit_xml = JUnitXml() @@ -162,7 +162,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): junit_xml.write(junit_xml_path) run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) - stop_monitoring_server = "python3 {}/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: From 386a348a66d10a4eb53855b234c13d859f58cf72 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 13:51:58 +0530 Subject: [PATCH 047/329] read me fixes --- tests/performance/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 7ca527782..e3e292a6d 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -6,9 +6,11 @@ We use Taurus test automation framework to run the test cases and metrics monito ## How to run the test suite To run the test suite just invoke the [run_perfomance_suite.py](run_perfomance_suite.py). You will have to provide the artifacts-dir path to store the test case results. You can specify test cases to be run by providing 'test-dir' (default='monitoring/tests') and 'pattern' (default='*.yaml'). For other options use '--help' option. -The script starts the server monitoring agent, collects all the test cases, executes them and then produces Junit XML and HTML report in artifacts-dir. -Note: It assumes that Model Server is already started. The different JMeter test case parameters such as Model Server Host, Port, image path are specified in test yamls. Modify as per your setup. -In future a global config file will be provided for commonly used parameters. + +The script starts the server monitoring agent, collects all the test cases, executes them and then produces Junit XML and HTML report in artifacts-dir. + +**Note**: The script assumes that Model Server is already started. The different JMeter test case parameters such as Model Server Host, Port, image path are specified in test yamls. Modify as per your setup. +In future, a global config file will be provided for commonly used parameters. ```bash From f31b37a37da98ce7b9822de8c8e053c8622d4958 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 14:00:24 +0530 Subject: [PATCH 048/329] read me fixes --- tests/performance/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/performance/README.md b/tests/performance/README.md index e3e292a6d..4d14add55 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -12,7 +12,16 @@ The script starts the server monitoring agent, collects all the test cases, exec **Note**: The script assumes that Model Server is already started. The different JMeter test case parameters such as Model Server Host, Port, image path are specified in test yamls. Modify as per your setup. In future, a global config file will be provided for commonly used parameters. +```bash +pip install -r requirements.txt +``` +To install monitoring server dependencies, use the following command. +```bash +pip install -r requirements.txt +``` + +To run the test suite. ```bash python run_perfomance_suite.py --artifacts-dir='' ``` @@ -126,6 +135,8 @@ Metrics can be monitored in two ways: you have to start a [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will be communicating with Taurus test client over sockets. The address and port(default=9009) of the monitoring script should be specified in test case yaml. + **Note**: For available metrics check AVAILABLE_METRICS in the script [metric/__init__.py](metrics/__init__.py). + To install monitoring server dependencies, use the following command ```bash pip install -r requirements.txt From addc87d353680a0e3032712c4b9737e9db571afa Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 16:31:39 +0530 Subject: [PATCH 049/329] refinements --- tests/performance/README.md | 39 +++++++++--------- tests/performance/kitten.jpg | Bin 0 -> 110969 bytes tests/performance/requirements.txt | 2 +- tests/performance/run_perfomance_suite.py | 7 ++-- .../tests/common/global_config.yaml | 26 ++++++++++++ .../tests/inference_server_monitoring.yaml | 20 +-------- .../inference_server_monitoring_criteria.yaml | 18 +------- .../inference_taurus_local_monitoring.yaml | 20 +-------- ...ence_taurus_local_monitoring_criteria.yaml | 18 +------- 9 files changed, 53 insertions(+), 97 deletions(-) create mode 100644 tests/performance/kitten.jpg create mode 100644 tests/performance/tests/common/global_config.yaml diff --git a/tests/performance/README.md b/tests/performance/README.md index 4d14add55..ff84165ed 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -9,17 +9,14 @@ You can specify test cases to be run by providing 'test-dir' (default='monitorin The script starts the server monitoring agent, collects all the test cases, executes them and then produces Junit XML and HTML report in artifacts-dir. -**Note**: The script assumes that Model Server is already started. The different JMeter test case parameters such as Model Server Host, Port, image path are specified in test yamls. Modify as per your setup. -In future, a global config file will be provided for commonly used parameters. - -```bash -pip install -r requirements.txt -``` - -To install monitoring server dependencies, use the following command. -```bash -pip install -r requirements.txt -``` +#### Check below things before running the test suite +1. Multi Model Server is running +2. Check [global_config.yaml](tests/common/global_config.yaml) for different parameters and set them as per your environment. Ideally you need to not to change anything as all default values are being used. +3. Make sure all the pip dependencies are installed. + ```bash + pip install -r requirements.txt + ``` +4. This guide assumes you are running scripts from this directory. To run the test suite. ```bash @@ -57,7 +54,7 @@ There are multiple ways you can specify your test scenario. To get you started quickly, we have provided a sample JMeter script and a Taurus yaml file [here](tests/register_and_inference.jmx) and [here](tests/call_jmx.yaml) . - Here is how the call_jmx.yaml looks like. Adjust the module/jmeter/properties section as per your environment. + Here is how the sample call_jmx.yaml looks like. Adjust the module/jmeter/properties section as per your environment. ```yaml execution: @@ -82,13 +79,13 @@ There are multiple ways you can specify your test scenario. Use Taurus command below to run the test case: ```bash - bzt call_jmx.yaml + bzt call_jmx.yaml tests/common/global_config.yaml ``` 2. Write a Taurus script You can also write a Taurus script for a specific executor type. - For quick reference, we have provided a Taurus script with JMeter executor [here](tests/inference.yaml). + For quick reference, we have provided a Taurus script with JMeter executor. Below is the yaml. @@ -122,7 +119,7 @@ There are multiple ways you can specify your test scenario. Use command Taurus command below to run the test yaml. Note this test script assumes squeezenet model is already registered. ```bash - bzt inference.yaml + bzt inference.yaml ``` #### 2. Metrics to monitor @@ -169,7 +166,7 @@ Metrics can be monitored in two ways: Use command Taurus command below to run the test yaml and observe the Metrics widget on CLI live report. ```bash - bzt inference_server_monitoring.yaml + bzt inference_server_monitoring.yaml tests/common/global_config.yaml ``` @@ -177,9 +174,11 @@ Metrics can be monitored in two ways: If your test client is running on the server itself, you may want to use this method. We have provided a custom Taurus plugin as [metrics_monitoring_taurus.py](metrics_monitoring_taurus.py). Make sure that the benchmarks/monitoring folder - is in PYTHONPATH. You need to specify the monitoring class. + is in PYTHONPATH. You need to specify the monitoring class. + + **Note**: To know the list of supported/available metrics check [here](metrics/__init__.py) - Use command below to add update PYTHONPATH. + Use commands below to add update PYTHONPATH. ```bash export MMS_HOME= @@ -229,6 +228,6 @@ Test yamls can be found [here](tests/inference_server_monitoring_criteria.yaml) Use command below to run the test case ```bash -bzt inference_server_monitoring_criteria.yaml -bzt inference_taurus_local_monitoring_criteria.yaml +bzt inference_server_monitoring_criteria.yaml tests/common/global_config.yaml +bzt inference_taurus_local_monitoring_criteria.yaml tests/common/global_config.yaml ``` diff --git a/tests/performance/kitten.jpg b/tests/performance/kitten.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ffcd2be2c674fddc4886117c12f3b29e4367aba8 GIT binary patch literal 110969 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

J66l>z7T20v&BrMUK^shcTlD$)2!& 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

hPAkbbTr(^R-tMK2 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%0DL;n=L?~l?ng&I8MPm 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 z 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%0D>0jKMROl@?0Zb`>KdeJYDW6roP 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 NZ`_ZLr78E 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 Date: Fri, 12 Jun 2020 16:38:48 +0530 Subject: [PATCH 050/329] chnages --- tests/performance/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index ff84165ed..1ebb32217 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -4,12 +4,12 @@ This test suite helps in running the load tests and monitoring the process, sub- We use Taurus test automation framework to run the test cases and metrics monitoring. ## How to run the test suite -To run the test suite just invoke the [run_perfomance_suite.py](run_perfomance_suite.py). You will have to provide the artifacts-dir path to store the test case results. +To run the test suite you need to execute the [run_perfomance_suite.py](run_perfomance_suite.py). You will have to provide the artifacts-dir path to store the test case results. You can specify test cases to be run by providing 'test-dir' (default='monitoring/tests') and 'pattern' (default='*.yaml'). For other options use '--help' option. The script starts the server monitoring agent, collects all the test cases, executes them and then produces Junit XML and HTML report in artifacts-dir. -#### Check below things before running the test suite +#### Ensure below things before running the test suite: 1. Multi Model Server is running 2. Check [global_config.yaml](tests/common/global_config.yaml) for different parameters and set them as per your environment. Ideally you need to not to change anything as all default values are being used. 3. Make sure all the pip dependencies are installed. @@ -18,12 +18,13 @@ The script starts the server monitoring agent, collects all the test cases, exec ``` 4. This guide assumes you are running scripts from this directory. -To run the test suite. +**Finally To run the test suite.** ```bash python run_perfomance_suite.py --artifacts-dir='' ``` -#### To know more about the Test Suite follow the guide below: + +## To know more about the Test Suite and to add test cases follow the guide below: ## Taurus @@ -231,3 +232,4 @@ Use command below to run the test case bzt inference_server_monitoring_criteria.yaml tests/common/global_config.yaml bzt inference_taurus_local_monitoring_criteria.yaml tests/common/global_config.yaml ``` + From 4aea8e4103be9e9c58aef048a3b9cc7435aba9f7 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 16:49:59 +0530 Subject: [PATCH 051/329] add taurus --- tests/performance/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/performance/requirements.txt b/tests/performance/requirements.txt index 2b216ad94..55b3d1409 100644 --- a/tests/performance/requirements.txt +++ b/tests/performance/requirements.txt @@ -1,3 +1,4 @@ +taurus gevent==20.5.2 junitparser==1.4.1 git+https://github.com/maheshambule/vjunit.git#egg=vjunit From a7207337ee5cafe0709cd91d430238e97635ea15 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 16:53:38 +0530 Subject: [PATCH 052/329] add taurus --- tests/performance/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/performance/requirements.txt b/tests/performance/requirements.txt index 55b3d1409..4a45d691e 100644 --- a/tests/performance/requirements.txt +++ b/tests/performance/requirements.txt @@ -1,4 +1,5 @@ taurus +bzt gevent==20.5.2 junitparser==1.4.1 git+https://github.com/maheshambule/vjunit.git#egg=vjunit From c139ae42f70427c52645ed4fd24dad0a15fb4655 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 12 Jun 2020 16:59:40 +0530 Subject: [PATCH 053/329] Updated Readme, local exec scripts, image SHA --- .circleci/README.md | 24 +++++++++++++++----- .circleci/config.yml | 2 +- run_circleci_tests.py | 51 +++++++++++++++++++++++++++++++------------ 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index beba9a7d8..a838897fd 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -4,8 +4,12 @@ Model Server uses CircleCI for builds. This folder contains the config and scrip ## config.yml _config.yml_ contains MMS build logic which will be used by CircleCI. -## Jobs -Currently following jobs are executed under "_**build_and_test***_" workflow +## Workflows and Jobs +Currently, following _workflows_ are available - +1. **build_and_test_python27** +2. **build_and_test_python36** + +Following _jobs_ are executed under each workflow 1. **build** - Builds _frontend/model-server.jar_ and executes tests from gradle 2. **modelarchiver** - Builds and tests modelarchiver module 3. **python_tests** - Executes pytests from _mms/tests/unit_tests/_ @@ -13,6 +17,8 @@ Currently following jobs are executed under "_**build_and_test***_" workflow 5. (NEW!) **api_tests** - Executes newman test suite for API testing 6. (NEW!) **performance_regression** - Executes performance tests using taurus/jmeter +> Checkout _workflows_ and _jobs_ section in _config.yml_ for an up to date list + ## scripts Instead of using inline commands inside _config.yml_, job steps are configured as shell scripts. This is easier for maintenance and reduces chances of error in config.yml @@ -32,16 +38,24 @@ Following files in the _images_ folder are used to create the docker images To make it easy for developers to debug build issues locally, MMS supports CircleCI cli for running a job in a container on your machine. #### Dependencies -1. CircleCI cli from - https://circleci.com/docs/2.0/local-cli/#installation +1. CircleCI cli [Quick Install](https://circleci.com/docs/2.0/local-cli/#quick-installation) 2. PyYAML (pip install PyYaml) -3. docker +3. docker (installed and running) #### Command Developers can use the following command to build MMS locally: **_./run_circleci_tests.py _** ```bash $ cd multi-model-server -$ ./run_circleci_tests.py build_and_test python_tests +$ ./run_circleci_tests.py build_and_test_python27 python_tests +$ ./run_circleci_tests.py build_and_test_python36 api_tests ``` +###### Checklist +> 1. Make sure docker is running before you start local execution. +> 2. Docker containers to have **at least 4GB RAM, 2 CPU**. +> 3. If you are on a network with low bandwidth availability, pull the images beforehand using - +> docker pull prashantsail/mms-build:python3.6@sha256:ea8ad58cc1740587f062d3b149175563e783d420d61ff242f9280a2550457853 +> docker pull prashantsail/mms-build:python2.7@sha256:9d18782939652917bbcc8b0181639529f582560dd42589a965e59708e9fd6306 + > To avoid Pull Request build failures on github, developers should always make sure that their local builds pass. diff --git a/.circleci/config.yml b/.circleci/config.yml index fd46ce409..94e2143ad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ executors: docker-python-2: docker: - - image: prashantsail/mms-build:python2.7@sha256:ea8ad58cc1740587f062d3b149175563e783d420d61ff242f9280a2550457853 + - image: prashantsail/mms-build:python2.7@sha256:9d18782939652917bbcc8b0181639529f582560dd42589a965e59708e9fd6306 environment: _JAVA_OPTIONS: "-Xmx2048m" diff --git a/run_circleci_tests.py b/run_circleci_tests.py index d623b0e7f..8a5b18a62 100755 --- a/run_circleci_tests.py +++ b/run_circleci_tests.py @@ -20,7 +20,8 @@ workflow = args.workflow job = args.job -circleci_config_file = '.circleci/config.yml' +cci_config_file = '.circleci/config.yml' +sw_cci_config_file = '.circleci/sw_config.yml' processed_file = '.circleci/processed.yml' xformed_file = '.circleci/xformed.yml' processed_config = {} @@ -28,8 +29,26 @@ xformed_job_name = 'mms_xformed_job' blacklisted_steps = ['persist_to_workspace', 'attach_workspace', 'store_artifacts'] +# Generate a new CircleCI config which only has ONLY one workflow (workflow name from cli args) +# WHY ? +# > In case of multiple workflows (py27, py36, etc), CircleCI updates the job names(myjob -> myjob-1) while creating +# > processed.yml. This causes mismatch between 'job name from cli args' and 'job name in processed yaml' +# > As a workaround, we pre-process config to only have 1 workflow. This ensures unchanged job names in process.yml +with open(cci_config_file) as f1stream: + try: + config = yaml.safe_load(f1stream) + single_workflow_config = { + 'version': config['workflows']['version'], + workflow: config['workflows'][workflow] + } + config['workflows'] = single_workflow_config + with open(sw_cci_config_file, 'w+') as f2stream: + yaml.dump(config, f2stream) + except yaml.YAMLError as err: + print(err) + # Create processed YAML using circleci cli's 'config process' commands -subprocess.check_call('circleci config process {} > {}'.format(circleci_config_file, processed_file), shell=True) +subprocess.check_call('circleci config process {} > {}'.format(sw_cci_config_file, processed_file), shell=True) # Read the processed config with open(processed_file) as fstream: @@ -40,31 +59,35 @@ jobs_in_workflow = processed_config['workflows'][workflow]['jobs'] + # Recursively iterate over jobs in the workflow to generate an ordered list of parent jobs -def getJobsToExec(job_name): +def get_jobs_to_exec(job_name): result = [] for job in jobs_in_workflow : - if(isinstance(job, str) and job_name == job): + if isinstance(job, str) and job_name == job: # We have reached a root job (outter most parent), no further traversal break - elif(isinstance(job, dict) and job_name == list(job)[0]): + elif isinstance(job, dict) and job_name == list(job)[0]: # Find all parent jpbs, recurse to find their respective ancestors parent_jobs = job[job_name].get('requires',[]) - for pjob in parent_jobs : - result += getJobsToExec(pjob) + for pjob in parent_jobs: + result += get_jobs_to_exec(pjob) break - return (result + [job_name]) + return result + [job_name] + + +# The list might contain duplicate parent jobs due to multiple fan-ins like config; remove the duplicates +jobs_to_exec = list(OrderedDict.fromkeys(get_jobs_to_exec(job))) -# The list might contain duplicate parent jobs due to muliple fan-ins like config; remove the duplicates -jobs_to_exec = list(OrderedDict.fromkeys(getJobsToExec(job))) # Merge all the steps from jobs to be executed -def getJobSteps(result, job_name): +def get_jobs_steps(result, job_name): job_steps = processed_config['jobs'][job_name]['steps'] filtered_job_steps = list(filter(lambda step: list(step)[0] not in blacklisted_steps, job_steps)) return result + filtered_job_steps -merged_steps = reduce(getJobSteps, jobs_to_exec, []) + +merged_steps = reduce(get_jobs_steps, jobs_to_exec, []) # Create a new job, using the first job as a reference # This ensures configs like executor, environment, etc are maintained as it is from the first job @@ -81,11 +104,11 @@ def getJobSteps(result, job_name): # Create a transformed yaml with open(xformed_file, 'w+') as fstream: - yaml.dump(xformed_config,fstream) + yaml.dump(xformed_config, fstream) # Locally execute the newly created job # This job has all the steps(ordered and merged) from the mentioned job along with it's ancestors subprocess.check_call('circleci local execute -c {} --job {}'.format(xformed_file, xformed_job_name), shell=True) # Clean up, remove the processed and transformed yml files -subprocess.check_call('rm {} {}'.format(processed_file, xformed_file), shell=True) +subprocess.check_call('rm {} {} {}'.format(sw_cci_config_file, processed_file, xformed_file), shell=True) From 8241daf1378a1dc4dd1e0e37e2ed0b7abdcbe3ef Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 12 Jun 2020 20:44:34 +0530 Subject: [PATCH 054/329] python 3.6 for local install for bzt --- .circleci/config.yml | 3 --- .circleci/images/Dockerfile.python2.7 | 14 +++++++++++++- .circleci/scripts/linux_test_taurus.sh | 9 +++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 94e2143ad..5cb067af6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -126,9 +126,6 @@ jobs: steps: - attach-mms-workspace - install-mms-server - - run: - name: Install Taurus - command: pip install bzt - run: name: Start MMS, Execute performance regression testcases, Stop MMS command: .circleci/scripts/linux_test_taurus.sh diff --git a/.circleci/images/Dockerfile.python2.7 b/.circleci/images/Dockerfile.python2.7 index c1d6b1d90..c1d1b5a45 100644 --- a/.circleci/images/Dockerfile.python2.7 +++ b/.circleci/images/Dockerfile.python2.7 @@ -49,4 +49,16 @@ RUN cd /opt \ && wget --no-check-certificate https://jmeter-plugins.org/get/ -O lib/ext/jmeter-plugins-manager-1.4.jar \ && wget http://search.maven.org/remotecontent?filepath=kg/apc/cmdrunner/2.2/cmdrunner-2.2.jar -O lib/cmdrunner-2.2.jar \ && java -cp lib/ext/jmeter-plugins-manager-1.4.jar org.jmeterplugins.repository.PluginManagerCMDInstaller \ - && bin/PluginsManagerCMD.sh install jpgc-synthesis=2.1,jpgc-filterresults=2.1,jpgc-mergeresults=2.1,jpgc-cmd=2.1,jpgc-perfmon=2.1 \ No newline at end of file + && bin/PluginsManagerCMD.sh install jpgc-synthesis=2.1,jpgc-filterresults=2.1,jpgc-mergeresults=2.1,jpgc-cmd=2.1,jpgc-perfmon=2.1 + +# bzt is used for performance regression test suite +# bzt requires python 3.6 runtime. +# Install pyenv to manage both python versions (2.7 and 3.6.5) +# Only the for the performance regression suite, Python 3.6.5 will be local installed using "pyenv local 3.6.5" +# !! MMS server will continue using Python 2.7 !! +RUN curl https://pyenv.run | bash \ + && echo 'export PATH="/root/.pyenv/bin:$PATH"' >> ~/.bashrc \ + && echo 'eval "$(pyenv init -)"' >> ~/.bashrc \ + && echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc \ + && . ~/.bashrc \ + && pyenv install 3.6.5 diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_taurus.sh index 788769089..0bf09bd1e 100755 --- a/.circleci/scripts/linux_test_taurus.sh +++ b/.circleci/scripts/linux_test_taurus.sh @@ -7,6 +7,15 @@ sleep 90 cd performance_regression +# Hack to use python 3.6.5 for bzt installation and execution on a Python 2.7 environment +# ToDo: Update condition to be more specific - check if python 2 is installed by default +if hash pyenv 2>/dev/null; then + pyenv local 3.6.5 +fi + +# Install dependencies +pip install bzt + curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg bzt -o modules.jmeter.path=/opt/apache-jmeter-5.3/bin/jmeter \ -o settings.artifacts-dir=/tmp/mms-performance-regression/ \ From a22a5273e3401a7d9e58ca0a380e66f111761d81 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 12 Jun 2020 21:11:31 +0530 Subject: [PATCH 055/329] config.yml executor without SHA, update readme --- .circleci/README.md | 6 +++--- .circleci/config.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index a838897fd..952dbd90b 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -54,8 +54,8 @@ $ ./run_circleci_tests.py build_and_test_python36 api_tests ###### Checklist > 1. Make sure docker is running before you start local execution. > 2. Docker containers to have **at least 4GB RAM, 2 CPU**. -> 3. If you are on a network with low bandwidth availability, pull the images beforehand using - -> docker pull prashantsail/mms-build:python3.6@sha256:ea8ad58cc1740587f062d3b149175563e783d420d61ff242f9280a2550457853 -> docker pull prashantsail/mms-build:python2.7@sha256:9d18782939652917bbcc8b0181639529f582560dd42589a965e59708e9fd6306 +> 3. If you are on a network with low bandwidth availability, explicitly pull the docker images beforehand using - +> docker pull prashantsail/mms-build:python2.7 +> docker pull prashantsail/mms-build:python3.6 > To avoid Pull Request build failures on github, developers should always make sure that their local builds pass. diff --git a/.circleci/config.yml b/.circleci/config.yml index 5cb067af6..6ada02ff3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,13 +4,13 @@ version: 2.1 executors: docker-python-3: docker: - - image: prashantsail/mms-build:python3.6@sha256:ea8ad58cc1740587f062d3b149175563e783d420d61ff242f9280a2550457853 + - image: prashantsail/mms-build:python3.6 environment: _JAVA_OPTIONS: "-Xmx2048m" docker-python-2: docker: - - image: prashantsail/mms-build:python2.7@sha256:9d18782939652917bbcc8b0181639529f582560dd42589a965e59708e9fd6306 + - image: prashantsail/mms-build:python2.7 environment: _JAVA_OPTIONS: "-Xmx2048m" From 8a139370d42aa32b7a050531bd9524457055a5e7 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 22:18:50 +0530 Subject: [PATCH 056/329] changes --- tests/performance/README.md | 203 ++++++++++------------ tests/performance/requirements.txt | 2 - tests/performance/run_perfomance_suite.py | 37 +++- tests/performance/tests/call_jmx.yaml | 19 -- 4 files changed, 123 insertions(+), 138 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 1ebb32217..e6395b64b 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -1,61 +1,71 @@ # Performance Regression Suite -This test suite helps in running the load tests and monitoring the process, sub-process and system wide metrics. It allows to specify the pass/fail criteria for metrics in the test case. -We use Taurus test automation framework to run the test cases and metrics monitoring. +This test suite helps in running the load tests and monitoring the process and system wide metrics. It allows to specify the pass/fail criteria for metrics in the test case. +We use Taurus with JMeter as a test automation framework to run the test cases and metrics monitoring. ## How to run the test suite To run the test suite you need to execute the [run_perfomance_suite.py](run_perfomance_suite.py). You will have to provide the artifacts-dir path to store the test case results. -You can specify test cases to be run by providing 'test-dir' (default='monitoring/tests') and 'pattern' (default='*.yaml'). For other options use '--help' option. +You can specify test cases to be run by providing 'test-dir' (default='$MMS_HOME/tests/performance/tests') and 'pattern' (default='\*criteria\*.yaml'). For other options use '--help' option. -The script starts the server monitoring agent, collects all the test cases, executes them and then produces Junit XML and HTML report in artifacts-dir. +Script does the following: +1. Optionally but by default start the metrics monitoring server +2. Collect all the test yamls from test-dir satisfying the pattern +3. Execute test yamls +4. Generate Junit XML and HTML report in artifacts-dir. -#### Ensure below things before running the test suite: -1. Multi Model Server is running -2. Check [global_config.yaml](tests/common/global_config.yaml) for different parameters and set them as per your environment. Ideally you need to not to change anything as all default values are being used. -3. Make sure all the pip dependencies are installed. +## Installation Prerequisites +1. Install Taurus. While Taurus needs Python3, you can run test suite in Python3 environment on same or different machine while your MMS instance is running on +Python2 or Python3 on same or different machine. ```bash - pip install -r requirements.txt - ``` -4. This guide assumes you are running scripts from this directory. - -**Finally To run the test suite.** + pip install bzt # Needs python3.6+ + ``` +2. Install other dependencies. + ```bash + export MMS_HOME= + pip install -r $MMS_HOME/tests/performance/requirements.txt + ``` + +## Running the test suite +1. Run MMS server +2. Make sure parameters set in the [global_config.yaml](tests/common/global_config.yaml) are correct. +3. Run the test suite runner script +4. Check the console logs, /junit.html report and other artifacts. + +**steps are provided below** ```bash -python run_perfomance_suite.py --artifacts-dir='' -``` +export MMS_HOME= +cd $MMS_HOME/tests/performance +# assumes multi-model-server is in path +multi-model-server --start -## To know more about the Test Suite and to add test cases follow the guide below: +# check variables +#vi tests/common/global_config.yaml +# jpeg download command for quick reference. Set input_filepath in global_config.yaml +#curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg -## Taurus +python run_perfomance_suite.py --artifacts-dir='' +``` -[Taurus](https://gettaurus.org) is Apache 2.0 Licenced, test automation framework which extends and abstracts -different popular load testing frameworks such as JMeter, Locust etc. Taurus simplifies creating, running and analyzing the -performance tests. +## Understanding the test suite artifacts and reports +1. The /junit.html contains the summary report of the test run. Note that each test yaml is treated as a +test suite. Different criteria in the yaml are treated as test cases. If criteria is not specified in the yaml, test suite is marked as skipped with 0 test cases. +2. For each test yaml a sub-directory is created with artifacts for it. -## Installation -Refer the Taurus installation guide [here](https://gettaurus.org/install/Installation/). +## How to add test case to test suite. -## How to create and run tests -To run a metrics monitoring test case, you need to specify below in Taurus test case yaml. -1. Load test case scenario -2. Metrics to monitor -3. Pass/fail criteria +To add test case follow steps below. +1. Add scenario +2. Add metrics to monitor +3. Add pass/fail criteria -#### 1. Load test case scenario +#### 1. Add scenario You can specify the test scenarios, in the scenario section of the yaml. - -There are multiple ways you can specify your test scenario. -1. Use existing JMeter/locust test script - - Taurus provides support for different executors such as JMeter. You can use test script written in those frameworks as it is. - Details about executor types are provided [here](https://gettaurus.org/docs/ExecutionSettings/). - Details about how to run an existing JMeter script are provided [here](https://gettaurus.org/docs/JMeter/). +To get you started quickly, we have provided a sample JMeter script and a Taurus yaml file [here](tests/register_and_inference.jmx) and [here](tests/call_jmx.yaml) . - To get you started quickly, we have provided a sample JMeter script and a Taurus yaml file [here](tests/register_and_inference.jmx) and [here](tests/call_jmx.yaml) . - - Here is how the sample call_jmx.yaml looks like. Adjust the module/jmeter/properties section as per your environment. +Here is how the sample call_jmx.yaml looks like. Note variables used by jmx script are specified in global_config.yaml file. ```yaml execution: @@ -63,69 +73,29 @@ There are multiple ways you can specify your test scenario. ramp-up: 1s hold-for: 40s scenario: Inference + scenarios: Inference: script: register_and_inference.jmx - modules: - jmeter: - properties: - hostname : 127.0.0.1 - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg ``` - Use Taurus command below to run the test case: +To run this individual test using Taurus(bzt) run commands below: ```bash - bzt call_jmx.yaml tests/common/global_config.yaml + export MMS_HOME= + cd $MMS_HOME/tests/performance + bzt tests/call_jmx.yaml tests/common/global_config.yaml ``` -2. Write a Taurus script - - You can also write a Taurus script for a specific executor type. - For quick reference, we have provided a Taurus script with JMeter executor. - - Below is the yaml. - - ```yaml - execution: - - concurrency: 4 - ramp-up: 1s - hold-for: 20s - scenario: Inference - scenarios: - Inference: - requests: - - follow-redirects: true - label: Inference Request - method: POST - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - model: ${__P(model_name,squeezenet_v1.1)} - - modules: - jmeter: - properties: - input_filepath : kitten.jpg - model_name : squeezenet - - ``` +**Note**: +Taurus provides support for different executors such as JMeter. You can use test script written in those frameworks as it is. +Details about executor types are provided [here](https://gettaurus.org/docs/ExecutionSettings/). +Details about how to run an existing JMeter script are provided [here](https://gettaurus.org/docs/JMeter/). - Use command Taurus command below to run the test yaml. Note this test script assumes squeezenet model is already registered. - - ```bash - bzt inference.yaml - ``` -#### 2. Metrics to monitor +#### 2. Add metrics to monitor You can specify the different metrics to monitor in services/monitoring section of the yaml. - Metrics can be monitored in two ways: 1. Standalone monitoring server @@ -134,19 +104,16 @@ Metrics can be monitored in two ways: The address and port(default=9009) of the monitoring script should be specified in test case yaml. **Note**: For available metrics check AVAILABLE_METRICS in the script [metric/__init__.py](metrics/__init__.py). - - To install monitoring server dependencies, use the following command - ```bash - pip install -r requirements.txt - ``` + **Note**: While running Test suite runner script, no need to manually start the monitoring server. The scripts optionally but by default starts and stops it in setup and teardown. + + To start monitoring server run commands below: + ```bash + export MMS_HOME= + pip install -r $MMS_HOME/tests/performance/requirements.txt + python $MMS_HOME/tests/performance/metrics_monitoring_server.py + ``` - To start monitoring script on server use command below: - - ```bash - python benchmarks/monitoring/metrics_monitoring_server.py - ``` - - Test yaml with monitoring section config. Complete yaml can be found [here](tests/inference_server_monitoring.yaml) + Sample yaml with monitoring section config. Complete yaml can be found [here](tests/inference_server_monitoring.yaml) ```yaml @@ -164,26 +131,28 @@ Metrics can be monitored in two ways: - server_workers # no of mms workers ``` - Use command Taurus command below to run the test yaml and observe the Metrics widget on CLI live report. + Use Taurus command below to run the test yaml and observe the Metrics widget on CLI live report. ```bash - bzt inference_server_monitoring.yaml tests/common/global_config.yaml + export MMS_HOME= + cd $MMS_HOME/tests/performance + bzt tests/inference_server_monitoring.yaml tests/common/global_config.yaml ``` 2. Taurus local monitoring plugin If your test client is running on the server itself, you may want to use this method. - We have provided a custom Taurus plugin as [metrics_monitoring_taurus.py](metrics_monitoring_taurus.py). Make sure that the benchmarks/monitoring folder - is in PYTHONPATH. You need to specify the monitoring class. + We have provided a custom Taurus plugin as [metrics_monitoring_taurus.py](metrics_monitoring_taurus.py). - **Note**: To know the list of supported/available metrics check [here](metrics/__init__.py) + **Note**: To know the list of supported/available metrics check [here](metrics_monitoring_taurus.py) + **Note**: While running Test suite runner script, no need to manually update the PYTHONPATH. The scripts updates it. - Use commands below to add update PYTHONPATH. + Use commands below to update PYTHONPATH so that plugin gets picked up by Taurus. ```bash export MMS_HOME= - export PYTHONPATH=$MMS_HOME/benchmarks/monitoring:$PYTHONPATH + export PYTHONPATH=$MMS_HOME/tests/performance:$PYTHONPATH ``` Relevant test yaml sections. Test yaml can be found [here](tests/inference_taurus_local_monitoring.yaml) @@ -206,7 +175,15 @@ Metrics can be monitored in two ways: ``` -#### 3. Pass/Fail criteria + Use Taurus command below to run the test yaml and observe the Metrics widget on CLI live report. + + ```bash + export MMS_HOME= + cd $MMS_HOME/tests/performance + bzt tests/inference_taurus_local_monitoring.yaml tests/common/global_config.yaml + ``` + +#### 3. Add pass/fail criteria You can specify the pass/fail criteria for the test cases. Read more about it [here](https://gettaurus.org/docs/PassFail/) @@ -229,7 +206,17 @@ Test yamls can be found [here](tests/inference_server_monitoring_criteria.yaml) Use command below to run the test case ```bash +export MMS_HOME= +cd $MMS_HOME/tests/performance bzt inference_server_monitoring_criteria.yaml tests/common/global_config.yaml bzt inference_taurus_local_monitoring_criteria.yaml tests/common/global_config.yaml ``` + +##Work in Progress +1. Add more metrics for cpu and gpu both. Add documentation around those. +2. Add hooks to add custom metrics. Add a metrics registry. +3. Better reporting and artifact management +4. Enhance framework to add better abstraction to hide Taurus and other scripts. +5. Auto threshold calculation, environment profiles +6. Comparison between runs and environments diff --git a/tests/performance/requirements.txt b/tests/performance/requirements.txt index 4a45d691e..2b216ad94 100644 --- a/tests/performance/requirements.txt +++ b/tests/performance/requirements.txt @@ -1,5 +1,3 @@ -taurus -bzt gevent==20.5.2 junitparser==1.4.1 git+https://github.com/maheshambule/vjunit.git#egg=vjunit diff --git a/tests/performance/run_perfomance_suite.py b/tests/performance/run_perfomance_suite.py index 8677ece08..95c4d7f56 100755 --- a/tests/performance/run_perfomance_suite.py +++ b/tests/performance/run_perfomance_suite.py @@ -24,6 +24,8 @@ import glob import pathlib import subprocess +import yaml +import requests from subprocess import PIPE, STDOUT from tqdm import tqdm from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure @@ -66,7 +68,7 @@ def run_process(cmd, wait=True): else: p = subprocess.Popen(cmd, shell=True) - return 1, '' + return p.returncode, '' def get_test_yamls(dir_path=None, pattern="*.yaml"): @@ -90,14 +92,26 @@ def get_options(artifacts_dir, jmeter_path=None): return options_str -def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): - if os.path.exists(artifacts_dir): - artifacts_dir = "{}_{}".format(artifacts_dir, str(int(time.time()))) +def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_server): path = pathlib.Path(__file__).parent.absolute() - start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) - run_process(start_monitoring_server, wait=False) + + if monitoring_server: + start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) + code, output = run_process(start_monitoring_server, wait=False) + if code: + raise Exception("Issue while staring monitoring server. Exiting..") global_config_file = "{}/tests/common/global_config.yaml".format(path) + with open(global_config_file) as conf_file: + global_config = yaml.load(conf_file) + + server_props = global_config["modules"]["jmeter"] + server_ping_url = "{}://{}:{}/ping".format(server_props["protocol"], server_props["hostname"], server_props["port"]) + try: + requests.get(server_ping_url) + except requests.exceptions.ConnectionError: + raise Exception("Server is not running. Exiting..") + junit_xml = JUnitXml() pre_command = 'export PYTHONPATH={}:$PYTHONPATH; '.format(str(path)) @@ -164,8 +178,9 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): junit_xml.write(junit_xml_path) run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) - stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) - run_process(stop_monitoring_server) + if monitoring_server: + stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) + run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: sys.exit(3) @@ -186,5 +201,9 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): parser.add_argument('-j', '--jmeter-path', nargs=1, type=str, dest='jmeter_path', default=[None], help='JMeter executable bin path') + parser.add_argument('-m', '--monitoring-server', nargs=1, type=bool, dest='monitoring_server', default=[True], + help='Whether to start monitoring server') + args = parser.parse_args() - run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0], args.jmeter_path[0]) + run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0], args.jmeter_path[0], + args.monitoring_server[0]) diff --git a/tests/performance/tests/call_jmx.yaml b/tests/performance/tests/call_jmx.yaml index d1d84f195..0332cc6d9 100644 --- a/tests/performance/tests/call_jmx.yaml +++ b/tests/performance/tests/call_jmx.yaml @@ -7,23 +7,4 @@ execution: scenarios: Inference: script: register_and_inference.jmx -modules: - jmeter: - properties: - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -settings: - env: - BASEDIR : '.' From 8ead18889f24ccbd036aa4aa7e7e9ca78435490e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 12 Jun 2020 22:57:19 +0530 Subject: [PATCH 057/329] [ci skip] set pyenv in PATH --- .circleci/images/Dockerfile.python2.7 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.circleci/images/Dockerfile.python2.7 b/.circleci/images/Dockerfile.python2.7 index c1d1b5a45..37956bbcf 100644 --- a/.circleci/images/Dockerfile.python2.7 +++ b/.circleci/images/Dockerfile.python2.7 @@ -56,9 +56,6 @@ RUN cd /opt \ # Install pyenv to manage both python versions (2.7 and 3.6.5) # Only the for the performance regression suite, Python 3.6.5 will be local installed using "pyenv local 3.6.5" # !! MMS server will continue using Python 2.7 !! +ENV PATH="$HOME/.pyenv/bin:$PATH" RUN curl https://pyenv.run | bash \ - && echo 'export PATH="/root/.pyenv/bin:$PATH"' >> ~/.bashrc \ - && echo 'eval "$(pyenv init -)"' >> ~/.bashrc \ - && echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc \ - && . ~/.bashrc \ && pyenv install 3.6.5 From f0e64ab052f966f89c1f2b2ab0e24ae8c6ae687e Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 23:13:28 +0530 Subject: [PATCH 058/329] changes --- tests/performance/README.md | 34 +++++++++---------- tests/performance/run_perfomance_suite.py | 25 ++++++++------ .../tests/common/global_config.yaml | 2 +- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index e6395b64b..b89af8ed3 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -5,17 +5,17 @@ We use Taurus with JMeter as a test automation framework to run the test cases a ## How to run the test suite To run the test suite you need to execute the [run_perfomance_suite.py](run_perfomance_suite.py). You will have to provide the artifacts-dir path to store the test case results. -You can specify test cases to be run by providing 'test-dir' (default='$MMS_HOME/tests/performance/tests') and 'pattern' (default='\*criteria\*.yaml'). For other options use '--help' option. +You can specify test cases to be run by providing 'test-dir'(default='$MMS_HOME/tests/performance/tests') and 'pattern' (default='*.yaml'). For other options use '--help' option. Script does the following: -1. Optionally but by default start the metrics monitoring server -2. Collect all the test yamls from test-dir satisfying the pattern -3. Execute test yamls -4. Generate Junit XML and HTML report in artifacts-dir. +1. Optionally but by default starts the metrics monitoring server +2. Collects all the test yamls from test-dir satisfying the pattern +3. Executes test yamls +4. Generates Junit XML and HTML report in artifacts-dir. ## Installation Prerequisites -1. Install Taurus. While Taurus needs Python3, you can run test suite in Python3 environment on same or different machine while your MMS instance is running on -Python2 or Python3 on same or different machine. +1. Install Taurus. The Taurus needs Python3 but since your tests and MMS instance can run in different virtual environement or machine, +you can configure system such that tests are running on Python3 and MMS instance can run on Python 2 or 3. ```bash pip install bzt # Needs python3.6+ ``` @@ -29,26 +29,26 @@ Python2 or Python3 on same or different machine. 1. Run MMS server 2. Make sure parameters set in the [global_config.yaml](tests/common/global_config.yaml) are correct. 3. Run the test suite runner script -4. Check the console logs, /junit.html report and other artifacts. +4. Check the console logs, $artifacts-dir$/junit.html report and other artifacts. **steps are provided below** ```bash export MMS_HOME= cd $MMS_HOME/tests/performance -# assumes multi-model-server is in path -multi-model-server --start +# Run the command below in different terminal to start MMS +# multi-model-server --start # check variables #vi tests/common/global_config.yaml # jpeg download command for quick reference. Set input_filepath in global_config.yaml #curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg -python run_perfomance_suite.py --artifacts-dir='' +python run_perfomance_suite.py --artifacts-dir='' --pattern='*criteria*.yaml' ``` ## Understanding the test suite artifacts and reports -1. The /junit.html contains the summary report of the test run. Note that each test yaml is treated as a +1. The $artifacts-dir$/junit.html contains the summary report of the test run. Note that each test yaml is treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not specified in the yaml, test suite is marked as skipped with 0 test cases. 2. For each test yaml a sub-directory is created with artifacts for it. @@ -65,7 +65,7 @@ To add test case follow steps below. You can specify the test scenarios, in the scenario section of the yaml. To get you started quickly, we have provided a sample JMeter script and a Taurus yaml file [here](tests/register_and_inference.jmx) and [here](tests/call_jmx.yaml) . -Here is how the sample call_jmx.yaml looks like. Note variables used by jmx script are specified in global_config.yaml file. +Here is how the sample call_jmx.yaml looks like. Note variables used by jmx script are specified in [global_config.yaml](tests/common/global_config.yaml) file. ```yaml execution: @@ -95,11 +95,11 @@ Details about how to run an existing JMeter script are provided [here](https://g #### 2. Add metrics to monitor -You can specify the different metrics to monitor in services/monitoring section of the yaml. +You can specify different metrics in services/monitoring section of the yaml. Metrics can be monitored in two ways: 1. Standalone monitoring server - If your server is hosted on different machine, you will be using this method. Before running the test case + If your MMS server is hosted on different machine, you will be using this method. Before running the test case you have to start a [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will be communicating with Taurus test client over sockets. The address and port(default=9009) of the monitoring script should be specified in test case yaml. @@ -110,7 +110,7 @@ Metrics can be monitored in two ways: ```bash export MMS_HOME= pip install -r $MMS_HOME/tests/performance/requirements.txt - python $MMS_HOME/tests/performance/metrics_monitoring_server.py + python $MMS_HOME/tests/performance/metrics_monitoring_server.py --start ``` Sample yaml with monitoring section config. Complete yaml can be found [here](tests/inference_server_monitoring.yaml) @@ -213,7 +213,7 @@ bzt inference_taurus_local_monitoring_criteria.yaml tests/common/global_config.y ``` -##Work in Progress +## Work in Progress 1. Add more metrics for cpu and gpu both. Add documentation around those. 2. Add hooks to add custom metrics. Add a metrics registry. 3. Better reporting and artifact management diff --git a/tests/performance/run_perfomance_suite.py b/tests/performance/run_perfomance_suite.py index 95c4d7f56..52d0032f5 100755 --- a/tests/performance/run_perfomance_suite.py +++ b/tests/performance/run_perfomance_suite.py @@ -93,24 +93,27 @@ def get_options(artifacts_dir, jmeter_path=None): def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_server): - path = pathlib.Path(__file__).parent.absolute() + if os.path.exists(artifacts_dir): + raise Exception("Artifacts dir '{}' already exists. Provide different one.".format(artifacts_dir)) - if monitoring_server: - start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) - code, output = run_process(start_monitoring_server, wait=False) - if code: - raise Exception("Issue while staring monitoring server. Exiting..") + path = pathlib.Path(__file__).parent.absolute() global_config_file = "{}/tests/common/global_config.yaml".format(path) with open(global_config_file) as conf_file: - global_config = yaml.load(conf_file) - - server_props = global_config["modules"]["jmeter"] + global_config = yaml.safe_load(conf_file) + server_props = global_config["modules"]["jmeter"]["properties"] server_ping_url = "{}://{}:{}/ping".format(server_props["protocol"], server_props["hostname"], server_props["port"]) try: requests.get(server_ping_url) except requests.exceptions.ConnectionError: - raise Exception("Server is not running. Exiting..") + raise Exception("Server is not running. Pinged url {}. Exiting..".format(server_ping_url)) + + if monitoring_server: + start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) + code, output = run_process(start_monitoring_server, wait=False) + time.sleep(2) + + ## TODO - Add check if server started junit_xml = JUnitXml() pre_command = 'export PYTHONPATH={}:$PYTHONPATH; '.format(str(path)) @@ -179,7 +182,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if monitoring_server: - stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python3 {}/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: diff --git a/tests/performance/tests/common/global_config.yaml b/tests/performance/tests/common/global_config.yaml index d9ded02c8..94731f4a3 100644 --- a/tests/performance/tests/common/global_config.yaml +++ b/tests/performance/tests/common/global_config.yaml @@ -23,4 +23,4 @@ reporting: settings: env: - BASEDIR : '.' \ No newline at end of file + BASEDIR : '.' From 4da6ecc522e9757c822a06866d242e93ec015cf1 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 12 Jun 2020 23:17:41 +0530 Subject: [PATCH 059/329] [ci skip] set pyenv in PATH --- .circleci/images/Dockerfile.python2.7 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.circleci/images/Dockerfile.python2.7 b/.circleci/images/Dockerfile.python2.7 index 37956bbcf..6790a7a4d 100644 --- a/.circleci/images/Dockerfile.python2.7 +++ b/.circleci/images/Dockerfile.python2.7 @@ -56,6 +56,10 @@ RUN cd /opt \ # Install pyenv to manage both python versions (2.7 and 3.6.5) # Only the for the performance regression suite, Python 3.6.5 will be local installed using "pyenv local 3.6.5" # !! MMS server will continue using Python 2.7 !! -ENV PATH="$HOME/.pyenv/bin:$PATH" RUN curl https://pyenv.run | bash \ + && echo 'export PATH="/root/.pyenv/bin:$PATH"' >> ~/.bashrc \ + && echo 'eval "$(pyenv init -)"' >> ~/.bashrc \ + && echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc \ + && . ~/.bashrc \ && pyenv install 3.6.5 +ENV PATH="$HOME/.pyenv/bin:$PATH" From f9d262aa171210e05b8503243dc5e79c2b2273a7 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 23:19:12 +0530 Subject: [PATCH 060/329] readme change --- tests/performance/README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index b89af8ed3..66b0429dc 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -5,7 +5,7 @@ We use Taurus with JMeter as a test automation framework to run the test cases a ## How to run the test suite To run the test suite you need to execute the [run_perfomance_suite.py](run_perfomance_suite.py). You will have to provide the artifacts-dir path to store the test case results. -You can specify test cases to be run by providing 'test-dir'(default='$MMS_HOME/tests/performance/tests') and 'pattern' (default='*.yaml'). For other options use '--help' option. +You can specify test cases to be run by providing 'test-dir' with default as '$MMS_HOME/tests/performance/tests' and 'pattern' with default as '*.yaml'. For other options use '--help' option. Script does the following: 1. Optionally but by default starts the metrics monitoring server @@ -115,8 +115,7 @@ Metrics can be monitored in two ways: Sample yaml with monitoring section config. Complete yaml can be found [here](tests/inference_server_monitoring.yaml) - ```yaml - + ```yaml services: - module: monitoring server-agent: @@ -157,7 +156,7 @@ Metrics can be monitored in two ways: Relevant test yaml sections. Test yaml can be found [here](tests/inference_taurus_local_monitoring.yaml) - ``` + ```yaml modules: server_local_monitoring: # metrics_monitoring_taurus and dependencies should be in python path From 93b102b65d111d3770bc1247eb33c7b252f274d5 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 23:21:20 +0530 Subject: [PATCH 061/329] readme change --- tests/performance/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 66b0429dc..357e804c5 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -13,7 +13,7 @@ Script does the following: 3. Executes test yamls 4. Generates Junit XML and HTML report in artifacts-dir. -## Installation Prerequisites +### Installation Prerequisites 1. Install Taurus. The Taurus needs Python3 but since your tests and MMS instance can run in different virtual environement or machine, you can configure system such that tests are running on Python3 and MMS instance can run on Python 2 or 3. ```bash @@ -25,7 +25,7 @@ you can configure system such that tests are running on Python3 and MMS instance pip install -r $MMS_HOME/tests/performance/requirements.txt ``` -## Running the test suite +### Running the test suite 1. Run MMS server 2. Make sure parameters set in the [global_config.yaml](tests/common/global_config.yaml) are correct. 3. Run the test suite runner script @@ -47,7 +47,7 @@ cd $MMS_HOME/tests/performance python run_perfomance_suite.py --artifacts-dir='' --pattern='*criteria*.yaml' ``` -## Understanding the test suite artifacts and reports +### Understanding the test suite artifacts and reports 1. The $artifacts-dir$/junit.html contains the summary report of the test run. Note that each test yaml is treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not specified in the yaml, test suite is marked as skipped with 0 test cases. 2. For each test yaml a sub-directory is created with artifacts for it. From c2603a97e51dd8cc51cc40ce0ea25eed1a845836 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 23:22:35 +0530 Subject: [PATCH 062/329] readme change --- tests/performance/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 357e804c5..6dcc0fb18 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -13,7 +13,7 @@ Script does the following: 3. Executes test yamls 4. Generates Junit XML and HTML report in artifacts-dir. -### Installation Prerequisites +### a.Installation Prerequisites 1. Install Taurus. The Taurus needs Python3 but since your tests and MMS instance can run in different virtual environement or machine, you can configure system such that tests are running on Python3 and MMS instance can run on Python 2 or 3. ```bash @@ -25,7 +25,7 @@ you can configure system such that tests are running on Python3 and MMS instance pip install -r $MMS_HOME/tests/performance/requirements.txt ``` -### Running the test suite +### b.Running the test suite 1. Run MMS server 2. Make sure parameters set in the [global_config.yaml](tests/common/global_config.yaml) are correct. 3. Run the test suite runner script @@ -47,7 +47,7 @@ cd $MMS_HOME/tests/performance python run_perfomance_suite.py --artifacts-dir='' --pattern='*criteria*.yaml' ``` -### Understanding the test suite artifacts and reports +### c.Understanding the test suite artifacts and reports 1. The $artifacts-dir$/junit.html contains the summary report of the test run. Note that each test yaml is treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not specified in the yaml, test suite is marked as skipped with 0 test cases. 2. For each test yaml a sub-directory is created with artifacts for it. From deb00995ebef86bf45f995ae9e68d932aa3e1c9b Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 23:25:19 +0530 Subject: [PATCH 063/329] readme change --- tests/performance/README.md | 39 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 6dcc0fb18..dd264c0fe 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -13,7 +13,7 @@ Script does the following: 3. Executes test yamls 4. Generates Junit XML and HTML report in artifacts-dir. -### a.Installation Prerequisites +### 1. Installation Prerequisites 1. Install Taurus. The Taurus needs Python3 but since your tests and MMS instance can run in different virtual environement or machine, you can configure system such that tests are running on Python3 and MMS instance can run on Python 2 or 3. ```bash @@ -25,32 +25,33 @@ you can configure system such that tests are running on Python3 and MMS instance pip install -r $MMS_HOME/tests/performance/requirements.txt ``` -### b.Running the test suite +### 2. Running the test suite 1. Run MMS server 2. Make sure parameters set in the [global_config.yaml](tests/common/global_config.yaml) are correct. 3. Run the test suite runner script 4. Check the console logs, $artifacts-dir$/junit.html report and other artifacts. -**steps are provided below** -```bash -export MMS_HOME= -cd $MMS_HOME/tests/performance - -# Run the command below in different terminal to start MMS -# multi-model-server --start - -# check variables -#vi tests/common/global_config.yaml -# jpeg download command for quick reference. Set input_filepath in global_config.yaml -#curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg - -python run_perfomance_suite.py --artifacts-dir='' --pattern='*criteria*.yaml' -``` + **steps are provided below** + ```bash + export MMS_HOME= + cd $MMS_HOME/tests/performance + + # Run the command below in different terminal to start MMS + # multi-model-server --start + + # check variables + #vi tests/common/global_config.yaml + # jpeg download command for quick reference. Set input_filepath in global_config.yaml + #curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg + + python run_perfomance_suite.py --artifacts-dir='' --pattern='*criteria*.yaml' + ``` -### c.Understanding the test suite artifacts and reports +### 3. Understanding the test suite artifacts and reports 1. The $artifacts-dir$/junit.html contains the summary report of the test run. Note that each test yaml is treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not specified in the yaml, test suite is marked as skipped with 0 test cases. -2. For each test yaml a sub-directory is created with artifacts for it. +2. For each test yaml a sub-directory is created with artifacts for it. + ## How to add test case to test suite. From 62a1a324f363e0e522cf6b0bc21a8f1c7f81510a Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 23:26:24 +0530 Subject: [PATCH 064/329] readme change --- tests/performance/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index dd264c0fe..23c2cd7d6 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -13,7 +13,7 @@ Script does the following: 3. Executes test yamls 4. Generates Junit XML and HTML report in artifacts-dir. -### 1. Installation Prerequisites +### A. Installation Prerequisites 1. Install Taurus. The Taurus needs Python3 but since your tests and MMS instance can run in different virtual environement or machine, you can configure system such that tests are running on Python3 and MMS instance can run on Python 2 or 3. ```bash @@ -25,7 +25,7 @@ you can configure system such that tests are running on Python3 and MMS instance pip install -r $MMS_HOME/tests/performance/requirements.txt ``` -### 2. Running the test suite +### B. Running the test suite 1. Run MMS server 2. Make sure parameters set in the [global_config.yaml](tests/common/global_config.yaml) are correct. 3. Run the test suite runner script @@ -47,7 +47,7 @@ you can configure system such that tests are running on Python3 and MMS instance python run_perfomance_suite.py --artifacts-dir='' --pattern='*criteria*.yaml' ``` -### 3. Understanding the test suite artifacts and reports +### C. Understanding the test suite artifacts and reports 1. The $artifacts-dir$/junit.html contains the summary report of the test run. Note that each test yaml is treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not specified in the yaml, test suite is marked as skipped with 0 test cases. 2. For each test yaml a sub-directory is created with artifacts for it. From 439aed6d7a184f85eeb6db19d1d9f0ef5680e831 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 12 Jun 2020 23:35:16 +0530 Subject: [PATCH 065/329] trigger build and use HOME env var --- .circleci/images/Dockerfile.python2.7 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/images/Dockerfile.python2.7 b/.circleci/images/Dockerfile.python2.7 index 6790a7a4d..257771b93 100644 --- a/.circleci/images/Dockerfile.python2.7 +++ b/.circleci/images/Dockerfile.python2.7 @@ -57,7 +57,7 @@ RUN cd /opt \ # Only the for the performance regression suite, Python 3.6.5 will be local installed using "pyenv local 3.6.5" # !! MMS server will continue using Python 2.7 !! RUN curl https://pyenv.run | bash \ - && echo 'export PATH="/root/.pyenv/bin:$PATH"' >> ~/.bashrc \ + && echo 'export PATH="$HOME/.pyenv/bin:$PATH"' >> ~/.bashrc \ && echo 'eval "$(pyenv init -)"' >> ~/.bashrc \ && echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc \ && . ~/.bashrc \ From 2ba24c924b76beb7b41b01e4d7f869241591ef3e Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 12 Jun 2020 23:38:18 +0530 Subject: [PATCH 066/329] taurus link --- tests/performance/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 23c2cd7d6..734325ec4 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -15,7 +15,8 @@ Script does the following: ### A. Installation Prerequisites 1. Install Taurus. The Taurus needs Python3 but since your tests and MMS instance can run in different virtual environement or machine, -you can configure system such that tests are running on Python3 and MMS instance can run on Python 2 or 3. +you can configure system such that tests are running on Python3 and MMS instance can run on Python 2 or 3. +Refer the [link](https://gettaurus.org/docs/Installation/) for more details on installation. ```bash pip install bzt # Needs python3.6+ ``` From 8ca12d65ab680e2762da4601bef66197798c4c88 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Sat, 13 Jun 2020 00:11:05 +0530 Subject: [PATCH 067/329] dir changes --- .circleci/scripts/linux_test_taurus.sh | 6 +- benchmarks/monitoring/README.md | 218 ------------------ benchmarks/monitoring/__init__.py | 15 -- benchmarks/monitoring/config.ini | 6 - benchmarks/monitoring/configuration.py | 26 --- benchmarks/monitoring/metrics/__init__.py | 55 ----- benchmarks/monitoring/metrics_collector.py | 143 ------------ .../monitoring/metrics_monitoring_server.py | 134 ----------- .../monitoring/metrics_monitoring_taurus.py | 87 ------- benchmarks/monitoring/process.py | 71 ------ benchmarks/monitoring/requirements.txt | 5 - benchmarks/monitoring/run_perfomance_suite.py | 186 --------------- benchmarks/monitoring/tests/call_jmx.yaml | 29 --- .../tests/inference_server_monitoring.yaml | 44 ---- .../inference_server_monitoring_criteria.yaml | 56 ----- .../inference_taurus_local_monitoring.yaml | 48 ---- ...ence_taurus_local_monitoring_criteria.yaml | 55 ----- benchmarks/monitoring/tests/kitten.jpg | Bin 110969 -> 0 bytes .../tests/register_and_inference.jmx | 189 --------------- .../imageInputModelPlan.jmx.yaml | 51 ---- 20 files changed, 3 insertions(+), 1421 deletions(-) delete mode 100644 benchmarks/monitoring/README.md delete mode 100644 benchmarks/monitoring/__init__.py delete mode 100644 benchmarks/monitoring/config.ini delete mode 100644 benchmarks/monitoring/configuration.py delete mode 100644 benchmarks/monitoring/metrics/__init__.py delete mode 100755 benchmarks/monitoring/metrics_collector.py delete mode 100644 benchmarks/monitoring/metrics_monitoring_server.py delete mode 100644 benchmarks/monitoring/metrics_monitoring_taurus.py delete mode 100644 benchmarks/monitoring/process.py delete mode 100644 benchmarks/monitoring/requirements.txt delete mode 100755 benchmarks/monitoring/run_perfomance_suite.py delete mode 100644 benchmarks/monitoring/tests/call_jmx.yaml delete mode 100644 benchmarks/monitoring/tests/inference_server_monitoring.yaml delete mode 100644 benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml delete mode 100644 benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml delete mode 100644 benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml delete mode 100644 benchmarks/monitoring/tests/kitten.jpg delete mode 100644 benchmarks/monitoring/tests/register_and_inference.jmx delete mode 100644 performance_regression/imageInputModelPlan.jmx.yaml diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_taurus.sh index 487f36e85..5e9b0fbf6 100755 --- a/.circleci/scripts/linux_test_taurus.sh +++ b/.circleci/scripts/linux_test_taurus.sh @@ -8,8 +8,8 @@ JMETER_PATH='/opt/apache-jmeter-5.3/bin/jmeter' multi-model-server --start >> mms.log 2>&1 sleep 10 -#cd taurus -cd benchmarks/monitoring +#cd test suite +cd tests/performance # Install dependencies pip install -r requirements.txt @@ -27,4 +27,4 @@ mkdir -p $RESULT_DIR cp $ARTIFACTS_DIR'/junit.xml' $RESULT_DIR # Exit with the same error code as that of test execution -exit EXIT_CODE \ No newline at end of file +exit EXIT_CODE diff --git a/benchmarks/monitoring/README.md b/benchmarks/monitoring/README.md deleted file mode 100644 index d57dba8c5..000000000 --- a/benchmarks/monitoring/README.md +++ /dev/null @@ -1,218 +0,0 @@ -# Performance Regression Suite - -This test suite helps in running the load tests and monitoring the process, sub-process and system wide metrics. It allows to specify the pass/fail criteria for metrics in the test case. -We use Taurus test automation framework to run the test cases and metrics monitoring. - -## How to run the test suite -To run the test suite just invoke the [run_perfomance_suite.py](run_perfomance_suite.py). You will have to provide the artifacts-dir path to store the test case results. -You can specify test cases to be run by providing 'test-dir' (default='monitoring/tests') and 'pattern' (default='*.yaml'). For other options use '--help' option. -The script starts the server monitoring agent, collects all the test cases, executes them and then produces Junit XML and HTML report in artifacts-dir. - -```bash -python run_perfomance_suite.py --artifacts-dir='' -``` - -#### To know more about the Test Suite follow the guide below: - -## Taurus - -[Taurus](https://gettaurus.org) is Apache 2.0 Licenced, test automation framework which extends and abstracts -different popular load testing frameworks such as JMeter, Locust etc. Taurus simplifies creating, running and analyzing the -performance tests. - -## Installation -Refer the Taurus installation guide [here](https://gettaurus.org/install/Installation/). - - -## How to create and run tests -To run a metrics monitoring test case, you need to specify below in Taurus test case yaml. -1. Load test case scenario -2. Metrics to monitor -3. Pass/fail criteria - - -#### 1. Load test case scenario -You can specify the test scenarios, in the scenario section of the yaml. - -There are multiple ways you can specify your test scenario. -1. Use existing JMeter/locust test script - - Taurus provides support for different executors such as JMeter. You can use test script written in those frameworks as it is. - Details about executor types are provided [here](https://gettaurus.org/docs/ExecutionSettings/). - Details about how to run an existing JMeter script are provided [here](https://gettaurus.org/docs/JMeter/). - - To get you started quickly, we have provided a sample JMeter script and a Taurus yaml file [here](tests/register_and_inference.jmx) and [here](tests/call_jmx.yaml) . - - Here is how the call_jmx.yaml looks like. Adjust the module/jmeter/properties section as per your environment. - - ```yaml - execution: - - concurrency: 1 - ramp-up: 1s - hold-for: 40s - scenario: Inference - scenarios: - Inference: - script: register_and_inference.jmx - modules: - jmeter: - properties: - hostname : 127.0.0.1 - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg - - ``` - - Use Taurus command below to run the test case: - - ```bash - bzt call_jmx.yaml - ``` - -2. Write a Taurus script - - You can also write a Taurus script for a specific executor type. - For quick reference, we have provided a Taurus script with JMeter executor [here](tests/inference.yaml). - - Below is the yaml. - - ```yaml - execution: - - concurrency: 4 - ramp-up: 1s - hold-for: 20s - scenario: Inference - scenarios: - Inference: - requests: - - follow-redirects: true - label: Inference Request - method: POST - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - model: ${__P(model_name,squeezenet_v1.1)} - - modules: - jmeter: - properties: - input_filepath : kitten.jpg - model_name : squeezenet - - ``` - - Use command Taurus command below to run the test yaml. Note this test script assumes squeezenet model is already registered. - - ```bash - bzt inference.yaml - ``` - -#### 2. Metrics to monitor -You can specify the different metrics to monitor in services/monitoring section of the yaml. - -Metrics can be monitored in two ways: -1. Standalone monitoring server - - If your server is hosted on different machine, you will be using this method. Before running the test case - you have to start a [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will be communicating with Taurus test client over sockets. - The address and port(default=9009) of the monitoring script should be specified in test case yaml. - - To install monitoring server dependencies, use the following command - ```bash - pip install -r requirements.txt - ``` - - To start monitoring script on server use command below: - - ```bash - python benchmarks/monitoring/metrics_monitoring_server.py - ``` - - Test yaml with monitoring section config. Complete yaml can be found [here](tests/inference_server_monitoring.yaml) - - ```yaml - - services: - - module: monitoring - server-agent: - - address: localhost:9009 # metric monitoring service address - label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port - interval: 1s # polling interval - logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir - metrics: # metrics should be supported by monitoring service - - sum_cpu_percent # cpu percent used by all the mms server processes and workers - - sum_memory_percent - - sum_num_handles - - server_workers # no of mms workers - ``` - - Use command Taurus command below to run the test yaml and observe the Metrics widget on CLI live report. - - ```bash - bzt inference_server_monitoring.yaml - ``` - - -2. Taurus local monitoring plugin - - If your test client is running on the server itself, you may want to use this method. - We have provided a custom Taurus plugin as [metrics_monitoring_taurus.py](metrics_monitoring_taurus.py). Make sure that the benchmarks/monitoring folder - is in PYTHONPATH. You need to specify the monitoring class. - - Use command below to add update PYTHONPATH. - - ```bash - export MMS_HOME= - export PYTHONPATH=$MMS_HOME/benchmarks/monitoring:$PYTHONPATH - ``` - - Relevant test yaml sections. Test yaml can be found [here](tests/inference_taurus_local_monitoring.yaml) - - ``` - modules: - server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. - - services: - - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - sum_memory_percent - - ``` - -#### 3. Pass/Fail criteria -You can specify the pass/fail criteria for the test cases. -Read more about it [here](https://gettaurus.org/docs/PassFail/) - -Relevant test yaml section: -```yaml -reporting: -- module: passfail - criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - subject: mms-inference-server/sum_num_handles - condition: '>' - threshold: 180 - timeframe: 1s - fail: true - stop: true - -``` - -Test yamls can be found [here](tests/inference_server_monitoring_criteria.yaml) and [here](tests/inference_taurus_local_monitoring_criteria.yaml). -Use command below to run the test case - -```bash -bzt inference_server_monitoring_criteria.yaml -bzt inference_taurus_local_monitoring_criteria.yaml -``` diff --git a/benchmarks/monitoring/__init__.py b/benchmarks/monitoring/__init__.py deleted file mode 100644 index b451f88e4..000000000 --- a/benchmarks/monitoring/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -Metrics Monitoring suite -""" \ No newline at end of file diff --git a/benchmarks/monitoring/config.ini b/benchmarks/monitoring/config.ini deleted file mode 100644 index 27bc4c37b..000000000 --- a/benchmarks/monitoring/config.ini +++ /dev/null @@ -1,6 +0,0 @@ -[server] -pid_file = model_server.pid - -[monitoring] -HOST = -PORT = 9009 \ No newline at end of file diff --git a/benchmarks/monitoring/configuration.py b/benchmarks/monitoring/configuration.py deleted file mode 100644 index e5ebeb998..000000000 --- a/benchmarks/monitoring/configuration.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -Read configuration file -""" -# pylint: disable=redefined-builtin -import configparser - -config = configparser.ConfigParser() -config.read('./config.ini') - -def get(section, key, default=''): - try: - return config[section][key] - except: - return default diff --git a/benchmarks/monitoring/metrics/__init__.py b/benchmarks/monitoring/metrics/__init__.py deleted file mode 100644 index 637857180..000000000 --- a/benchmarks/monitoring/metrics/__init__.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -Custom metrics -""" -# pylint: disable=redefined-builtin - -AVAILABLE_METRICS = ["sum_cpu_percent", - "sum_memory_percent", - "sum_num_handles", - "server_workers"] - - -def get_metrics(server_process, child_processes): - """ Get Server processes specific metrics - """ - - # TODO - make this modular may be a diff function for each metric - # TODO - allow users to add new metrics easily - # TODO - make sure available metric list is maintained - - sum_cpu_percent = 0 - sum_memory_percent = 0 - sum_num_handles = 0 - server_workers = 0 - metrics = {} - for process in [server_process] + child_processes: - try: - process.cpu_percent() # to warm-up - except: - pass - else: - cpu_percent = process.cpu_percent() - memory_percent = process.memory_percent() - sum_cpu_percent += cpu_percent - sum_memory_percent += memory_percent - sum_num_handles += process.num_fds() - server_workers += 1 - - metrics["sum_cpu_percent"] = sum_cpu_percent - metrics["sum_memory_percent"] = sum_memory_percent - metrics["sum_num_handles"] = sum_num_handles - metrics["server_workers"] = server_workers - - return metrics \ No newline at end of file diff --git a/benchmarks/monitoring/metrics_collector.py b/benchmarks/monitoring/metrics_collector.py deleted file mode 100755 index a14c94999..000000000 --- a/benchmarks/monitoring/metrics_collector.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -Server metrics collector -""" -# pylint: disable=redefined-builtin - -import os -import sys -import time -import logging -import psutil -import gevent -import argparse -import tempfile - -logger = logging.getLogger(__name__) - -from process import find_procs_by_name, get_process_pid_from_file, get_child_processes, \ - get_server_processes, get_server_pidfile -from metrics import AVAILABLE_METRICS, get_metrics - -TMP_DIR = tempfile.gettempdir() -METRICS_LOG_FILE = "{}/server_metrics_{}.log".format(TMP_DIR, int(time.time())) -METRICS_COLLECTOR_PID_FILE = "{}/.metrics_collector.pid".format(TMP_DIR) - -MONITOR_INTERVAL = 1 - - -def store_pid(pid_file): - """ Store the current process id to pid_file""" - process = psutil.Process() - pid_file = os.path.join(pid_file) - with open(pid_file, "w") as pf: - pf.write(str(process.pid)) - - -def stop_process(pid_file): - """This will stop already running process . - Note at a time only one pid file will be available. - """ - pid = get_process_pid_from_file(pid_file) - if pid: - try: - process = psutil.Process(pid) - if process.is_running(): - logger.info("Process with pid {} is running. Killing it.".format(process.pid)) - process.kill() - except Exception as e: - pass - else: - logger.info("Dead process with pid {} found in '{}'.".format(process.pid, pid_file)) - - logger.info("Removing pid file '{}'.".format(pid_file)) - os.remove(pid_file) - - -def check_is_running(pid_file): - pid = get_process_pid_from_file(pid_file) - if pid: - try: - perf_mon_process = psutil.Process(pid) - except Exception as e: - stop_process(pid_file) - else: - if perf_mon_process.is_running(): - logger.error("Performance monitoring script already running. " - "Stop it using stop option.") - sys.exit() - - -def store_metrics_collector_pid(): - """ Store the process id of metrics collector process""" - store_pid(METRICS_COLLECTOR_PID_FILE) - - -def stop_metrics_collector_process(): - """This will stop already running metrics collector process. - Note at a time only one pid file will be available. - """ - stop_process(METRICS_COLLECTOR_PID_FILE) - - -def monitor_processes(server_process, metrics, interval, socket): - """ Monitor the metrics of server_process and its child processes - """ - while True: - message = [] - collected_metrics = get_metrics(server_process, get_child_processes(server_process)) - for metric in metrics: - message.append(str(collected_metrics.get(metric, 0))) - message = "\t".join(message)+"\n" - - if socket: - socket.send(message.encode("latin-1")) - - # TODO - log metrics to a file METRICS_LOG_FILE if METRICS_LOG_FILE is provided - gevent.sleep(interval) - - -def start_metric_collection(server_process, metrics, interval, socket): - bad_metrics = set(metrics) - set(AVAILABLE_METRICS) - if bad_metrics: - raise Exception("Metrics not available for monitoring {}.".format(bad_metrics)) - - logger.info("Started metric collection for target server processes.....") - thread = gevent.spawn(monitor_processes, server_process, metrics, interval, socket) - gevent.joinall([thread]) - - -def start_metric_collector_process(): - """Spawn a metric collection process and keep on monitoring """ - - check_is_running(METRICS_COLLECTOR_PID_FILE) - store_metrics_collector_pid() - server_pid = get_process_pid_from_file(get_server_pidfile()) - server_process = get_server_processes(server_pid) - start_metric_collection(server_process, AVAILABLE_METRICS, MONITOR_INTERVAL, None) - - -if __name__ == "__main__": - logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) - parser = argparse.ArgumentParser(prog='perf-mon-script', description='System Performance Monitoring') - sub_parse = parser.add_mutually_exclusive_group(required=True) - sub_parse.add_argument('--start', action='store_true', help='Start the perf-mon-script') - sub_parse.add_argument('--stop', action='store_true', help='Stop the perf-mon-script') - - args = parser.parse_args() - - if args.start: - start_metric_collector_process() - elif args.stop: - stop_metrics_collector_process() diff --git a/benchmarks/monitoring/metrics_monitoring_server.py b/benchmarks/monitoring/metrics_monitoring_server.py deleted file mode 100644 index 98fd73155..000000000 --- a/benchmarks/monitoring/metrics_monitoring_server.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -Remote server monitoring script -""" -# pylint: disable=redefined-builtin - -import logging -import sys -from gevent import socket -from gevent import select -from gevent import monkey -import argparse -import tempfile - -monkey.patch_select() -monkey.patch_socket() - -logger = logging.getLogger(__name__) -from metrics_collector import start_metric_collection, stop_process, store_pid, check_is_running -from process import find_procs_by_name, get_process_pid_from_file, \ - get_child_processes, get_server_processes, get_server_pidfile -import configuration - -TMP_DIR = tempfile.gettempdir() -METRICS_MON_SERVER_PID_FILE = "{}/.metrics_monitoring_server.pid".format(TMP_DIR) - -HOST = str(configuration.get('monitoring','HOST')) -PORT = int(configuration.get('monitoring','PORT', 9009)) - - -SOCKET_LIST = [] -RECV_BUFFER = 4096 - -interval = 1 - - -def perf_server(): - server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - server_socket.bind((HOST, PORT)) - server_socket.listen(10) - SOCKET_LIST.append(server_socket) - logger.info("Started metrics monitoring server on port {}".format(PORT)) - - while True: - ready_to_read, ready_to_write, in_error = select.select(SOCKET_LIST, [], [], 0) - - for sock in ready_to_read: - # a new connection request recieved - if sock == server_socket: - sockfd, addr = server_socket.accept() - SOCKET_LIST.append(sockfd) - logger.info("client ({}, {}) connected".format(addr[0], addr[1])) - - # a message from a client, not a new connection - else: - try: - # receiving data from the socket. - data = sock.recv(RECV_BUFFER).decode() - if data: - if data == 'test\n': - send_message(sock, "Yep\n") - elif data == 'exit\n': - close_socket(sock) - elif data.startswith('interval'): - try: - global interval - interval = int(data.split(":")[1][:-1]) - except Exception: - send_message(sock, "In-correct interval data") - elif data.startswith('metrics'): - metrics = data[:-1].split("metrics:")[1].split("\t") - server_pid = get_process_pid_from_file(get_server_pidfile()) - server_process = get_server_processes(server_pid) - start_metric_collection(server_process, metrics, interval, sock) - else: - # TODO - decide what to do here - pass - - else: - # remove the socket that's broken - if sock in SOCKET_LIST: - SOCKET_LIST.remove(sock) - except Exception as e: - logger.warning("Error {}".format(str(e))) - continue - - server_socket.close() - - -def send_message(socket, message): - try: - socket.send(message.encode("latin-1")) - except Exception as e: - logger.warning("Error while sending the message {}. Closing the socket.".format(str(e))) - close_socket(socket) - - -def close_socket(socket): - socket.close() - if socket in SOCKET_LIST: - SOCKET_LIST.remove(socket) - - -if __name__ == "__main__": - logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) - parser = argparse.ArgumentParser(prog='perf-mon-script', description='System Performance Monitoring') - sub_parse = parser.add_mutually_exclusive_group(required=True) - sub_parse.add_argument('--start', action='store_true', help='Start the perf-mon-script') - sub_parse.add_argument('--stop', action='store_true', help='Stop the perf-mon-script') - - args = parser.parse_args() - - if args.start: - check_is_running(METRICS_MON_SERVER_PID_FILE) - store_pid(METRICS_MON_SERVER_PID_FILE) - perf_server() - elif args.stop: - stop_process(METRICS_MON_SERVER_PID_FILE) - - - - diff --git a/benchmarks/monitoring/metrics_monitoring_taurus.py b/benchmarks/monitoring/metrics_monitoring_taurus.py deleted file mode 100644 index ac402c7db..000000000 --- a/benchmarks/monitoring/metrics_monitoring_taurus.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -Taurus Local plugin for server monitoring. -Should be used when server and Taurus are running on same machine. -This file should be placed in Python Path along with monitoring package. -""" -# pylint: disable=redefined-builtin - -import csv -from bzt.modules import monitoring -from bzt import TaurusConfigError -from bzt.utils import dehumanize_time -import sys - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -from metrics import get_metrics, AVAILABLE_METRICS as AVAILABLE_SERVER_METRICS -from process import get_process_pid_from_file, get_server_processes, \ - get_child_processes, get_server_pidfile - - -class Monitor(monitoring.Monitoring): - def __init__(self): - super(Monitor, self).__init__() - self.client_classes.update({'ServerLocalClient': ServerLocalClient}) - - -class ServerLocalClient(monitoring.LocalClient): - AVAILABLE_METRICS = monitoring.LocalClient.AVAILABLE_METRICS + \ - AVAILABLE_SERVER_METRICS - - def __init__(self, parent_log, label, config, engine=None): - - super(ServerLocalClient, self).__init__(parent_log, label, config, engine=engine) - if label: - self.label = label - else: - self.label = 'ServerLocalClient' - - def connect(self): - exc = TaurusConfigError('Metric is required in Local monitoring client') - metric_names = self.config.get('metrics', exc) - - bad_list = set(metric_names) - set(self.AVAILABLE_METRICS) - if bad_list: - self.log.warning('Wrong metrics found: %s', bad_list) - - good_list = set(metric_names) & set(self.AVAILABLE_METRICS) - if not good_list: - raise exc - - self.metrics = list(set(good_list)) - - self.monitor = ServerLocalMonitor(self.log, self.metrics, self.engine) - self.interval = dehumanize_time(self.config.get("interval", self.engine.check_interval)) - - if self.config.get("logging", False): - if not PY3: - self.log.warning("Logging option doesn't work on python2.") - else: - self.logs_file = self.engine.create_artifact("local_monitoring_logs", ".csv") - with open(self.logs_file, "a", newline='') as mon_logs: - logs_writer = csv.writer(mon_logs, delimiter=',') - metrics = ['ts'] + sorted([metric for metric in good_list]) - logs_writer.writerow(metrics) - - -class ServerLocalMonitor(monitoring.LocalMonitor): - - def _calc_resource_stats(self, interval): - result = super()._calc_resource_stats(interval) - server_pid = get_process_pid_from_file(get_server_pidfile()) - server_process = get_server_processes(server_pid) - result.update(get_metrics(server_process, get_child_processes(server_process))) - return result diff --git a/benchmarks/monitoring/process.py b/benchmarks/monitoring/process.py deleted file mode 100644 index c8f84bfba..000000000 --- a/benchmarks/monitoring/process.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -Utility methods for process related information -""" -# pylint: disable=redefined-builtin - -import os -import psutil -import tempfile -import configuration - -def find_procs_by_name(name): - """Return a list of processes matching 'name'.""" - ls = [] - for p in psutil.process_iter(["name", "exe", "cmdline"]): - if name == p.info['name'] or \ - p.info['exe'] and os.path.basename(p.info['exe']) == name or \ - p.info['cmdline'] and p.info['cmdline'][0] == name: - ls.append(p) - - if len(ls) > 1: - raise Exception("Multiple processes found with name {}.".format(name)) - - return ls[0] - - -def get_process_pid_from_file(file_path): - """Get the process pid from pid file. - """ - pid = None - if os.path.isfile(file_path): - with open(file_path, "r") as f: - pid = int(f.readline()) - - return pid - - -def get_child_processes(process): - """Get all running child processes recursively""" - child_processes = [] - for p in process.children(recursive=True): - if p.status() == 'running': - child_processes.append(p) - return child_processes - - -def get_server_processes(server_process_pid): - """ It caches the main server and child processes at module level. - Ensure that you call this process so that MMS process - """ - try: - server_process = psutil.Process(server_process_pid) - except Exception as e: - print("Server process not found. Error: {}".format(str(e))) - raise - return server_process - - -def get_server_pidfile(): - return "{0}/.{1}".format(tempfile.gettempdir(), configuration.get('server', 'pid_file', 'model_server.pid')) diff --git a/benchmarks/monitoring/requirements.txt b/benchmarks/monitoring/requirements.txt deleted file mode 100644 index 2c29825ff..000000000 --- a/benchmarks/monitoring/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -gevent==20.5.2 -junitparser==1.4.1 -vjunit==0.6.1 -tqdm==4.40.0 -pathlib diff --git a/benchmarks/monitoring/run_perfomance_suite.py b/benchmarks/monitoring/run_perfomance_suite.py deleted file mode 100755 index 3f160fd24..000000000 --- a/benchmarks/monitoring/run_perfomance_suite.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -Run Tarus test cases and generate the Junit XML report -""" -# pylint: disable=redefined-builtin - -import os -import sys -import time -import logging -import argparse -import glob -import pathlib -import subprocess -import socket -from tqdm import tqdm -from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure - -logger = logging.getLogger(__name__) -code = 0 - - -class Timer(object): - def __init__(self, description): - self.description = description - - def __enter__(self): - self.start = int(time.time()) - return self - - def __exit__(self, type, value, traceback): - logger.info(f"{self.description}: {self.diff()}s") - - def diff(self): - return int(time.time()) - self.start - - -def run_process(cmd, wait=True): - print("running command : {}".format(cmd)) - - if wait: - try: - output = subprocess.check_output( - cmd, stderr=subprocess.STDOUT, shell=True, timeout=30*60, - universal_newlines=True) - except subprocess.CalledProcessError as exc: - print("Status : FAIL", exc.returncode, exc.output) - return exc.returncode, exc.output - else: - print("Output: \n{}\n".format(output)) - return 0, output - - else: - p = subprocess.Popen(cmd, shell=True) - return 1, '' - - -def get_test_yamls(dir_path=None, pattern="*.yaml"): - if not dir_path: - path = pathlib.Path(__file__).parent.absolute() - dir_path = str(path) + "/tests" - - path_pattern = "{}/{}".format(dir_path, pattern) - return glob.glob(path_pattern) - - -def get_options(artifacts_dir, jmeter_path=None): - options=[] - if jmeter_path: - options.append('-o modules.jmeter.path={}'.format(jmeter_path)) - options.append('-o settings.artifacts-dir={}'.format(artifacts_dir)) - options.append('-o modules.console.disable=true') - options.append('-o settings.env.BASEDIR={}'.format(artifacts_dir)) - options_str = ' '.join(options) - - return options_str - - -def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path): - if os.path.exists(artifacts_dir): - artifacts_dir = "{}_{}".format(artifacts_dir, str(int(time.time()))) - path = pathlib.Path(__file__).parent.absolute() - start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) - run_process(start_monitoring_server, wait=False) - - junit_xml = JUnitXml() - pre_command = 'export PYTHONPATH={}:$PYTHONPATH; '.format(str(path)) - - test_yamls = get_test_yamls(test_dir, pattern) - for test_file in tqdm(test_yamls, desc="Test Suites"): - suite_name = os.path.basename(test_file).rsplit('.', 1)[0] - with Timer("Test suit {} execution time".format(suite_name)) as t: - suit_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) - options_str = get_options(suit_artifacts_dir, jmeter_path) - code, err = run_process("{} bzt {} {} ".format(pre_command, options_str, test_file)) - suite_time = t.diff() - suite_start = t.start - - # Assumes default file name - xunit_file = "{}/xunit.xml".format(suit_artifacts_dir) - - tests, failures, skipped, errors = 0, 0, 0, 0 - err_txt = "" - ts = TestSuite(suite_name) - if os.path.exists(xunit_file): - xml = JUnitXml.fromfile(xunit_file) - for i, suite in enumerate(xml): #tqqdm - for case in suite: - name = "scenario_{}: {}".format(i, case.name) - result = case.result - if isinstance(result, Error): - errors += 1 - err_txt = err - elif isinstance(result, Failure): - failures += 1 - err_txt = err - elif isinstance(result, Skipped): - skipped += 1 - else: - tests +=1 - - tc = TestCase(name) - tc.result = result - # tc.system_err = err_txt[:-4] - ts.add_testcase(tc) - else: - tc = TestCase(suite_name) - if code: - tc.result = Error("Suite run failed", "Error") - # tc.system_err = err[:-4] - else: - tc.result = Skipped() - # tc.system_out = err[:-4] - ts.add_testcase(tc) - - ts.hostname = socket.gethostname() - ts.timestamp = suite_start - ts.time = suite_time - ts.tests = tests - ts.failures = failures - ts.skipped = skipped - ts.errors = errors - ts.update_statistics() - junit_xml.add_testsuite(ts) - - junit_xml.update_statistics() - junit_xml_path = '{}/junit.xml'.format(artifacts_dir) - junit_html_path = '{}/junit.html'.format(artifacts_dir) - junit_xml.write(junit_xml_path) - run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) - - stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) - run_process(stop_monitoring_server) - - if junit_xml.errors or junit_xml.failures or junit_xml.skipped: - sys.exit(3) - - -if __name__ == "__main__": - logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) - parser = argparse.ArgumentParser(prog='run_perfomance_suite.py', description='Performance Test Suite Runner') - parser.add_argument('-a', '--artifacts-dir', nargs=1, type=str, dest='artifacts', required=True, - help='A artifacts directory') - - parser.add_argument('-d', '--test-dir', nargs=1, type=str, dest='test_dir', default=[None], - help='A test dir') - - parser.add_argument('-p', '--pattern', nargs=1, type=str, dest='pattern', default=["*.yaml"], - help='Test case file name pattern. example *.yaml') - - parser.add_argument('-j', '--jmeter-path', nargs=1, type=str, dest='jmeter_path', default=[None], - help='JMeter executable bin path') - - args = parser.parse_args() - run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0], args.jmeter_path[0]) diff --git a/benchmarks/monitoring/tests/call_jmx.yaml b/benchmarks/monitoring/tests/call_jmx.yaml deleted file mode 100644 index 7973ee521..000000000 --- a/benchmarks/monitoring/tests/call_jmx.yaml +++ /dev/null @@ -1,29 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 1s - hold-for: 40s - scenario: Inference -scenarios: - Inference: - script: register_and_inference.jmx -modules: - jmeter: - properties: - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -setting: - env: - BASEDIR : '.' - diff --git a/benchmarks/monitoring/tests/inference_server_monitoring.yaml b/benchmarks/monitoring/tests/inference_server_monitoring.yaml deleted file mode 100644 index d23af46b1..000000000 --- a/benchmarks/monitoring/tests/inference_server_monitoring.yaml +++ /dev/null @@ -1,44 +0,0 @@ - -execution: -- concurrency: 4 - ramp-up: 1s - hold-for: 20s - scenario: Inference -scenarios: - Inference: - script: register_and_inference.jmx - -modules: - jmeter: - properties: - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg - - -services: - - module: monitoring - server-agent: - - address: localhost:9009 # metric monitoring service address - label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port - interval: 1s # polling interval - logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir - metrics: # metrics should be supported by monitoring service - - sum_cpu_percent # cpu percent used by all the mms server processes and workers - - sum_memory_percent - - sum_num_handles - - server_workers # no of mms workers - -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -setting: - env: - BASEDIR : '.' \ No newline at end of file diff --git a/benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml b/benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml deleted file mode 100644 index ace86a732..000000000 --- a/benchmarks/monitoring/tests/inference_server_monitoring_criteria.yaml +++ /dev/null @@ -1,56 +0,0 @@ - -execution: -- concurrency: 4 - ramp-up: 1s - hold-for: 20s - scenario: Inference -scenarios: - Inference: - script: register_and_inference.jmx - -modules: - jmeter: - properties: - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg - - -services: - - module: monitoring - server-agent: - - address: localhost:9009 # metric monitoring service address - label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port - interval: 1s # polling interval - logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir - metrics: # metrics should be supported by monitoring service - - sum_cpu_percent # cpu percent used by all the mms server processes and workers - - sum_memory_percent - - sum_num_handles - - server_workers # no of mms workers - - -reporting: -- module: passfail - criteria: - - fail of s2_fail >50%, stop as failed - - p90 of s2_p90 >250ms , stop as failed - - avg-rt of s2_avg_rt >1s , stop as failed - - class: bzt.modules.monitoring.MonitoringCriteria - subject: mms-inference-server/sum_num_handles - condition: '>' - threshold: 80 - timeframe: 1s - fail: true - stop: true -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -setting: - env: - BASEDIR : '.' \ No newline at end of file diff --git a/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml b/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml deleted file mode 100644 index 99a7d2910..000000000 --- a/benchmarks/monitoring/tests/inference_taurus_local_monitoring.yaml +++ /dev/null @@ -1,48 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 5s - hold-for: 20s - scenario: Inference -scenarios: - Inference: - script: register_and_inference.jmx - -modules: - jmeter: - properties: - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg - server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. - -services: - - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - sum_memory_percent - - module: monitoring - ~local: - - interval: 20s # polling interval - logging: True # local monitoring logs will be saved to "local_monitoring_logs.csv" in the artifacts dir - metrics: - - cpu -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -setting: - env: - BASEDIR : '.' \ No newline at end of file diff --git a/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml b/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml deleted file mode 100644 index 98cb72eb3..000000000 --- a/benchmarks/monitoring/tests/inference_taurus_local_monitoring_criteria.yaml +++ /dev/null @@ -1,55 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 5s - hold-for: 20s - scenario: Inference -scenarios: - Inference: - script: register_and_inference.jmx - -modules: - jmeter: - properties: - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg - server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. - -services: - - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - sum_memory_percent - -reporting: -- module: passfail - criteria: - - fail of s2_fail >50%, stop as failed - - p90 of s2_p90 >250ms , stop as failed - - avg-rt of s2_avg_rt >1s , stop as failed - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_memory_percent - condition: '>' - threshold: 10 - timeframe: 1s - stop : true - fail : true - -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -setting: - env: - BASEDIR : '.' \ No newline at end of file diff --git a/benchmarks/monitoring/tests/kitten.jpg b/benchmarks/monitoring/tests/kitten.jpg deleted file mode 100644 index ffcd2be2c674fddc4886117c12f3b29e4367aba8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110969 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

J66l>z7T20v&BrMUK^shcTlD$)2!& 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

hPAkbbTr(^R-tMK2 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%0DL;n=L?~l?ng&I8MPm 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 z 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%0D>0jKMROl@?0Zb`>KdeJYDW6roP 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 NZ`_ZLr78E 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 - - - - - false - true - false - - - - - - - - - - cnn_url - https://s3.amazonaws.com/model-server/models/squeezenet_v1.1/squeezenet_v1.1.model - = - The url from where to fetch noop model from - - - scale_up_workers - ${__P(min_workers,1)} - = - The workers to scale No op model to - - - scale_down_workers - 0 - Offload the No-Op Model - = - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_up_workers} - PUT - true - false - true - false - - - - - - - - continue - - false - ${__P(loops,4)} - - ${__P(threads,10)} - ${__P(rampup,10)} - false - - - true - - - - - - - ${__P(input_filepath)} - data - image/jpeg - - - - - - - - - - - /predictions/${model} - POST - true - false - true - true - - - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_down_workers} - DELETE - true - false - true - false - - - - - - - - - diff --git a/performance_regression/imageInputModelPlan.jmx.yaml b/performance_regression/imageInputModelPlan.jmx.yaml deleted file mode 100644 index c662874d0..000000000 --- a/performance_regression/imageInputModelPlan.jmx.yaml +++ /dev/null @@ -1,51 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 5s - hold-for: 1m - scenario: Inference -scenarios: - Inference: - default-address: ${__P(protocol,https)}://${__P(hostname,127.0.0.1)}:${__P(port,8443)}/ - requests: - - follow-redirects: true - label: Inference Request - method: POST - upload-files: - - mime-type: image/jpeg - param: data - path: ${__P(input_filepath)} - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - cnn_url: ${__P(url, https://s3.amazonaws.com/model-server/models/squeezenet_v1.1/squeezenet_v1.1.model)} - model: ${__P(model_name,squeezenet_v1.1)} - scale_down_workers: '0' - scale_up_workers: ${__P(min_workers,1)} -modules: - jmeter: - properties: - input_filepath : kitten.jpg - model_name : squeezenet -services: - - module: monitoring - local: - - interval: 2s - logging: True - metrics: - - cpu - - disk-space - - mem -reporting: -- module: passfail - criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - subject: local/cpu - condition: '>' - threshold: 100 - timeframe: 6s -- module: junit-xml - filename: ${TAURUS_ARTIFACTS_DIR}/output/results.xml - data-source: pass-fail From 0705f743318332e983bf3ef9fecddff095dbcb07 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Sat, 13 Jun 2020 00:14:58 +0530 Subject: [PATCH 068/329] [ci_skip] fixed pyenv path in image --- .circleci/images/Dockerfile.python2.7 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/images/Dockerfile.python2.7 b/.circleci/images/Dockerfile.python2.7 index 257771b93..0b8341e92 100644 --- a/.circleci/images/Dockerfile.python2.7 +++ b/.circleci/images/Dockerfile.python2.7 @@ -57,9 +57,9 @@ RUN cd /opt \ # Only the for the performance regression suite, Python 3.6.5 will be local installed using "pyenv local 3.6.5" # !! MMS server will continue using Python 2.7 !! RUN curl https://pyenv.run | bash \ - && echo 'export PATH="$HOME/.pyenv/bin:$PATH"' >> ~/.bashrc \ + && echo 'export PATH="/root/.pyenv/bin:$PATH"' >> ~/.bashrc \ && echo 'eval "$(pyenv init -)"' >> ~/.bashrc \ && echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc \ && . ~/.bashrc \ && pyenv install 3.6.5 -ENV PATH="$HOME/.pyenv/bin:$PATH" +ENV PATH="/root/.pyenv/bin:$PATH" From 5529fc5de0e130468d777b5e3123f1ff5bb0a314 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Sat, 13 Jun 2020 00:30:24 +0530 Subject: [PATCH 069/329] trigger --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6ada02ff3..a4c14cfca 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -198,4 +198,4 @@ workflows: - performance_regression: executor: docker-python-2 requires: - - build \ No newline at end of file + - build From 2a7ca5ebba5f9e529ecc1db9d26691d1c1332f53 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Sat, 13 Jun 2020 01:29:22 +0530 Subject: [PATCH 070/329] pyenv shim location to PATH var [skip ci] --- .circleci/images/Dockerfile.python2.7 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/images/Dockerfile.python2.7 b/.circleci/images/Dockerfile.python2.7 index 0b8341e92..b19b18a6c 100644 --- a/.circleci/images/Dockerfile.python2.7 +++ b/.circleci/images/Dockerfile.python2.7 @@ -62,4 +62,4 @@ RUN curl https://pyenv.run | bash \ && echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc \ && . ~/.bashrc \ && pyenv install 3.6.5 -ENV PATH="/root/.pyenv/bin:$PATH" +ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:$PATH" From 1d3e6d79faf66639a68b33a144619df76d9f01bf Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Sat, 13 Jun 2020 01:43:33 +0530 Subject: [PATCH 071/329] Trigger Build From f82d46523c349de9519975d1eb4d49eb42522c6e Mon Sep 17 00:00:00 2001 From: maheshambule Date: Sat, 13 Jun 2020 02:27:31 +0530 Subject: [PATCH 072/329] remove system err --- tests/performance/run_perfomance_suite.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/performance/run_perfomance_suite.py b/tests/performance/run_perfomance_suite.py index 52d0032f5..1c4fc6331 100755 --- a/tests/performance/run_perfomance_suite.py +++ b/tests/performance/run_perfomance_suite.py @@ -113,7 +113,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser code, output = run_process(start_monitoring_server, wait=False) time.sleep(2) - ## TODO - Add check if server started + # TODO - Add check if server started junit_xml = JUnitXml() pre_command = 'export PYTHONPATH={}:$PYTHONPATH; '.format(str(path)) @@ -153,16 +153,17 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser tc = TestCase(name) tc.result = result - tc.system_err = err_txt[:-4] + # TODO Fix html report with system_err + # tc.system_err = err_txt[:-4] ts.add_testcase(tc) else: tc = TestCase(suite_name) if code: tc.result = Error("Suite run failed", "Error") - tc.system_err = err[:-4] + # tc.system_err = err[:-4] else: tc.result = Skipped() - tc.system_out = err[:-4] + # tc.system_out = err[:-4] ts.add_testcase(tc) ts.hostname = socket.gethostname() From 0892735f67c6e4f7f2e6b3a5340dedd6f3b3c655 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Sat, 13 Jun 2020 13:26:54 +0530 Subject: [PATCH 073/329] [skip ci] download pyenv, install 3.6.5 --- .circleci/images/Dockerfile.python2.7 | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.circleci/images/Dockerfile.python2.7 b/.circleci/images/Dockerfile.python2.7 index b19b18a6c..3d51bf465 100644 --- a/.circleci/images/Dockerfile.python2.7 +++ b/.circleci/images/Dockerfile.python2.7 @@ -57,9 +57,4 @@ RUN cd /opt \ # Only the for the performance regression suite, Python 3.6.5 will be local installed using "pyenv local 3.6.5" # !! MMS server will continue using Python 2.7 !! RUN curl https://pyenv.run | bash \ - && echo 'export PATH="/root/.pyenv/bin:$PATH"' >> ~/.bashrc \ - && echo 'eval "$(pyenv init -)"' >> ~/.bashrc \ - && echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc \ - && . ~/.bashrc \ - && pyenv install 3.6.5 -ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:$PATH" + && $HOME/.pyenv/bin/pyenv install 3.6.5 \ No newline at end of file From 13611fabb30dafd85f218f9fc3ae5e461866b805 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Sat, 13 Jun 2020 13:44:51 +0530 Subject: [PATCH 074/329] set pyenv and shim in path, before start tests --- .circleci/images/Dockerfile.python2.7 | 5 +++-- .circleci/scripts/linux_test_taurus.sh | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.circleci/images/Dockerfile.python2.7 b/.circleci/images/Dockerfile.python2.7 index 3d51bf465..a7bf0d2cd 100644 --- a/.circleci/images/Dockerfile.python2.7 +++ b/.circleci/images/Dockerfile.python2.7 @@ -53,8 +53,9 @@ RUN cd /opt \ # bzt is used for performance regression test suite # bzt requires python 3.6 runtime. -# Install pyenv to manage both python versions (2.7 and 3.6.5) -# Only the for the performance regression suite, Python 3.6.5 will be local installed using "pyenv local 3.6.5" +# Download pyenv, use pyenv to download python 3.6.5. +# The downloaded python 3.6.5 is isolated and doesn't interfere with default python(2.7) +# Only before starting the performance regression suite, py 3.6.5 is local installed(pyenv local 3.6.5) in test dir # !! MMS server will continue using Python 2.7 !! RUN curl https://pyenv.run | bash \ && $HOME/.pyenv/bin/pyenv install 3.6.5 \ No newline at end of file diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_taurus.sh index 0bf09bd1e..6d3f80764 100755 --- a/.circleci/scripts/linux_test_taurus.sh +++ b/.circleci/scripts/linux_test_taurus.sh @@ -7,9 +7,11 @@ sleep 90 cd performance_regression -# Hack to use python 3.6.5 for bzt installation and execution on a Python 2.7 environment -# ToDo: Update condition to be more specific - check if python 2 is installed by default -if hash pyenv 2>/dev/null; then +# Only on a python 2 environment - +PY_MAJOR_VER=$(python -c 'import sys; major = sys.version_info.major; print(major);') +if [ $PY_MAJOR_VER -eq 2 ]; then + # Hack to use python 3.6.5 for bzt installation and execution + export PATH="/root/.pyenv/bin:/root/.pyenv/shims:$PATH" pyenv local 3.6.5 fi From a5a062b8c2525e50e0aa09cda5d5486d49248161 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Sat, 13 Jun 2020 14:02:24 +0530 Subject: [PATCH 075/329] revert change to setup, trigger all suites --- .circleci/config.yml | 72 ++++++++++++++++++++++---------------------- setup.py | 2 +- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1407ad8fa..6ada02ff3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -154,47 +154,47 @@ jobs: workflows: version: 2 -# build_and_test_python36: -# jobs: -# - build: -# executor: docker-python-3 -# - modelarchiver: -# executor: docker-python-3 -# - python_tests: -# executor: docker-python-3 -# requires: -# - build -# - api_tests: -# executor: docker-python-3 -# requires: -# - build -# - benchmark: -# executor: docker-python-3 -# requires: -# - build -# - performance_regression: -# executor: docker-python-3 -# requires: -# - build + build_and_test_python36: + jobs: + - build: + executor: docker-python-3 + - modelarchiver: + executor: docker-python-3 + - python_tests: + executor: docker-python-3 + requires: + - build + - api_tests: + executor: docker-python-3 + requires: + - build + - benchmark: + executor: docker-python-3 + requires: + - build + - performance_regression: + executor: docker-python-3 + requires: + - build build_and_test_python27: jobs: - build: executor: docker-python-2 -# - modelarchiver: -# executor: docker-python-2 -# - python_tests: -# executor: docker-python-2 -# requires: -# - build -# - api_tests: -# executor: docker-python-2 -# requires: -# - build -# - benchmark: -# executor: docker-python-2 -# requires: -# - build + - modelarchiver: + executor: docker-python-2 + - python_tests: + executor: docker-python-2 + requires: + - build + - api_tests: + executor: docker-python-2 + requires: + - build + - benchmark: + executor: docker-python-2 + requires: + - build - performance_regression: executor: docker-python-2 requires: diff --git a/setup.py b/setup.py index 079a0e40a..3c125fff5 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ def run(self): rmtree('build/lib/') try: - subprocess.check_call('frontend/gradlew -p frontend clean assemble', shell=True) + subprocess.check_call('frontend/gradlew -p frontend clean build', shell=True) except OSError: assert 0, "build failed" copy2(self.source_server_file, self.dest_file_name) From c0ed4762420b5d470d1424debad58a82ad9060e5 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Sat, 13 Jun 2020 14:21:16 +0530 Subject: [PATCH 076/329] [skip ci] Update newman readme and shell script --- test/README.md | 23 ++++++++--------------- test/regression_tests.sh | 12 ++++++------ 2 files changed, 14 insertions(+), 21 deletions(-) mode change 100755 => 100644 test/regression_tests.sh diff --git a/test/README.md b/test/README.md index 93cd23621..cc30f24cd 100644 --- a/test/README.md +++ b/test/README.md @@ -1,25 +1,18 @@ # MMS Regression Tests -This folder contains nightly regression tests execututed against MMS master.These tests use [POSTMAN](https://www.postman.com/downloads/) for exercising all the Management & Inference APIs. - -### Latest Test Run Status - +This folder contains regression tests executed against MMS master.These tests use [POSTMAN](https://www.postman.com/downloads/) for exercising all the Management & Inference APIs. ### Running the test manually. -Clone Torch Serve Repo & Build the Docker Image for the execition env. - +Pull multi-model-server pre build docker image ``` -git clone https://github.com/awslabs/multi-model-server.git - -run mms docker - +docker pull awsdeeplearningteam/multi-model-server ``` -This would build a docker Image with a pytorch/torchserve:latest in which we would run our Regression Tests. +This would build a docker Image with a awsdeeplearningteam/multi-model-server:latest in which we would run our Regression Tests. ``` -docker run -it --user root pytorch/mms:latest /bin/bash +docker run -it --user root awsdeeplearningteam/multi-model-server:latest /bin/bash ``` In the Docker CLI execute the following cmds. @@ -28,7 +21,7 @@ In the Docker CLI execute the following cmds. apt-get update apt-get install -y git wget sudo git clone https://github.com/awslabs/multi-model-server.git -cd serve +cd multi-model-server ``` To execute tests on master run: @@ -39,11 +32,11 @@ To execute tests on different run: `./test/regression_tests.sh ` -You can view the logs for Test execution & the Torch serve in the /tmp dir. +You can view the logs for Test execution & the Multi-model-server in the /tmp dir. ``` cat /tmp/test_exec.log -cat /tmp/ts.log +cat /tmp/mms.log ``` ### Adding tests diff --git a/test/regression_tests.sh b/test/regression_tests.sh old mode 100755 new mode 100644 index 4ad53b7d1..8825ee6ad --- a/test/regression_tests.sh +++ b/test/regression_tests.sh @@ -48,7 +48,7 @@ stop_mms_serve() { start_secure_mms() { # Start MMS with Model Store - multi-model-server --start --mms-config resources/config.properties --model-store $1 &>> $2 + multi-model-server --start --mms-config test/resources/config.properties --model-store $1 &>> $2 sleep 10 curl --insecure -X GET https://127.0.0.1:8444/models } @@ -57,25 +57,25 @@ start_secure_mms() { run_postman_test() { # Run Postman Scripts mkdir $ROOT_DIR/report/ - cd $CODEBUILD_WD/test/ + cd $CODEBUILD_WD/ set +e # Run Management API Tests stop_mms_serve start_mms $MODEL_STORE $MMS_LOG_FILE - newman run -e postman/environment.json --bail --verbose postman/management_api_test_collection.json \ + newman run -e test/postman/environment.json --bail --verbose test/postman/management_api_test_collection.json \ -r cli,html --reporter-html-export $ROOT_DIR/report/management_report.html >>$1 2>&1 # Run Inference API Tests after Restart stop_mms_serve start_mms $MODEL_STORE $MMS_LOG_FILE - newman run -e postman/environment.json --bail --verbose postman/inference_api_test_collection.json \ - -d postman/inference_data.json -r cli,html --reporter-html-export $ROOT_DIR/report/inference_report.html >>$1 2>&1 + newman run -e test/postman/environment.json --bail --verbose test/postman/inference_api_test_collection.json \ + -d test/postman/inference_data.json -r cli,html --reporter-html-export $ROOT_DIR/report/inference_report.html >>$1 2>&1 # Run Https test cases stop_mms_serve start_secure_mms $MODEL_STORE $MMS_LOG_FILE - newman run --insecure -e postman/environment.json --bail --verbose postman/https_test_collection.json \ + newman run --insecure -e test/postman/environment.json --bail --verbose test/postman/https_test_collection.json \ -r cli,html --reporter-html-export $ROOT_DIR/report/MMS_https_test_report.html >>$1 2>&1 stop_mms_serve From 590a52cb26bd1bc3659d0c988fa05aa6a7eba632 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Sat, 13 Jun 2020 14:29:29 +0530 Subject: [PATCH 077/329] remove system err --- tests/performance/run_perfomance_suite.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/performance/run_perfomance_suite.py b/tests/performance/run_perfomance_suite.py index 52d0032f5..1c4fc6331 100755 --- a/tests/performance/run_perfomance_suite.py +++ b/tests/performance/run_perfomance_suite.py @@ -113,7 +113,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser code, output = run_process(start_monitoring_server, wait=False) time.sleep(2) - ## TODO - Add check if server started + # TODO - Add check if server started junit_xml = JUnitXml() pre_command = 'export PYTHONPATH={}:$PYTHONPATH; '.format(str(path)) @@ -153,16 +153,17 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser tc = TestCase(name) tc.result = result - tc.system_err = err_txt[:-4] + # TODO Fix html report with system_err + # tc.system_err = err_txt[:-4] ts.add_testcase(tc) else: tc = TestCase(suite_name) if code: tc.result = Error("Suite run failed", "Error") - tc.system_err = err[:-4] + # tc.system_err = err[:-4] else: tc.result = Skipped() - tc.system_out = err[:-4] + # tc.system_out = err[:-4] ts.add_testcase(tc) ts.hostname = socket.gethostname() From 94f88b86a467233ab29f1fb69dc191f3e893e940 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Sat, 13 Jun 2020 14:41:10 +0530 Subject: [PATCH 078/329] rename script to perf_regression --- .circleci/config.yml | 2 +- .../{linux_test_taurus.sh => linux_test_perf_regression.sh} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename .circleci/scripts/{linux_test_taurus.sh => linux_test_perf_regression.sh} (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index a4c14cfca..4e63cd9f4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -128,7 +128,7 @@ jobs: - install-mms-server - run: name: Start MMS, Execute performance regression testcases, Stop MMS - command: .circleci/scripts/linux_test_taurus.sh + command: .circleci/scripts/linux_test_perf_regression.sh - store_artifacts: name: Store server logs path: mms.log diff --git a/.circleci/scripts/linux_test_taurus.sh b/.circleci/scripts/linux_test_perf_regression.sh similarity index 100% rename from .circleci/scripts/linux_test_taurus.sh rename to .circleci/scripts/linux_test_perf_regression.sh From 2c82c282beb8d37120fd5e56b6d54023a6244023 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 16 Jun 2020 21:27:10 +0530 Subject: [PATCH 079/329] compute metrics --- tests/performance/metrics/__init2__.py | 66 ++++++++++ tests/performance/metrics/__init__.py | 171 +++++++++++++++++++++---- 2 files changed, 210 insertions(+), 27 deletions(-) create mode 100644 tests/performance/metrics/__init2__.py diff --git a/tests/performance/metrics/__init2__.py b/tests/performance/metrics/__init2__.py new file mode 100644 index 000000000..ea2a694bd --- /dev/null +++ b/tests/performance/metrics/__init2__.py @@ -0,0 +1,66 @@ +import psutil +import pandas as pd + +SEPARATOR = '~' +# INPUTS = ['min~cpu_percent', 'max~cpu_times~system', 'sum~memory_info~rss', 'mean~io_counters~read_count', 'sum~num_fds', 'sum~num_threads'] +INPUTS = ['max~cpu_times~system', 'sum~num_threads'] + +def input_parser(input): + tokens = input.split(SEPARATOR) + operator = tokens[0] + metric_type = tokens[1] + try: + metric_attr = tokens[2] + except IndexError: + metric_attr = None + + metric_name = metric_type if metric_attr is None else metric_type + '_' + metric_attr + return operator, metric_type, metric_attr, metric_name + + +info = { + 'name': [], + 'ptype': [], + 'pid': [], + 'parent_pid': [] +} + +def parse_process_tree(process, level=0): + stat_dict = process.as_dict() + + info['name'].append(stat_dict['name']) + info['ptype'].append(level) + info['pid'].append(stat_dict['pid']) + info['parent_pid'].append(stat_dict['ppid']) + + for input in INPUTS: + operator, metric_type, metric_attr, metric_name = input_parser(input) + info[metric_name] = info[metric_name] if info.get(metric_name) else [] + + if metric_attr is None: + info[metric_name].append(stat_dict.get(metric_type, 0)) + else: + info[metric_name].append(getattr(stat_dict.get(metric_type, {}), metric_attr, 0)) + + for child in process.children(): + parse_process_tree(child, level+1) + + +server_process = psutil.Process(8182) +parse_process_tree(server_process) + +df = pd.DataFrame(info) +print("---------------------------Data Frame Object---------------------------") +print(df) +print() +result = {} +for input in INPUTS: + operator, metric_type, metric_attr, metric_name = input_parser(input) + operator_fn = getattr(df, operator) + res = operator_fn() + print("-------------- "+operator+" : "+metric_name+" --------------") + print(res) + print() + result[input] = res.get(metric_name) +print("---------------------------------------------------") +print(result) \ No newline at end of file diff --git a/tests/performance/metrics/__init__.py b/tests/performance/metrics/__init__.py index 637857180..cea0f9423 100644 --- a/tests/performance/metrics/__init__.py +++ b/tests/performance/metrics/__init__.py @@ -10,16 +10,101 @@ # express or implied. See the License for the specific language governing # permissions and limitations under the License. +import psutil +from statistics import mean +from enum import Enum + """ Custom metrics """ # pylint: disable=redefined-builtin -AVAILABLE_METRICS = ["sum_cpu_percent", - "sum_memory_percent", - "sum_num_handles", - "server_workers"] +class ProcessType(Enum): + FRONTEND = 1 + WORKER = 2 + ALL = 3 + + + +result = { + 'sum_cpu_percent': None, + 'avg_cpu_percent': None, + 'min_cpu_percent': None, + 'max_cpu_percent': None, + + 'sum_memory_percent': None, + 'avg_memory_percent': None, + 'min_memory_percent': None, + 'max_memory_percent': None, + + 'sum_cpu_user_time': None, + 'avg_cpu_user_time': None, + 'min_cpu_user_time': None, + 'max_cpu_user_time': None, + + 'sum_cpu_system_time': None, + 'avg_cpu_system_time': None, + 'min_cpu_system_time': None, + 'max_cpu_system_time': None, + + 'sum_cpu_iowait_time': None, + 'avg_cpu_iowait_time': None, + 'min_cpu_iowait_time': None, + 'max_cpu_iowait_time': None, + + 'sum_memory_rss': None, + 'avg_memory_rss': None, + 'min_memory_rss': None, + 'max_memory_rss': None, + + 'sum_memory_vms': None, + 'avg_memory_vms': None, + 'min_memory_vms': None, + 'max_memory_vms': None, + + 'sum_io_read_count': None, + 'avg_io_read_count': None, + 'min_io_read_count': None, + 'max_io_read_count': None, + + 'sum_io_write_count': None, + 'avg_io_write_count': None, + 'min_io_write_count': None, + 'max_io_write_count': None, + + 'sum_io_read_bytes': None, + 'avg_io_read_bytes': None, + 'min_io_read_bytes': None, + 'max_io_read_bytes': None, + + 'sum_io_write_bytes': None, + 'avg_io_write_bytes': None, + 'min_io_write_bytes': None, + 'max_io_write_bytes': None, + + 'sum_file_descriptors': None, + 'avg_file_descriptors': None, + 'min_file_descriptors': None, + 'max_file_descriptors': None, + + 'sum_threads': None, + 'avg_threads': None, + 'min_threads': None, + 'max_threads': None, + + 'sum_processes': None, + + 'system_disk_used': None, + 'system_memory_used': None, + 'system_read_count': None, + 'system_read_bytes': None, + 'system_write_bytes': None, + + 'frontend_rss': None, + 'sum_workers_rss': None +} +AVAILABLE_METRICS = list(result) def get_metrics(server_process, child_processes): """ Get Server processes specific metrics @@ -29,27 +114,59 @@ def get_metrics(server_process, child_processes): # TODO - allow users to add new metrics easily # TODO - make sure available metric list is maintained - sum_cpu_percent = 0 - sum_memory_percent = 0 - sum_num_handles = 0 - server_workers = 0 - metrics = {} - for process in [server_process] + child_processes: - try: - process.cpu_percent() # to warm-up - except: - pass + def update_metric(name, type, stats): + if type == ProcessType.WORKER: + type = "workers" + elif type == ProcessType.FRONTEND : + result['frontend_' + name] = stats[0] + return else: - cpu_percent = process.cpu_percent() - memory_percent = process.memory_percent() - sum_cpu_percent += cpu_percent - sum_memory_percent += memory_percent - sum_num_handles += process.num_fds() - server_workers += 1 - - metrics["sum_cpu_percent"] = sum_cpu_percent - metrics["sum_memory_percent"] = sum_memory_percent - metrics["sum_num_handles"] = sum_num_handles - metrics["server_workers"] = server_workers - - return metrics \ No newline at end of file + type="all" + + result['sum_'+type+'_'+ name] = sum(stats) + result['avg_'+type+'_'+ name] = mean(stats) + result['min_'+type+'_'+ name] = min(stats) + result['max_'+type+'_'+ name] = max(stats) + + processes_stats = [] + processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) + for process in child_processes: + # Get all stats in one shots + processes_stats.append({'type': ProcessType.WORKER, 'stats' : process.as_dict()}) + + ### PROCESS METRICS ### + + metrics = {} + metrics['cpu_percent'] = lambda p: p.get('cpu_percent', 0) + metrics['rss'] = lambda p: getattr(p.get('memory_info', {}), 'rss', 0) + + for k in metrics: + + worker_stats = list(map(lambda x: x['stats'], filter(lambda x: x['type'] == ProcessType.WORKER, processes_stats))) + all_stats = list(map(lambda x: x['stats'], processes_stats)) + server_stats = list(map(lambda x: x['stats'], filter(lambda x: x['type'] == ProcessType.FRONTEND, processes_stats))) + + if len(worker_stats): update_metric(k, ProcessType.WORKER, list(map(metrics[k], worker_stats))) + if len(all_stats): update_metric(k, ProcessType.ALL, list(map(metrics[k], all_stats))) + if len(server_stats): update_metric(k, ProcessType.FRONTEND, list(map(metrics[k], server_stats))) + + + # Total processes + result['sum_processes'] = len(processes_stats) + + ### SYSTEM METRICS ### + result["system_disk_used"] = psutil.disk_usage('/').used + + result["system_memory_percent"] = psutil.virtual_memory().percent + + system_disk_io_counters = psutil.disk_io_counters() + result["system_read_count"] = system_disk_io_counters.read_count + result["system_write_count"] = system_disk_io_counters.write_count + result["system_read_bytes"] = system_disk_io_counters.read_bytes + result["system_write_bytes"] = system_disk_io_counters.write_bytes + + # print('frontend_rss :', result["frontend_rss"]) + # print('sum_workers_rss :', result["sum_workers_rss"]) + # print() + + return result \ No newline at end of file From 0c252e36e03093afcdd2b2047b0d65671a520dbe Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 16 Jun 2020 22:45:38 +0530 Subject: [PATCH 080/329] add metrics to the refactored code --- tests/performance/metrics/__init__.py | 182 ++++++++++-------- ...ence_taurus_local_monitoring_criteria.yaml | 6 +- 2 files changed, 103 insertions(+), 85 deletions(-) diff --git a/tests/performance/metrics/__init__.py b/tests/performance/metrics/__init__.py index cea0f9423..575378b8d 100644 --- a/tests/performance/metrics/__init__.py +++ b/tests/performance/metrics/__init__.py @@ -27,82 +27,82 @@ class ProcessType(Enum): result = { - 'sum_cpu_percent': None, - 'avg_cpu_percent': None, - 'min_cpu_percent': None, - 'max_cpu_percent': None, - - 'sum_memory_percent': None, - 'avg_memory_percent': None, - 'min_memory_percent': None, - 'max_memory_percent': None, - - 'sum_cpu_user_time': None, - 'avg_cpu_user_time': None, - 'min_cpu_user_time': None, - 'max_cpu_user_time': None, - - 'sum_cpu_system_time': None, - 'avg_cpu_system_time': None, - 'min_cpu_system_time': None, - 'max_cpu_system_time': None, - - 'sum_cpu_iowait_time': None, - 'avg_cpu_iowait_time': None, - 'min_cpu_iowait_time': None, - 'max_cpu_iowait_time': None, - - 'sum_memory_rss': None, - 'avg_memory_rss': None, - 'min_memory_rss': None, - 'max_memory_rss': None, - - 'sum_memory_vms': None, - 'avg_memory_vms': None, - 'min_memory_vms': None, - 'max_memory_vms': None, - - 'sum_io_read_count': None, - 'avg_io_read_count': None, - 'min_io_read_count': None, - 'max_io_read_count': None, - - 'sum_io_write_count': None, - 'avg_io_write_count': None, - 'min_io_write_count': None, - 'max_io_write_count': None, - - 'sum_io_read_bytes': None, - 'avg_io_read_bytes': None, - 'min_io_read_bytes': None, - 'max_io_read_bytes': None, - - 'sum_io_write_bytes': None, - 'avg_io_write_bytes': None, - 'min_io_write_bytes': None, - 'max_io_write_bytes': None, - - 'sum_file_descriptors': None, - 'avg_file_descriptors': None, - 'min_file_descriptors': None, - 'max_file_descriptors': None, - - 'sum_threads': None, - 'avg_threads': None, - 'min_threads': None, - 'max_threads': None, - - 'sum_processes': None, - - - 'system_disk_used': None, - 'system_memory_used': None, - 'system_read_count': None, - 'system_read_bytes': None, - 'system_write_bytes': None, - - 'frontend_rss': None, - 'sum_workers_rss': None + # 'sum_cpu_percent': None, + # 'avg_cpu_percent': None, + # 'min_cpu_percent': None, + # 'max_cpu_percent': None, + # + # 'sum_memory_percent': None, + # 'avg_memory_percent': None, + # 'min_memory_percent': None, + # 'max_memory_percent': None, + # + # 'sum_cpu_user_time': None, + # 'avg_cpu_user_time': None, + # 'min_cpu_user_time': None, + # 'max_cpu_user_time': None, + # + # 'sum_cpu_system_time': None, + # 'avg_cpu_system_time': None, + # 'min_cpu_system_time': None, + # 'max_cpu_system_time': None, + # + # 'sum_cpu_iowait_time': None, + # 'avg_cpu_iowait_time': None, + # 'min_cpu_iowait_time': None, + # 'max_cpu_iowait_time': None, + # + # 'sum_memory_rss': None, + # 'avg_memory_rss': None, + # 'min_memory_rss': None, + # 'max_memory_rss': None, + # + # 'sum_memory_vms': None, + # 'avg_memory_vms': None, + # 'min_memory_vms': None, + # 'max_memory_vms': None, + # + # 'sum_io_read_count': None, + # 'avg_io_read_count': None, + # 'min_io_read_count': None, + # 'max_io_read_count': None, + # + # 'sum_io_write_count': None, + # 'avg_io_write_count': None, + # 'min_io_write_count': None, + # 'max_io_write_count': None, + # + # 'sum_io_read_bytes': None, + # 'avg_io_read_bytes': None, + # 'min_io_read_bytes': None, + # 'max_io_read_bytes': None, + # + # 'sum_io_write_bytes': None, + # 'avg_io_write_bytes': None, + # 'min_io_write_bytes': None, + # 'max_io_write_bytes': None, + # + # 'sum_file_descriptors': None, + # 'avg_file_descriptors': None, + # 'min_file_descriptors': None, + # 'max_file_descriptors': None, + # + # 'sum_threads': None, + # 'avg_threads': None, + # 'min_threads': None, + # 'max_threads': None, + # + # 'sum_processes': None, + # + # + # 'system_disk_used': None, + # 'system_memory_used': None, + # 'system_read_count': None, + # 'system_read_bytes': None, + # 'system_write_bytes': None, + + 'frontend_memory_rss': None, + 'sum_workers_memory_rss': None } AVAILABLE_METRICS = list(result) @@ -136,9 +136,27 @@ def update_metric(name, type, stats): ### PROCESS METRICS ### - metrics = {} - metrics['cpu_percent'] = lambda p: p.get('cpu_percent', 0) - metrics['rss'] = lambda p: getattr(p.get('memory_info', {}), 'rss', 0) + metrics = { + 'cpu_percent': lambda p: p.get('cpu_percent', 0), + + 'memory_percent': lambda p: p.get('memory_percent', 0), + + 'cpu_user_time': lambda p: getattr(p.get('cpu_times', {}), 'user', 0), + 'cpu_system_time': lambda p: getattr(p.get('cpu_times', {}), 'system', 0), + 'cpu_iowait_time': lambda p: getattr(p.get('cpu_times', {}), 'iowait', 0), + + 'memory_rss': lambda p: getattr(p.get('memory_info', {}), 'rss', 0), + 'memory_vms': lambda p: getattr(p.get('memory_info', {}), 'vms', 0), + + 'io_read_count': lambda p: getattr(p.get('io_counters', {}), 'read_count', 0), + 'io_write_count': lambda p: getattr(p.get('io_counters', {}), 'write_count', 0), + 'io_read_bytes': lambda p: getattr(p.get('io_counters', {}), 'read_bytes', 0), + 'io_write_bytes': lambda p: getattr(p.get('io_counters', {}), 'write_bytes', 0), + + 'file_descriptors': lambda p: p.get('num_fds', 0), + + 'threads': lambda p: p.get('num_threads', 0) + } for k in metrics: @@ -165,8 +183,8 @@ def update_metric(name, type, stats): result["system_read_bytes"] = system_disk_io_counters.read_bytes result["system_write_bytes"] = system_disk_io_counters.write_bytes - # print('frontend_rss :', result["frontend_rss"]) - # print('sum_workers_rss :', result["sum_workers_rss"]) - # print() + print('frontend_memory_rss :', result["frontend_memory_rss"]) + print('sum_workers_memory_rss :', result["sum_workers_memory_rss"]) + print() return result \ No newline at end of file diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml index 5e3cea34c..c21a2cd18 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml @@ -26,7 +26,7 @@ services: - cpu - disk-space - mem - - sum_memory_percent + - sum_workers_memory_rss reporting: - module: passfail @@ -35,9 +35,9 @@ reporting: - p90 of s2_p90 >250ms , stop as failed - avg-rt of s2_avg_rt >1s , stop as failed - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_memory_percent + subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: 4 + threshold: 132000000 timeframe: 1s stop : true fail : true From 5ff38bc0c0cf64f5011c3f9adec7399df7d7c25b Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 17 Jun 2020 15:46:20 +0530 Subject: [PATCH 081/329] concurrent upscale of workers --- tests/performance/metrics/__init__.py | 14 +- .../tests/concurrent_upscale_workers.jmx | 161 ++++++++++++++++++ .../tests/concurrent_upscale_workers.yaml | 42 +++++ 3 files changed, 211 insertions(+), 6 deletions(-) create mode 100644 tests/performance/tests/concurrent_upscale_workers.jmx create mode 100644 tests/performance/tests/concurrent_upscale_workers.yaml diff --git a/tests/performance/metrics/__init__.py b/tests/performance/metrics/__init__.py index 575378b8d..58e70050e 100644 --- a/tests/performance/metrics/__init__.py +++ b/tests/performance/metrics/__init__.py @@ -101,6 +101,7 @@ class ProcessType(Enum): # 'system_read_bytes': None, # 'system_write_bytes': None, + 'total_workers': 0, 'frontend_memory_rss': None, 'sum_workers_memory_rss': None } @@ -116,17 +117,18 @@ def get_metrics(server_process, child_processes): def update_metric(name, type, stats): if type == ProcessType.WORKER: + result['total_workers'] = len(stats) - 1 type = "workers" - elif type == ProcessType.FRONTEND : + elif type == ProcessType.FRONTEND: result['frontend_' + name] = stats[0] return else: type="all" - result['sum_'+type+'_'+ name] = sum(stats) - result['avg_'+type+'_'+ name] = mean(stats) - result['min_'+type+'_'+ name] = min(stats) - result['max_'+type+'_'+ name] = max(stats) + result['sum_' + type+'_' + name] = sum(stats) + result['avg_' + type+'_' + name] = mean(stats) + result['min_' + type+'_' + name] = min(stats) + result['max_' + type+'_' + name] = max(stats) processes_stats = [] processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) @@ -183,8 +185,8 @@ def update_metric(name, type, stats): result["system_read_bytes"] = system_disk_io_counters.read_bytes result["system_write_bytes"] = system_disk_io_counters.write_bytes + print('total_workers :', result["total_workers"]) print('frontend_memory_rss :', result["frontend_memory_rss"]) print('sum_workers_memory_rss :', result["sum_workers_memory_rss"]) - print() return result \ No newline at end of file diff --git a/tests/performance/tests/concurrent_upscale_workers.jmx b/tests/performance/tests/concurrent_upscale_workers.jmx new file mode 100644 index 000000000..140675c57 --- /dev/null +++ b/tests/performance/tests/concurrent_upscale_workers.jmx @@ -0,0 +1,161 @@ + + + + + + false + true + false + + + + + + + + + + cnn_url + https://s3.amazonaws.com/model-server/models/squeezenet_v1.1/squeezenet_v1.1.model + = + The url from where to fetch noop model from + + + scale_up_workers + ${__P(min_workers,4)} + = + The workers to scale No op model to + + + scale_down_workers + 0 + Offload the No-Op Model + = + + + model + squeezenet_v1.1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${cnn_url} + POST + true + false + true + false + + + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_up_workers}&synchronous=true + PUT + true + false + true + false + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_down_workers} + DELETE + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/concurrent_upscale_workers.yaml b/tests/performance/tests/concurrent_upscale_workers.yaml new file mode 100644 index 000000000..3a870ade4 --- /dev/null +++ b/tests/performance/tests/concurrent_upscale_workers.yaml @@ -0,0 +1,42 @@ +--- +execution: +- concurrency: 3 + ramp-up: 5s + iterations: 5 + scenario: Inference + +scenarios: + Inference: + script: concurrent_upscale_workers.jmx + +modules: + server_local_monitoring: + # metrics_monitoring_taurus and dependencies should be in python path + class : metrics_monitoring_taurus.Monitor # monitoring class. + new_passfail: + class: new_passfail.PassFailStatus + + + +services: + - module: server_local_monitoring # should be added in modules section + ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + - interval: 1s + metrics: + - cpu + - disk-space + - mem + - total_workers +# - sum_worker_memomry_rss +# - frontend_memory_rss + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 4 + timeframe: 1s + stop : true + fail : true \ No newline at end of file From 32b792b7d02a3a416f709779ec51ccddb74df5c2 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 17 Jun 2020 19:10:30 +0530 Subject: [PATCH 082/329] updates bzt yaml as per available metrics --- tests/performance/metrics/__init__.py | 206 +++++++----------- .../tests/concurrent_upscale_workers.yaml | 2 - .../tests/inference_server_monitoring.yaml | 8 +- .../inference_server_monitoring_criteria.yaml | 10 +- .../inference_taurus_local_monitoring.yaml | 8 +- 5 files changed, 88 insertions(+), 146 deletions(-) diff --git a/tests/performance/metrics/__init__.py b/tests/performance/metrics/__init__.py index 58e70050e..4d8b4d690 100644 --- a/tests/performance/metrics/__init__.py +++ b/tests/performance/metrics/__init__.py @@ -25,87 +25,62 @@ class ProcessType(Enum): ALL = 3 +operators = { + 'sum': sum, + 'avg': mean, + 'min': min, + 'max': max +} + +process_metrics = { + 'cpu_percent': lambda p: p.get('cpu_percent', 0), + + 'memory_percent': lambda p: p.get('memory_percent', 0), + + 'cpu_user_time': lambda p: getattr(p.get('cpu_times', {}), 'user', 0), + 'cpu_system_time': lambda p: getattr(p.get('cpu_times', {}), 'system', 0), + 'cpu_iowait_time': lambda p: getattr(p.get('cpu_times', {}), 'iowait', 0), + + 'memory_rss': lambda p: getattr(p.get('memory_info', {}), 'rss', 0), + 'memory_vms': lambda p: getattr(p.get('memory_info', {}), 'vms', 0), + + 'io_read_count': lambda p: getattr(p.get('io_counters', {}), 'read_count', 0), + 'io_write_count': lambda p: getattr(p.get('io_counters', {}), 'write_count', 0), + 'io_read_bytes': lambda p: getattr(p.get('io_counters', {}), 'read_bytes', 0), + 'io_write_bytes': lambda p: getattr(p.get('io_counters', {}), 'write_bytes', 0), -result = { - # 'sum_cpu_percent': None, - # 'avg_cpu_percent': None, - # 'min_cpu_percent': None, - # 'max_cpu_percent': None, - # - # 'sum_memory_percent': None, - # 'avg_memory_percent': None, - # 'min_memory_percent': None, - # 'max_memory_percent': None, - # - # 'sum_cpu_user_time': None, - # 'avg_cpu_user_time': None, - # 'min_cpu_user_time': None, - # 'max_cpu_user_time': None, - # - # 'sum_cpu_system_time': None, - # 'avg_cpu_system_time': None, - # 'min_cpu_system_time': None, - # 'max_cpu_system_time': None, - # - # 'sum_cpu_iowait_time': None, - # 'avg_cpu_iowait_time': None, - # 'min_cpu_iowait_time': None, - # 'max_cpu_iowait_time': None, - # - # 'sum_memory_rss': None, - # 'avg_memory_rss': None, - # 'min_memory_rss': None, - # 'max_memory_rss': None, - # - # 'sum_memory_vms': None, - # 'avg_memory_vms': None, - # 'min_memory_vms': None, - # 'max_memory_vms': None, - # - # 'sum_io_read_count': None, - # 'avg_io_read_count': None, - # 'min_io_read_count': None, - # 'max_io_read_count': None, - # - # 'sum_io_write_count': None, - # 'avg_io_write_count': None, - # 'min_io_write_count': None, - # 'max_io_write_count': None, - # - # 'sum_io_read_bytes': None, - # 'avg_io_read_bytes': None, - # 'min_io_read_bytes': None, - # 'max_io_read_bytes': None, - # - # 'sum_io_write_bytes': None, - # 'avg_io_write_bytes': None, - # 'min_io_write_bytes': None, - # 'max_io_write_bytes': None, - # - # 'sum_file_descriptors': None, - # 'avg_file_descriptors': None, - # 'min_file_descriptors': None, - # 'max_file_descriptors': None, - # - # 'sum_threads': None, - # 'avg_threads': None, - # 'min_threads': None, - # 'max_threads': None, - # - # 'sum_processes': None, - # - # - # 'system_disk_used': None, - # 'system_memory_used': None, - # 'system_read_count': None, - # 'system_read_bytes': None, - # 'system_write_bytes': None, - - 'total_workers': 0, - 'frontend_memory_rss': None, - 'sum_workers_memory_rss': None + 'file_descriptors': lambda p: p.get('num_fds', 0), + + 'threads': lambda p: p.get('num_threads', 0) +} + +system_metrics = { + 'total_processes': None, + 'total_workers': None, + 'system_disk_used': None, + 'system_memory_percent': None, + 'system_read_count': None, + 'system_write_count': None, + 'system_read_bytes': None, + 'system_write_bytes': None, } -AVAILABLE_METRICS = list(result) + +AVAILABLE_METRICS = list(system_metrics) +for metric in list(process_metrics): + for ptype in list(ProcessType): + if ptype == ProcessType.WORKER: + for op in list(operators): + type = 'workers' + AVAILABLE_METRICS.append('{}_{}_{}'.format(op,type,metric)) + elif ptype == ProcessType.FRONTEND: + type = 'frontend' + AVAILABLE_METRICS.append('{}_{}'.format(type, metric)) + else: + type = 'all' + for op in list(operators): + type = 'all' + AVAILABLE_METRICS.append('{}_{}_{}'.format(op,type,metric)) + def get_metrics(server_process, child_processes): """ Get Server processes specific metrics @@ -115,78 +90,53 @@ def get_metrics(server_process, child_processes): # TODO - allow users to add new metrics easily # TODO - make sure available metric list is maintained - def update_metric(name, type, stats): + result = {} + + def update_metric(metric_name, type, stats): + stats = stats if stats else [0] + if type == ProcessType.WORKER: - result['total_workers'] = len(stats) - 1 - type = "workers" + type = 'workers' elif type == ProcessType.FRONTEND: - result['frontend_' + name] = stats[0] + result['frontend_' + metric_name] = stats[0] return else: - type="all" + type='all' - result['sum_' + type+'_' + name] = sum(stats) - result['avg_' + type+'_' + name] = mean(stats) - result['min_' + type+'_' + name] = min(stats) - result['max_' + type+'_' + name] = max(stats) + for op_name in operators: + result['{}_{}_{}'.format(op_name, type, metric_name)] = operators[op_name](stats) + # as_dict() gets all stats in one shot processes_stats = [] processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) for process in child_processes: - # Get all stats in one shots processes_stats.append({'type': ProcessType.WORKER, 'stats' : process.as_dict()}) ### PROCESS METRICS ### - - metrics = { - 'cpu_percent': lambda p: p.get('cpu_percent', 0), - - 'memory_percent': lambda p: p.get('memory_percent', 0), - - 'cpu_user_time': lambda p: getattr(p.get('cpu_times', {}), 'user', 0), - 'cpu_system_time': lambda p: getattr(p.get('cpu_times', {}), 'system', 0), - 'cpu_iowait_time': lambda p: getattr(p.get('cpu_times', {}), 'iowait', 0), - - 'memory_rss': lambda p: getattr(p.get('memory_info', {}), 'rss', 0), - 'memory_vms': lambda p: getattr(p.get('memory_info', {}), 'vms', 0), - - 'io_read_count': lambda p: getattr(p.get('io_counters', {}), 'read_count', 0), - 'io_write_count': lambda p: getattr(p.get('io_counters', {}), 'write_count', 0), - 'io_read_bytes': lambda p: getattr(p.get('io_counters', {}), 'read_bytes', 0), - 'io_write_bytes': lambda p: getattr(p.get('io_counters', {}), 'write_bytes', 0), - - 'file_descriptors': lambda p: p.get('num_fds', 0), - - 'threads': lambda p: p.get('num_threads', 0) - } - - for k in metrics: + for k in process_metrics: worker_stats = list(map(lambda x: x['stats'], filter(lambda x: x['type'] == ProcessType.WORKER, processes_stats))) all_stats = list(map(lambda x: x['stats'], processes_stats)) server_stats = list(map(lambda x: x['stats'], filter(lambda x: x['type'] == ProcessType.FRONTEND, processes_stats))) - if len(worker_stats): update_metric(k, ProcessType.WORKER, list(map(metrics[k], worker_stats))) - if len(all_stats): update_metric(k, ProcessType.ALL, list(map(metrics[k], all_stats))) - if len(server_stats): update_metric(k, ProcessType.FRONTEND, list(map(metrics[k], server_stats))) + update_metric(k, ProcessType.WORKER, list(map(process_metrics[k], worker_stats))) + update_metric(k, ProcessType.ALL, list(map(process_metrics[k], all_stats))) + update_metric(k, ProcessType.FRONTEND, list(map(process_metrics[k], server_stats))) # Total processes - result['sum_processes'] = len(processes_stats) + result['total_processes'] = len([server_process] + child_processes) + result['total_workers'] = len(child_processes) - 1 ### SYSTEM METRICS ### - result["system_disk_used"] = psutil.disk_usage('/').used + result['system_disk_used'] = psutil.disk_usage('/').used - result["system_memory_percent"] = psutil.virtual_memory().percent + result['system_memory_percent'] = psutil.virtual_memory().percent system_disk_io_counters = psutil.disk_io_counters() - result["system_read_count"] = system_disk_io_counters.read_count - result["system_write_count"] = system_disk_io_counters.write_count - result["system_read_bytes"] = system_disk_io_counters.read_bytes - result["system_write_bytes"] = system_disk_io_counters.write_bytes - - print('total_workers :', result["total_workers"]) - print('frontend_memory_rss :', result["frontend_memory_rss"]) - print('sum_workers_memory_rss :', result["sum_workers_memory_rss"]) + result['system_read_count'] = system_disk_io_counters.read_count + result['system_write_count'] = system_disk_io_counters.write_count + result['system_read_bytes'] = system_disk_io_counters.read_bytes + result['system_write_bytes'] = system_disk_io_counters.write_bytes - return result \ No newline at end of file + return result diff --git a/tests/performance/tests/concurrent_upscale_workers.yaml b/tests/performance/tests/concurrent_upscale_workers.yaml index 3a870ade4..535c78911 100644 --- a/tests/performance/tests/concurrent_upscale_workers.yaml +++ b/tests/performance/tests/concurrent_upscale_workers.yaml @@ -27,8 +27,6 @@ services: - disk-space - mem - total_workers -# - sum_worker_memomry_rss -# - frontend_memory_rss reporting: - module: passfail diff --git a/tests/performance/tests/inference_server_monitoring.yaml b/tests/performance/tests/inference_server_monitoring.yaml index 1a423dd13..020a1ce2a 100644 --- a/tests/performance/tests/inference_server_monitoring.yaml +++ b/tests/performance/tests/inference_server_monitoring.yaml @@ -19,8 +19,8 @@ services: interval: 1s # polling interval logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir metrics: # metrics should be supported by monitoring service - - sum_cpu_percent # cpu percent used by all the mms server processes and workers - - sum_memory_percent - - sum_num_handles - - server_workers # no of mms workers + - sum_all_cpu_percent # cpu percent used by all the mms server processes and workers + - sum_workers_memory_percent + - frontend_file_descriptors + - total_workers # no of mms workers diff --git a/tests/performance/tests/inference_server_monitoring_criteria.yaml b/tests/performance/tests/inference_server_monitoring_criteria.yaml index ce422fb64..61730a84d 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_server_monitoring_criteria.yaml @@ -19,10 +19,10 @@ services: interval: 1s # polling interval logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir metrics: # metrics should be supported by monitoring service - - sum_cpu_percent # cpu percent used by all the mms server processes and workers - - sum_memory_percent - - sum_num_handles - - server_workers # no of mms workers + - sum_workers_cpu_percent # cpu percent used by all the mms server processes and workers + - sum_workers_memory_percent + - sum_workers_file_descriptors + - total_workers # no of mms workers reporting: @@ -32,7 +32,7 @@ reporting: - p90 of s2_p90 >250ms , stop as failed - avg-rt of s2_avg_rt >1s , stop as failed - class: bzt.modules.monitoring.MonitoringCriteria - subject: mms-inference-server/sum_num_handles + subject: mms-inference-server/sum_workers_file_descriptors condition: '>' threshold: 80 timeframe: 1s diff --git a/tests/performance/tests/inference_taurus_local_monitoring.yaml b/tests/performance/tests/inference_taurus_local_monitoring.yaml index f2c80b061..0369b101c 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring.yaml @@ -21,10 +21,4 @@ services: - cpu - disk-space - mem - - sum_memory_percent - - module: monitoring - ~local: - - interval: 20s # polling interval - logging: True # local monitoring logs will be saved to "local_monitoring_logs.csv" in the artifacts dir - metrics: - - cpu \ No newline at end of file + - sum_workers_memory_percent \ No newline at end of file From 6889ce7ae405ff97648e7a9cabb074218ab9f945 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 17 Jun 2020 20:40:06 +0530 Subject: [PATCH 083/329] Updated readme for available for metrics --- tests/performance/README.md | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 734325ec4..817ebe147 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -183,7 +183,7 @@ Metrics can be monitored in two ways: cd $MMS_HOME/tests/performance bzt tests/inference_taurus_local_monitoring.yaml tests/common/global_config.yaml ``` - + #### 3. Add pass/fail criteria You can specify the pass/fail criteria for the test cases. Read more about it [here](https://gettaurus.org/docs/PassFail/) @@ -213,6 +213,41 @@ bzt inference_server_monitoring_criteria.yaml tests/common/global_config.yaml bzt inference_taurus_local_monitoring_criteria.yaml tests/common/global_config.yaml ``` +## Metrics that you can use for monitoring \ passfail criteria + **System Metrics** + > disk_used, memory_percent, read_count, write_count, read_bytes, write_byte + + | Syntax | Examples | + | ------ | -------- | + | system_{metricname} | system_disk_used, system_memory_percent, system_write_count | + + + **Process Metrics** + > cpu_percent, memory_percent, cpu_user_time, cpu_system_time, cpu_iowait_time, memory_rss, memory_vms, io_read_count, io_write_count, io_read_bytes, io_write_bytes, file_descriptors, threads + + - Frontend + It is a single java process + + | Syntax | Examples | + | ------ | -------- | + | frontend_{metricname} | frontend_cpu_percent, frontend_memory_percent, frontend_cpu_iowait_time, frontend_memory_rss, frontend_io_write_bytes, frontend_threads | + + - Workers + These are python processes. MMS can have more than one worker + Metrics for worker(s) are always available with an aggregate + > Aggregates + > sum, avg, min, max + + | Syntax | Examples | + | ------ | -------- | + | {aggregate}\_workers\_{metricname} | sum_workers_memory_percent, avg_workers_iowait_time, min_workers_io_write_bytes, max_workers_threads | + + - All (Frontend + Workers) + We can also aggregate metrics for both frontend and worker processes together + + | Syntax | Examples | + | ------ | -------- | + | {aggreagate}\_all\_{metricname} | sum_all_memory_percent, avg_all_iowait_time, min_all_io_write_bytes, max_all_threads | ## Work in Progress 1. Add more metrics for cpu and gpu both. Add documentation around those. From d0252e7b54032be785562782457c0e62a8689b6f Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 17 Jun 2020 20:57:41 +0530 Subject: [PATCH 084/329] Rename testcase names --- .../tests/{concurrent_upscale_workers.jmx => upscale_workers.jmx} | 0 .../{concurrent_upscale_workers.yaml => upscale_workers.yaml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/performance/tests/{concurrent_upscale_workers.jmx => upscale_workers.jmx} (100%) rename tests/performance/tests/{concurrent_upscale_workers.yaml => upscale_workers.yaml} (100%) diff --git a/tests/performance/tests/concurrent_upscale_workers.jmx b/tests/performance/tests/upscale_workers.jmx similarity index 100% rename from tests/performance/tests/concurrent_upscale_workers.jmx rename to tests/performance/tests/upscale_workers.jmx diff --git a/tests/performance/tests/concurrent_upscale_workers.yaml b/tests/performance/tests/upscale_workers.yaml similarity index 100% rename from tests/performance/tests/concurrent_upscale_workers.yaml rename to tests/performance/tests/upscale_workers.yaml From f8959d86b8cf603068377025c76844885ba65592 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 17 Jun 2020 21:21:40 +0530 Subject: [PATCH 085/329] total_workers, total_processes in Readme --- tests/performance/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 817ebe147..a4003de3f 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -240,14 +240,14 @@ bzt inference_taurus_local_monitoring_criteria.yaml tests/common/global_config.y | Syntax | Examples | | ------ | -------- | - | {aggregate}\_workers\_{metricname} | sum_workers_memory_percent, avg_workers_iowait_time, min_workers_io_write_bytes, max_workers_threads | + | {aggregate}\_workers\_{metricname} | total_workers, sum_workers_memory_percent, avg_workers_iowait_time, min_workers_io_write_bytes, max_workers_threads | - All (Frontend + Workers) We can also aggregate metrics for both frontend and worker processes together | Syntax | Examples | | ------ | -------- | - | {aggreagate}\_all\_{metricname} | sum_all_memory_percent, avg_all_iowait_time, min_all_io_write_bytes, max_all_threads | + | {aggreagate}\_all\_{metricname} | total_processes, sum_all_memory_percent, avg_all_iowait_time, min_all_io_write_bytes, max_all_threads | ## Work in Progress 1. Add more metrics for cpu and gpu both. Add documentation around those. From 072743d561cc1420aac6da1ca0d23bb176df5e6a Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 17 Jun 2020 22:13:48 +0530 Subject: [PATCH 086/329] upscale and downscale testcase --- tests/performance/tests/downscale_workers.jmx | 180 ++++++++++++++++++ .../performance/tests/downscale_workers.yaml | 40 ++++ .../tests/register_and_inference.jmx | 2 +- tests/performance/tests/upscale_workers.jmx | 2 +- tests/performance/tests/upscale_workers.yaml | 8 +- 5 files changed, 226 insertions(+), 6 deletions(-) create mode 100644 tests/performance/tests/downscale_workers.jmx create mode 100644 tests/performance/tests/downscale_workers.yaml diff --git a/tests/performance/tests/downscale_workers.jmx b/tests/performance/tests/downscale_workers.jmx new file mode 100644 index 000000000..5ea8dedac --- /dev/null +++ b/tests/performance/tests/downscale_workers.jmx @@ -0,0 +1,180 @@ + + + + + + false + true + false + + + + + + + + + + cnn_url + https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + = + The url from where to fetch noop model from + + + scale_up_workers + 4 + = + The workers to scale No op model to + + + scale_down_workers + 2 + Offload the No-Op Model + = + + + model + squeezenet_v1.1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${cnn_url} + POST + true + false + true + false + + + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_up_workers} + PUT + true + false + true + false + + + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_down_workers} + PUT + true + false + true + false + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=0 + DELETE + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/downscale_workers.yaml b/tests/performance/tests/downscale_workers.yaml new file mode 100644 index 000000000..da03deb70 --- /dev/null +++ b/tests/performance/tests/downscale_workers.yaml @@ -0,0 +1,40 @@ +--- +execution: +- concurrency: 3 + ramp-up: 5s + iterations: 20 + scenario: Downscale + +scenarios: + Downscale: + script: downscale_workers.jmx + +modules: + server_local_monitoring: + # metrics_monitoring_taurus and dependencies should be in python path + class : metrics_monitoring_taurus.Monitor # monitoring class. + new_passfail: + class: new_passfail.PassFailStatus + + + +services: + - module: server_local_monitoring # should be added in modules section + ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + - interval: 1s + metrics: + - cpu + - disk-space + - mem + - total_workers + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 2 + timeframe: 5s + stop : true + fail : true \ No newline at end of file diff --git a/tests/performance/tests/register_and_inference.jmx b/tests/performance/tests/register_and_inference.jmx index 8f7b21e6d..265b5efb8 100644 --- a/tests/performance/tests/register_and_inference.jmx +++ b/tests/performance/tests/register_and_inference.jmx @@ -16,7 +16,7 @@ cnn_url - https://s3.amazonaws.com/model-server/models/squeezenet_v1.1/squeezenet_v1.1.model + https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar = The url from where to fetch noop model from diff --git a/tests/performance/tests/upscale_workers.jmx b/tests/performance/tests/upscale_workers.jmx index 140675c57..ba2c883ad 100644 --- a/tests/performance/tests/upscale_workers.jmx +++ b/tests/performance/tests/upscale_workers.jmx @@ -16,7 +16,7 @@ cnn_url - https://s3.amazonaws.com/model-server/models/squeezenet_v1.1/squeezenet_v1.1.model + https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar = The url from where to fetch noop model from diff --git a/tests/performance/tests/upscale_workers.yaml b/tests/performance/tests/upscale_workers.yaml index 535c78911..862c939ed 100644 --- a/tests/performance/tests/upscale_workers.yaml +++ b/tests/performance/tests/upscale_workers.yaml @@ -2,12 +2,12 @@ execution: - concurrency: 3 ramp-up: 5s - iterations: 5 - scenario: Inference + iterations: 20 + scenario: Upscale scenarios: - Inference: - script: concurrent_upscale_workers.jmx + Upscale: + script: upscale_workers.jmx modules: server_local_monitoring: From 56daa44f1fe18fb73a3e84e4cf9b8d45031c57dc Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 17 Jun 2020 22:56:50 +0530 Subject: [PATCH 087/329] change python3 --- tests/performance/run_perfomance_suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/run_perfomance_suite.py b/tests/performance/run_perfomance_suite.py index 1c4fc6331..6a3420054 100755 --- a/tests/performance/run_perfomance_suite.py +++ b/tests/performance/run_perfomance_suite.py @@ -183,7 +183,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if monitoring_server: - stop_monitoring_server = "python3 {}/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: From 76ff3de22fca6b8b1692c75b3bd30214cd618885 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 18 Jun 2020 14:09:55 +0530 Subject: [PATCH 088/329] Updated init py and readme --- tests/performance/README.md | 3 +- tests/performance/metrics/__init__.py | 62 +++++++++++---------------- 2 files changed, 27 insertions(+), 38 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index a4003de3f..adf9c240f 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -104,8 +104,7 @@ Metrics can be monitored in two ways: If your MMS server is hosted on different machine, you will be using this method. Before running the test case you have to start a [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will be communicating with Taurus test client over sockets. The address and port(default=9009) of the monitoring script should be specified in test case yaml. - - **Note**: For available metrics check AVAILABLE_METRICS in the script [metric/__init__.py](metrics/__init__.py). + **Note**: While running Test suite runner script, no need to manually start the monitoring server. The scripts optionally but by default starts and stops it in setup and teardown. To start monitoring server run commands below: diff --git a/tests/performance/metrics/__init__.py b/tests/performance/metrics/__init__.py index 4d8b4d690..db4d4a7f8 100644 --- a/tests/performance/metrics/__init__.py +++ b/tests/performance/metrics/__init__.py @@ -33,25 +33,23 @@ class ProcessType(Enum): } process_metrics = { - 'cpu_percent': lambda p: p.get('cpu_percent', 0), - - 'memory_percent': lambda p: p.get('memory_percent', 0), - - 'cpu_user_time': lambda p: getattr(p.get('cpu_times', {}), 'user', 0), - 'cpu_system_time': lambda p: getattr(p.get('cpu_times', {}), 'system', 0), - 'cpu_iowait_time': lambda p: getattr(p.get('cpu_times', {}), 'iowait', 0), - - 'memory_rss': lambda p: getattr(p.get('memory_info', {}), 'rss', 0), - 'memory_vms': lambda p: getattr(p.get('memory_info', {}), 'vms', 0), - - 'io_read_count': lambda p: getattr(p.get('io_counters', {}), 'read_count', 0), - 'io_write_count': lambda p: getattr(p.get('io_counters', {}), 'write_count', 0), - 'io_read_bytes': lambda p: getattr(p.get('io_counters', {}), 'read_bytes', 0), - 'io_write_bytes': lambda p: getattr(p.get('io_counters', {}), 'write_bytes', 0), - - 'file_descriptors': lambda p: p.get('num_fds', 0), - - 'threads': lambda p: p.get('num_threads', 0) + # cpu + 'cpu_percent': lambda p : p.get('cpu_percent', 0), + 'cpu_user_time': lambda p : getattr(p.get('cpu_times', {}), 'user', 0), + 'cpu_system_time': lambda p : getattr(p.get('cpu_times', {}), 'system', 0), + 'cpu_iowait_time': lambda p : getattr(p.get('cpu_times', {}), 'iowait', 0), + # memory + 'memory_percent': lambda p : p.get('memory_percent', 0), + 'memory_rss': lambda p : getattr(p.get('memory_info', {}), 'rss', 0), + 'memory_vms': lambda p : getattr(p.get('memory_info', {}), 'vms', 0), + # io + 'io_read_count': lambda p : getattr(p.get('io_counters', {}), 'read_count', 0), + 'io_write_count': lambda p : getattr(p.get('io_counters', {}), 'write_count', 0), + 'io_read_bytes': lambda p : getattr(p.get('io_counters', {}), 'read_bytes', 0), + 'io_write_bytes': lambda p : getattr(p.get('io_counters', {}), 'write_bytes', 0), + 'file_descriptors': lambda p : p.get('num_fds', 0), + # processes + 'threads': lambda p : p.get('num_threads', 0) } system_metrics = { @@ -66,30 +64,25 @@ class ProcessType(Enum): } AVAILABLE_METRICS = list(system_metrics) + for metric in list(process_metrics): for ptype in list(ProcessType): if ptype == ProcessType.WORKER: + type = 'workers' for op in list(operators): - type = 'workers' - AVAILABLE_METRICS.append('{}_{}_{}'.format(op,type,metric)) + AVAILABLE_METRICS.append('{}_{}_{}'.format(op, type,metric)) elif ptype == ProcessType.FRONTEND: type = 'frontend' AVAILABLE_METRICS.append('{}_{}'.format(type, metric)) else: type = 'all' for op in list(operators): - type = 'all' - AVAILABLE_METRICS.append('{}_{}_{}'.format(op,type,metric)) + AVAILABLE_METRICS.append('{}_{}_{}'.format(op, type,metric)) def get_metrics(server_process, child_processes): """ Get Server processes specific metrics """ - - # TODO - make this modular may be a diff function for each metric - # TODO - allow users to add new metrics easily - # TODO - make sure available metric list is maintained - result = {} def update_metric(metric_name, type, stats): @@ -113,12 +106,11 @@ def update_metric(metric_name, type, stats): processes_stats.append({'type': ProcessType.WORKER, 'stats' : process.as_dict()}) ### PROCESS METRICS ### - for k in process_metrics: - - worker_stats = list(map(lambda x: x['stats'], filter(lambda x: x['type'] == ProcessType.WORKER, processes_stats))) - all_stats = list(map(lambda x: x['stats'], processes_stats)) - server_stats = list(map(lambda x: x['stats'], filter(lambda x: x['type'] == ProcessType.FRONTEND, processes_stats))) + worker_stats = list(map(lambda x: x['stats'], filter(lambda x: x['type'] == ProcessType.WORKER, processes_stats))) + all_stats = list(map(lambda x: x['stats'], processes_stats)) + server_stats = list(map(lambda x: x['stats'], filter(lambda x: x['type'] == ProcessType.FRONTEND, processes_stats))) + for k in process_metrics: update_metric(k, ProcessType.WORKER, list(map(process_metrics[k], worker_stats))) update_metric(k, ProcessType.ALL, list(map(process_metrics[k], all_stats))) update_metric(k, ProcessType.FRONTEND, list(map(process_metrics[k], server_stats))) @@ -130,13 +122,11 @@ def update_metric(metric_name, type, stats): ### SYSTEM METRICS ### result['system_disk_used'] = psutil.disk_usage('/').used - result['system_memory_percent'] = psutil.virtual_memory().percent - system_disk_io_counters = psutil.disk_io_counters() result['system_read_count'] = system_disk_io_counters.read_count result['system_write_count'] = system_disk_io_counters.write_count result['system_read_bytes'] = system_disk_io_counters.read_bytes result['system_write_bytes'] = system_disk_io_counters.write_bytes - return result + return result \ No newline at end of file From 2cf93930cd720398d3442a252a6238f48df7ab46 Mon Sep 17 00:00:00 2001 From: Mahesh Ambule <15611578+maheshambule@users.noreply.github.com> Date: Thu, 18 Jun 2020 14:51:11 +0530 Subject: [PATCH 089/329] Delete new_passfail.py --- tests/performance/new_passfail.py | 180 ------------------------------ 1 file changed, 180 deletions(-) delete mode 100644 tests/performance/new_passfail.py diff --git a/tests/performance/new_passfail.py b/tests/performance/new_passfail.py deleted file mode 100644 index d9ee91392..000000000 --- a/tests/performance/new_passfail.py +++ /dev/null @@ -1,180 +0,0 @@ -from bzt.modules.passfail import FailCriterion, PassFailStatus -from abc import abstractmethod - - -class FailCriterion1(object): - def __init__(self, config, owner): - self.owner = owner - self.config = config - self.agg_buffer = OrderedDict() - if not 'threshold' in config: - raise TaurusConfigError("Criteria string is malformed in its threshold part.") - self.percentage = str(config['threshold']).endswith('%') - if not 'subject' in config: - raise TaurusConfigError("Criteria string is malformed in its subject part.") - if config['subject'] == 'bytes': - self.threshold = get_bytes_count(config.get('threshold')) - else: - self.threshold = dehumanize_time(config.get('threshold')) - - self.get_value = self._get_field_functor(config['subject'], self.percentage) - self.window_logic = config.get('logic', 'for') - self.agg_logic = self._get_aggregator_functor(self.window_logic, config['subject']) - if not 'condition' in config: - raise TaurusConfigError("Criteria string is malformed in its condition part.") - self.condition = self._get_condition_functor(config.get('condition')) - - self.stop = config.get('stop', True) - self.fail = config.get('fail', True) - self.message = config.get('message', None) - self.window = dehumanize_time(config.get('timeframe', 0)) - self._start = sys.maxsize - self._end = 0 - self.is_candidate = False - self.is_triggered = False - - self.get_threshold = config.get('get_threshold', None) - if self.get_threshold: - self.threshold == 'init' - self.prev_data_str = None - - def __repr__(self): - if self.is_triggered: - if self.fail: - state = "Failed" - else: - state = "Notice" - else: - state = "Alert" - - if self.message is not None: - return "%s: %s" % (state, self.message) - else: - data = (state, - self.config['subject'], - self.config['condition'], - self.config['threshold'], - self.window_logic, - self.get_counting()) - return "%s: %s%s%s %s %d sec" % data - - def process_criteria_logic(self, tstmp, get_value): - value = self.agg_logic(tstmp, get_value) - if self.threshold == 'init': - self.threshold = value - state = self.condition(value, self.threshold) - - if self.window_logic == 'for': - if state: - self._start = min(self._start, tstmp) - self._end = tstmp - else: - self._start = sys.maxsize - self._end = 0 - - if self.get_counting() >= self.window: - self.trigger(value) - elif self.window_logic == 'within' and state: - self._start = tstmp - self.window + 1 - self._end = tstmp - self.trigger(value) - elif self.window_logic == 'over' and state: - min_buffer_tstmp = min(self.agg_buffer.keys()) - self._start = min_buffer_tstmp - self._end = tstmp - if self.get_counting() >= self.window: - self.trigger(value) - - self.owner.log.debug("%s %s: %s", tstmp, self, state) - - def trigger(self, value): - print("triggered value is "+str(value)) - if self.get_threshold: - self.threshold = value - data_search = [self.config['subject'], self.config['condition'], - self.config['threshold'], self.window_logic] - data_search_str = ','.join(data_search) - data_str = ','.join(data_search + [value]) - replaced = False - - with open(self.get_threshold, "r+") as f: - lines = f.readlines() - for i, line in enumerate(lines): - if line.startswith(data_search_str): - lines[i] = data_str - replaced = True - break - - if not replaced: - lines.append(data_str) - - f.seek(0) - f.writelines(lines) - - else: - if not self.is_triggered: - self.owner.log.warning("%s", self) - self.is_triggered = True - - def check(self): - """ - Interrupt the execution if desired condition occured - :raise AutomatedShutdown: - """ - if self.stop and self.is_triggered: - if self.fail: - self.owner.log.info("Pass/Fail criterion triggered shutdown: %s", self) - raise AutomatedShutdown("%s" % self) - else: - return True - return False - - @abstractmethod - def _get_field_functor(self, subject, percentage): - pass - - def _get_condition_functor(self, cond): - if cond == '=' or cond == '==': - return lambda x, y: x == y - elif cond == '>': - return lambda x, y: x > y - elif cond == '>=': - return lambda x, y: x >= y - elif cond == '<': - return lambda x, y: x < y - elif cond == '<=': - return lambda x, y: x <= y - else: - raise TaurusConfigError("Unsupported fail criteria condition: %s" % cond) - - def _get_aggregator_functor(self, logic, _subject): - if logic == 'for': - return lambda tstmp, value: value - elif logic in ('within', 'over'): - return self._within_aggregator_avg # FIXME: having simple average for percented values is a bit wrong - else: - raise TaurusConfigError("Unsupported window logic: %s" % logic) - - def _get_windowed_points(self, tstmp, value): - self.agg_buffer[tstmp] = value - keys = list(self.agg_buffer.keys()) - for tstmp_old in keys: - if tstmp_old <= tstmp - self.window: - del self.agg_buffer[tstmp_old] - continue - break - - return viewvalues(self.agg_buffer) - - def _within_aggregator_sum(self, tstmp, value): - return sum(self._get_windowed_points(tstmp, value)) - - def _within_aggregator_avg(self, tstmp, value): - points = self._get_windowed_points(tstmp, value) - return sum(points) / len(points) - - def get_counting(self): - return self._end - self._start + 1 - - -FailCriterion = FailCriterion1 From 2b53bb1bf50e11249f2a419b11fae58ffa59ad4d Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 18 Jun 2020 14:51:27 +0530 Subject: [PATCH 090/329] pylint corrections --- tests/performance/metrics/__init__.py | 71 ++++++++++++++------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/tests/performance/metrics/__init__.py b/tests/performance/metrics/__init__.py index db4d4a7f8..cb8752681 100644 --- a/tests/performance/metrics/__init__.py +++ b/tests/performance/metrics/__init__.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +""" Customised system and mms process metrics for monitoring and pass-fail criteria in taurus""" # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # Licensed under the Apache License, Version 2.0 (the "License"). @@ -10,19 +11,16 @@ # express or implied. See the License for the specific language governing # permissions and limitations under the License. -import psutil from statistics import mean from enum import Enum +import psutil -""" -Custom metrics -""" -# pylint: disable=redefined-builtin class ProcessType(Enum): + """ Type of MMS processes to compute metrics on """ FRONTEND = 1 WORKER = 2 - ALL = 3 + ALL = 3 operators = { @@ -34,22 +32,22 @@ class ProcessType(Enum): process_metrics = { # cpu - 'cpu_percent': lambda p : p.get('cpu_percent', 0), - 'cpu_user_time': lambda p : getattr(p.get('cpu_times', {}), 'user', 0), - 'cpu_system_time': lambda p : getattr(p.get('cpu_times', {}), 'system', 0), - 'cpu_iowait_time': lambda p : getattr(p.get('cpu_times', {}), 'iowait', 0), + 'cpu_percent': lambda p: p.get('cpu_percent', 0), + 'cpu_user_time': lambda p: getattr(p.get('cpu_times', {}), 'user', 0), + 'cpu_system_time': lambda p: getattr(p.get('cpu_times', {}), 'system', 0), + 'cpu_iowait_time': lambda p: getattr(p.get('cpu_times', {}), 'iowait', 0), # memory - 'memory_percent': lambda p : p.get('memory_percent', 0), - 'memory_rss': lambda p : getattr(p.get('memory_info', {}), 'rss', 0), - 'memory_vms': lambda p : getattr(p.get('memory_info', {}), 'vms', 0), + 'memory_percent': lambda p: p.get('memory_percent', 0), + 'memory_rss': lambda p: getattr(p.get('memory_info', {}), 'rss', 0), + 'memory_vms': lambda p: getattr(p.get('memory_info', {}), 'vms', 0), # io - 'io_read_count': lambda p : getattr(p.get('io_counters', {}), 'read_count', 0), - 'io_write_count': lambda p : getattr(p.get('io_counters', {}), 'write_count', 0), - 'io_read_bytes': lambda p : getattr(p.get('io_counters', {}), 'read_bytes', 0), - 'io_write_bytes': lambda p : getattr(p.get('io_counters', {}), 'write_bytes', 0), - 'file_descriptors': lambda p : p.get('num_fds', 0), + 'io_read_count': lambda p: getattr(p.get('io_counters', {}), 'read_count', 0), + 'io_write_count': lambda p: getattr(p.get('io_counters', {}), 'write_count', 0), + 'io_read_bytes': lambda p: getattr(p.get('io_counters', {}), 'read_bytes', 0), + 'io_write_bytes': lambda p: getattr(p.get('io_counters', {}), 'write_bytes', 0), + 'file_descriptors': lambda p: p.get('num_fds', 0), # processes - 'threads': lambda p : p.get('num_threads', 0) + 'threads': lambda p: p.get('num_threads', 0) } system_metrics = { @@ -68,16 +66,16 @@ class ProcessType(Enum): for metric in list(process_metrics): for ptype in list(ProcessType): if ptype == ProcessType.WORKER: - type = 'workers' + PNAME = 'workers' for op in list(operators): - AVAILABLE_METRICS.append('{}_{}_{}'.format(op, type,metric)) + AVAILABLE_METRICS.append('{}_{}_{}'.format(op, PNAME, metric)) elif ptype == ProcessType.FRONTEND: - type = 'frontend' - AVAILABLE_METRICS.append('{}_{}'.format(type, metric)) + PNAME = 'frontend' + AVAILABLE_METRICS.append('{}_{}'.format(PNAME, metric)) else: - type = 'all' + PNAME = 'all' for op in list(operators): - AVAILABLE_METRICS.append('{}_{}_{}'.format(op, type,metric)) + AVAILABLE_METRICS.append('{}_{}_{}'.format(op, PNAME, metric)) def get_metrics(server_process, child_processes): @@ -85,19 +83,20 @@ def get_metrics(server_process, child_processes): """ result = {} - def update_metric(metric_name, type, stats): + def update_metric(metric_name, proc_type, stats): stats = stats if stats else [0] - if type == ProcessType.WORKER: - type = 'workers' - elif type == ProcessType.FRONTEND: - result['frontend_' + metric_name] = stats[0] + if proc_type == ProcessType.WORKER: + proc_name = 'workers' + elif proc_type == ProcessType.FRONTEND: + proc_name = 'frontend' + result[proc_name+ '_' + metric_name] = stats[0] return else: - type='all' + proc_name = 'all' for op_name in operators: - result['{}_{}_{}'.format(op_name, type, metric_name)] = operators[op_name](stats) + result['{}_{}_{}'.format(op_name, proc_name, metric_name)] = operators[op_name](stats) # as_dict() gets all stats in one shot processes_stats = [] @@ -106,9 +105,11 @@ def update_metric(metric_name, type, stats): processes_stats.append({'type': ProcessType.WORKER, 'stats' : process.as_dict()}) ### PROCESS METRICS ### - worker_stats = list(map(lambda x: x['stats'], filter(lambda x: x['type'] == ProcessType.WORKER, processes_stats))) + worker_stats = list(map(lambda x: x['stats'], \ + filter(lambda x: x['type'] == ProcessType.WORKER, processes_stats))) + server_stats = list(map(lambda x: x['stats'], \ + filter(lambda x: x['type'] == ProcessType.FRONTEND, processes_stats))) all_stats = list(map(lambda x: x['stats'], processes_stats)) - server_stats = list(map(lambda x: x['stats'], filter(lambda x: x['type'] == ProcessType.FRONTEND, processes_stats))) for k in process_metrics: update_metric(k, ProcessType.WORKER, list(map(process_metrics[k], worker_stats))) @@ -129,4 +130,4 @@ def update_metric(metric_name, type, stats): result['system_read_bytes'] = system_disk_io_counters.read_bytes result['system_write_bytes'] = system_disk_io_counters.write_bytes - return result \ No newline at end of file + return result From 75480be01f82a5eda08de3cdd6f5e8409a672f27 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 18 Jun 2020 14:51:45 +0530 Subject: [PATCH 091/329] removed inti2 --- tests/performance/metrics/__init2__.py | 66 -------------------------- 1 file changed, 66 deletions(-) delete mode 100644 tests/performance/metrics/__init2__.py diff --git a/tests/performance/metrics/__init2__.py b/tests/performance/metrics/__init2__.py deleted file mode 100644 index ea2a694bd..000000000 --- a/tests/performance/metrics/__init2__.py +++ /dev/null @@ -1,66 +0,0 @@ -import psutil -import pandas as pd - -SEPARATOR = '~' -# INPUTS = ['min~cpu_percent', 'max~cpu_times~system', 'sum~memory_info~rss', 'mean~io_counters~read_count', 'sum~num_fds', 'sum~num_threads'] -INPUTS = ['max~cpu_times~system', 'sum~num_threads'] - -def input_parser(input): - tokens = input.split(SEPARATOR) - operator = tokens[0] - metric_type = tokens[1] - try: - metric_attr = tokens[2] - except IndexError: - metric_attr = None - - metric_name = metric_type if metric_attr is None else metric_type + '_' + metric_attr - return operator, metric_type, metric_attr, metric_name - - -info = { - 'name': [], - 'ptype': [], - 'pid': [], - 'parent_pid': [] -} - -def parse_process_tree(process, level=0): - stat_dict = process.as_dict() - - info['name'].append(stat_dict['name']) - info['ptype'].append(level) - info['pid'].append(stat_dict['pid']) - info['parent_pid'].append(stat_dict['ppid']) - - for input in INPUTS: - operator, metric_type, metric_attr, metric_name = input_parser(input) - info[metric_name] = info[metric_name] if info.get(metric_name) else [] - - if metric_attr is None: - info[metric_name].append(stat_dict.get(metric_type, 0)) - else: - info[metric_name].append(getattr(stat_dict.get(metric_type, {}), metric_attr, 0)) - - for child in process.children(): - parse_process_tree(child, level+1) - - -server_process = psutil.Process(8182) -parse_process_tree(server_process) - -df = pd.DataFrame(info) -print("---------------------------Data Frame Object---------------------------") -print(df) -print() -result = {} -for input in INPUTS: - operator, metric_type, metric_attr, metric_name = input_parser(input) - operator_fn = getattr(df, operator) - res = operator_fn() - print("-------------- "+operator+" : "+metric_name+" --------------") - print(res) - print() - result[input] = res.get(metric_name) -print("---------------------------------------------------") -print(result) \ No newline at end of file From 383fd62c0f1396e0557834ce9d9701a0dd20b5cb Mon Sep 17 00:00:00 2001 From: Mahesh Ambule <15611578+maheshambule@users.noreply.github.com> Date: Thu, 18 Jun 2020 14:52:03 +0530 Subject: [PATCH 092/329] Delete __init2__.py --- tests/performance/metrics/__init2__.py | 66 -------------------------- 1 file changed, 66 deletions(-) delete mode 100644 tests/performance/metrics/__init2__.py diff --git a/tests/performance/metrics/__init2__.py b/tests/performance/metrics/__init2__.py deleted file mode 100644 index ea2a694bd..000000000 --- a/tests/performance/metrics/__init2__.py +++ /dev/null @@ -1,66 +0,0 @@ -import psutil -import pandas as pd - -SEPARATOR = '~' -# INPUTS = ['min~cpu_percent', 'max~cpu_times~system', 'sum~memory_info~rss', 'mean~io_counters~read_count', 'sum~num_fds', 'sum~num_threads'] -INPUTS = ['max~cpu_times~system', 'sum~num_threads'] - -def input_parser(input): - tokens = input.split(SEPARATOR) - operator = tokens[0] - metric_type = tokens[1] - try: - metric_attr = tokens[2] - except IndexError: - metric_attr = None - - metric_name = metric_type if metric_attr is None else metric_type + '_' + metric_attr - return operator, metric_type, metric_attr, metric_name - - -info = { - 'name': [], - 'ptype': [], - 'pid': [], - 'parent_pid': [] -} - -def parse_process_tree(process, level=0): - stat_dict = process.as_dict() - - info['name'].append(stat_dict['name']) - info['ptype'].append(level) - info['pid'].append(stat_dict['pid']) - info['parent_pid'].append(stat_dict['ppid']) - - for input in INPUTS: - operator, metric_type, metric_attr, metric_name = input_parser(input) - info[metric_name] = info[metric_name] if info.get(metric_name) else [] - - if metric_attr is None: - info[metric_name].append(stat_dict.get(metric_type, 0)) - else: - info[metric_name].append(getattr(stat_dict.get(metric_type, {}), metric_attr, 0)) - - for child in process.children(): - parse_process_tree(child, level+1) - - -server_process = psutil.Process(8182) -parse_process_tree(server_process) - -df = pd.DataFrame(info) -print("---------------------------Data Frame Object---------------------------") -print(df) -print() -result = {} -for input in INPUTS: - operator, metric_type, metric_attr, metric_name = input_parser(input) - operator_fn = getattr(df, operator) - res = operator_fn() - print("-------------- "+operator+" : "+metric_name+" --------------") - print(res) - print() - result[input] = res.get(metric_name) -print("---------------------------------------------------") -print(result) \ No newline at end of file From b06204fb5c387d7278733d65340c223a7c66c8d2 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 18 Jun 2020 19:23:02 +0530 Subject: [PATCH 093/329] refactor agents --- tests/performance/README.md | 4 ++-- tests/performance/agents/__init__.py | 0 .../performance/{ => agents}/metrics_collector.py | 0 .../{ => agents}/metrics_monitoring_server.py | 0 .../{ => agents}/metrics_monitoring_taurus.py | 0 ...erfomance_suite.py => run_performance_suite.py} | 14 ++++++++++---- 6 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 tests/performance/agents/__init__.py rename tests/performance/{ => agents}/metrics_collector.py (100%) rename tests/performance/{ => agents}/metrics_monitoring_server.py (100%) rename tests/performance/{ => agents}/metrics_monitoring_taurus.py (100%) rename tests/performance/{run_perfomance_suite.py => run_performance_suite.py} (93%) diff --git a/tests/performance/README.md b/tests/performance/README.md index adf9c240f..f80ed6b28 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -4,7 +4,7 @@ This test suite helps in running the load tests and monitoring the process and s We use Taurus with JMeter as a test automation framework to run the test cases and metrics monitoring. ## How to run the test suite -To run the test suite you need to execute the [run_perfomance_suite.py](run_perfomance_suite.py). You will have to provide the artifacts-dir path to store the test case results. +To run the test suite you need to execute the [run_performance_suite.py](run_performance_suite.py). You will have to provide the artifacts-dir path to store the test case results. You can specify test cases to be run by providing 'test-dir' with default as '$MMS_HOME/tests/performance/tests' and 'pattern' with default as '*.yaml'. For other options use '--help' option. Script does the following: @@ -45,7 +45,7 @@ Refer the [link](https://gettaurus.org/docs/Installation/) for more details on i # jpeg download command for quick reference. Set input_filepath in global_config.yaml #curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg - python run_perfomance_suite.py --artifacts-dir='' --pattern='*criteria*.yaml' + python run_performance_suite.py --artifacts-dir='' --pattern='*criteria*.yaml' ``` ### C. Understanding the test suite artifacts and reports diff --git a/tests/performance/agents/__init__.py b/tests/performance/agents/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/performance/metrics_collector.py b/tests/performance/agents/metrics_collector.py similarity index 100% rename from tests/performance/metrics_collector.py rename to tests/performance/agents/metrics_collector.py diff --git a/tests/performance/metrics_monitoring_server.py b/tests/performance/agents/metrics_monitoring_server.py similarity index 100% rename from tests/performance/metrics_monitoring_server.py rename to tests/performance/agents/metrics_monitoring_server.py diff --git a/tests/performance/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_taurus.py similarity index 100% rename from tests/performance/metrics_monitoring_taurus.py rename to tests/performance/agents/metrics_monitoring_taurus.py diff --git a/tests/performance/run_perfomance_suite.py b/tests/performance/run_performance_suite.py similarity index 93% rename from tests/performance/run_perfomance_suite.py rename to tests/performance/run_performance_suite.py index 6a3420054..cf8a12641 100755 --- a/tests/performance/run_perfomance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -26,6 +26,11 @@ import subprocess import yaml import requests +import csv +import psutil +import pandas as pd +import boto3 +import configuration from subprocess import PIPE, STDOUT from tqdm import tqdm from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure @@ -33,6 +38,7 @@ logger = logging.getLogger(__name__) code = 0 +S3_BUCKET = configuration.get('suite', 's3_bucket') class Timer(object): def __init__(self, description): @@ -109,14 +115,14 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser raise Exception("Server is not running. Pinged url {}. Exiting..".format(server_ping_url)) if monitoring_server: - start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) + start_monitoring_server = "python3 {}/agents/metrics_monitoring_server.py --start".format(path) code, output = run_process(start_monitoring_server, wait=False) time.sleep(2) # TODO - Add check if server started junit_xml = JUnitXml() - pre_command = 'export PYTHONPATH={}:$PYTHONPATH; '.format(str(path)) + pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(path)) test_yamls = get_test_yamls(test_dir, pattern) for test_file in tqdm(test_yamls, desc="Test Suites"): @@ -183,7 +189,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if monitoring_server: - stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python3 {}/agents/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: @@ -192,7 +198,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser if __name__ == "__main__": logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) - parser = argparse.ArgumentParser(prog='run_perfomance_suite.py', description='Performance Test Suite Runner') + parser = argparse.ArgumentParser(prog='run_performance_suite.py', description='Performance Test Suite Runner') parser.add_argument('-a', '--artifacts-dir', nargs=1, type=str, dest='artifacts', required=True, help='A artifacts directory') From 7451520369af90994cc82939fc917798dc361fbf Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 18 Jun 2020 19:24:34 +0530 Subject: [PATCH 094/329] refactor agents --- tests/performance/run_performance_suite.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index cf8a12641..21cd39ab5 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -26,10 +26,6 @@ import subprocess import yaml import requests -import csv -import psutil -import pandas as pd -import boto3 import configuration from subprocess import PIPE, STDOUT from tqdm import tqdm @@ -38,7 +34,6 @@ logger = logging.getLogger(__name__) code = 0 -S3_BUCKET = configuration.get('suite', 's3_bucket') class Timer(object): def __init__(self, description): From cf50505add7cb0f797ae43e53513b0e9d94923a6 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 18 Jun 2020 19:28:29 +0530 Subject: [PATCH 095/329] python3 --- tests/performance/run_performance_suite.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 21cd39ab5..74cd77a1c 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -110,7 +110,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser raise Exception("Server is not running. Pinged url {}. Exiting..".format(server_ping_url)) if monitoring_server: - start_monitoring_server = "python3 {}/agents/metrics_monitoring_server.py --start".format(path) + start_monitoring_server = "python {}/agents/metrics_monitoring_server.py --start".format(path) code, output = run_process(start_monitoring_server, wait=False) time.sleep(2) @@ -184,7 +184,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if monitoring_server: - stop_monitoring_server = "python3 {}/agents/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python {}/agents/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: From a1259a603c756eba13813779eeb6575832cd8527 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 18 Jun 2020 19:28:30 +0530 Subject: [PATCH 096/329] downscale workers --- tests/performance/tests/common/setup.jmx | 100 +++++++++++++++++ tests/performance/tests/common/teardown.jmx | 75 +++++++++++++ tests/performance/tests/downscale_workers.jmx | 103 +----------------- .../performance/tests/downscale_workers.yaml | 39 ++++--- 4 files changed, 199 insertions(+), 118 deletions(-) create mode 100644 tests/performance/tests/common/setup.jmx create mode 100644 tests/performance/tests/common/teardown.jmx diff --git a/tests/performance/tests/common/setup.jmx b/tests/performance/tests/common/setup.jmx new file mode 100644 index 000000000..fcddf3495 --- /dev/null +++ b/tests/performance/tests/common/setup.jmx @@ -0,0 +1,100 @@ + + + + + + false + true + false + + + + + + + + + + cnn_url + https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + = + The url from where to fetch noop model from + + + model + squeezenet_v1.1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${cnn_url} + POST + true + false + true + false + + + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=4 + PUT + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/common/teardown.jmx b/tests/performance/tests/common/teardown.jmx new file mode 100644 index 000000000..b769d3aae --- /dev/null +++ b/tests/performance/tests/common/teardown.jmx @@ -0,0 +1,75 @@ + + + + + + false + true + false + + + + + + + + + + model + squeezenet_v1.1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model} + DELETE + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/downscale_workers.jmx b/tests/performance/tests/downscale_workers.jmx index 5ea8dedac..609cc4832 100644 --- a/tests/performance/tests/downscale_workers.jmx +++ b/tests/performance/tests/downscale_workers.jmx @@ -14,18 +14,6 @@ - - cnn_url - https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - = - The url from where to fetch noop model from - - - scale_up_workers - 4 - = - The workers to scale No op model to - scale_down_workers 2 @@ -54,60 +42,7 @@ - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_up_workers} - PUT - true - false - true - false - - - - - - - + continue false @@ -121,7 +56,7 @@ true - + @@ -141,40 +76,6 @@ - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=0 - DELETE - true - false - true - false - - - - - - diff --git a/tests/performance/tests/downscale_workers.yaml b/tests/performance/tests/downscale_workers.yaml index da03deb70..4c8438606 100644 --- a/tests/performance/tests/downscale_workers.yaml +++ b/tests/performance/tests/downscale_workers.yaml @@ -1,40 +1,45 @@ --- execution: -- concurrency: 3 - ramp-up: 5s - iterations: 20 +- concurrency: 1 + ramp-up: 0s + iterations: 1 scenario: Downscale scenarios: Downscale: script: downscale_workers.jmx -modules: - server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. - new_passfail: - class: new_passfail.PassFailStatus - - - services: + - module: shellexec + prepare: + - bzt tests/common/setup.jmx tests/common/global_config.yaml + # startup: + # - echo 1 > tmp_guy/bla.txt + # - echo 2 >> tmp_guy/bla.txt + # shutdown: + # - echo "shutdown time is $(date)" >> tmp/bla.txt + post-process: + - bzt tests/common/teardown.jmx tests/common/global_config.yaml - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - interval: 1s metrics: - - cpu - - disk-space - - mem - total_workers +modules: + server_local_monitoring: + # metrics_monitoring_taurus and dependencies should be in python path + class : metrics_monitoring_taurus.Monitor # monitoring class. + new_passfail: + class: new_passfail.PassFailStatus + reporting: - module: passfail criteria: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers - condition: '>' + condition: '<' threshold: 2 - timeframe: 5s + timeframe: 1s stop : true fail : true \ No newline at end of file From 6e2f9d284212a60a81923c6425de50a3d2d2e386 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 18 Jun 2020 19:31:07 +0530 Subject: [PATCH 097/329] Remove unused code --- tests/performance/tests/downscale_workers.yaml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/performance/tests/downscale_workers.yaml b/tests/performance/tests/downscale_workers.yaml index 4c8438606..d406a2aad 100644 --- a/tests/performance/tests/downscale_workers.yaml +++ b/tests/performance/tests/downscale_workers.yaml @@ -13,22 +13,16 @@ services: - module: shellexec prepare: - bzt tests/common/setup.jmx tests/common/global_config.yaml - # startup: - # - echo 1 > tmp_guy/bla.txt - # - echo 2 >> tmp_guy/bla.txt - # shutdown: - # - echo "shutdown time is $(date)" >> tmp/bla.txt post-process: - bzt tests/common/teardown.jmx tests/common/global_config.yaml - - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + - module: server_local_monitoring + ServerLocalClient: - interval: 1s metrics: - total_workers modules: server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path class : metrics_monitoring_taurus.Monitor # monitoring class. new_passfail: class: new_passfail.PassFailStatus From 6511a9e02aa521af8623ac892ceda505e5f505e2 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 18 Jun 2020 19:54:47 +0530 Subject: [PATCH 098/329] compare --- tests/performance/config.ini | 6 +- tests/performance/run_performance_suite.py | 210 +++++++++++++++++- .../inference_server_monitoring_criteria.yaml | 1 + ...ence_taurus_local_monitoring_criteria.yaml | 2 + tests/performance/tests/upscale_workers.yaml | 4 +- 5 files changed, 219 insertions(+), 4 deletions(-) diff --git a/tests/performance/config.ini b/tests/performance/config.ini index 27bc4c37b..5b391b46e 100644 --- a/tests/performance/config.ini +++ b/tests/performance/config.ini @@ -3,4 +3,8 @@ pid_file = model_server.pid [monitoring] HOST = -PORT = 9009 \ No newline at end of file +PORT = 9009 + +[suite] +diff_percent = 35 +s3_bucket = regression-reports-123 \ No newline at end of file diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 74cd77a1c..3be6cddad 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -27,6 +27,9 @@ import yaml import requests import configuration +import csv +import pandas as pd +import boto3 from subprocess import PIPE, STDOUT from tqdm import tqdm from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure @@ -34,6 +37,7 @@ logger = logging.getLogger(__name__) code = 0 +S3_BUCKET = configuration.get('suite', 's3_bucket') class Timer(object): def __init__(self, description): @@ -93,7 +97,175 @@ def get_options(artifacts_dir, jmeter_path=None): return options_str -def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_server): +def get_folder_names(dir1): + return [di for di in os.listdir(dir1) if os.path.isdir(os.path.join(dir1, di))] + + +def get_latest(names, env_id): + max_ts = 0 + latest_run = '' + for run_name in names: + run_name_list = run_name.split('_') + if env_id == run_name_list[0]: + if int(run_name_list[2]) > max_ts: + max_ts = int(run_name_list[2]) + latest_run = run_name + + return latest_run + + +def get_latest_dir(dir1, env_id): + names= get_folder_names(dir1) + latest_run= get_latest(names, env_id) + return os.path.join(dir1, latest_run) + + +def download_s3_files(env_id, tgt_path, bucket_name=S3_BUCKET): + s3 = boto3.resource('s3') + bucket = s3.Bucket(bucket_name) + result = bucket.meta.client.list_objects(Bucket=bucket.name, + Delimiter='/') + + run_names = [] + for o in result.get('CommonPrefixes'): + run_names.append(o.get('Prefix')[:-1]) + + latest_run = get_latest(run_names, env_id) + if not latest_run: + logger.info("No run found for env_id {}".format(env_id)) + return False + + if not os.path.exists(tgt_path): + os.makedirs(tgt_path) + + tgt_path = "{}/{}".format(tgt_path, latest_run) + run_process("aws s3 cp s3://{}/{} {} --recursive".format(bucket.name, latest_run, tgt_path)) + + return tgt_path + + +def compare_artifacts(dir1, dir2, out_dir, diff_percent=None): + if not diff_percent: + diff_percent = float(configuration.get('suite', 'diff_percent', 30)) + + dir1, dir2 = dir1.strip(), dir2.strip() + if not os.path.exists(dir1): + logger.info("The path {} does not exit".format(dir1)) + return False + + if not os.path.exists(dir2): + logger.info("The path {} does not exit".format(dir2)) + return False + + over_all_pass = True + sub_dirs_1 = list([x[0].rsplit('/',1)[1] for x in os.walk(dir1) if x[0] != dir1]) + sub_dirs_2 = list([x[0].rsplit('/',1)[1] for x in os.walk(dir2) if x[0] != dir2]) + + aggregates = ["mean", "max", "min"] + header = ["test_suite", "metric", "run1", "run2", "percentage_diff", "result"] + rows = [header] + for sub_dir1 in sub_dirs_1: + if sub_dir1 in sub_dirs_2: + + def get_metrics_diff(dir1): + diff_percents1 = [] + metrics1 =[] + test_yaml1 = "{}/{}/{}.yaml".format(dir1, sub_dir1, sub_dir1) + with open(test_yaml1) as test_yaml1: + test_yaml1 = yaml.safe_load(test_yaml1) + for rep_section in test_yaml1['reporting']: + if rep_section.get('module', None) == 'passfail' : + for criterion in rep_section.get('criteria', []): + if isinstance(criterion, dict) and\ + 'monitoring' in criterion.get('class', ''): + subject = criterion["subject"] + metric = subject.rsplit('/',1) + metric = metric[1] if len(metric) == 1 else metric[0] + diff_percent = criterion.get("diff_percent", None) + + if diff_percent: + metrics1.append(metric) + diff_percents1.append(diff_percent) + + return metrics1, diff_percents1 + + metrics1, diff_percents1 = get_metrics_diff(dir1) + metrics2, diff_percents2 = get_metrics_diff(dir2) + + + metrics_file1 = glob.glob("{}/{}/SAlogs_*".format(dir1, sub_dir1)) + metrics_file2 = glob.glob("{}/{}/SAlogs_*".format(dir2, sub_dir1)) + + if not (metrics_file1 and metrics_file2): + metrics_file1 = glob.glob("{}/{}/local_*".format(dir1, sub_dir1)) + metrics_file2 = glob.glob("{}/{}/local_*".format(dir2, sub_dir1)) + + if not (metrics_file1 and metrics_file2): + rows.append([sub_dir1, "log_file", "NA", "NA", "NA", "pass"]) + continue + + metrics1 = pd.read_csv(metrics_file1[0]) + metrics2 = pd.read_csv(metrics_file2[0]) + + for col in metrics1.columns[1:]: + for agg_func in aggregates: + name = "{}_{}".format(agg_func, str(col)) + try: + val1 = getattr(metrics1[str(col)], agg_func)() + except TypeError: + val1 = "NULL" + print(col) + + if str(col) in metrics2: + try: + val2 = getattr(metrics2[str(col)], agg_func)() + except TypeError: + val2 = "NULL" + else: + val2 = "NA" + + if val1 == val2 and val1 == "NULL": + diff = "NULL" + pass_fail = "pass" + elif val1 == "NULL": + diff = val2 + pass_fail = "fail" + elif val2 == "NULL": + diff = val1 + pass_fail = "fail" + elif val2 == "NA": + diff = val2 + pass_fail = "pass" + else: + try: + if val2 != val1: + diff = (abs(val2 - val1) / ((val2 + val1)/2)) * 100 + pass_fail = "pass" if diff < diff_percent else "fail" + else: # special case of 0 + pass_fail = "pass" + + except Exception as e: + logger.info("error while calculating the diff {}".format(str(e))) + pass_fail = "fail" + + if over_all_pass: + over_all_pass = pass_fail == "pass" + rows.append([sub_dir1, name, val1, val2, diff, pass_fail]) + else: + rows.append([sub_dir1, "log_file", "A", "NA", "NA", "pass"]) + + out_path = "{}/comparison.csv".format(out_dir) + logger.info("Writing comparison report to log file ".format(out_path)) + with open(out_path, 'w', newline='') as csvfile: + csv_writer = csv.writer(csvfile, delimiter=',', + quotechar='|', quoting=csv.QUOTE_MINIMAL) + csv_writer.writerows(rows) + + return over_all_pass + + +def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, + monitoring_server, env_name, diff_percent): if os.path.exists(artifacts_dir): raise Exception("Artifacts dir '{}' already exists. Provide different one.".format(artifacts_dir)) @@ -120,6 +292,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(path)) test_yamls = get_test_yamls(test_dir, pattern) + out_report_rows = [] for test_file in tqdm(test_yamls, desc="Test Suites"): suite_name = os.path.basename(test_file).rsplit('.', 1)[0] with Timer("Test suite {} execution time".format(suite_name)) as t: @@ -134,6 +307,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser xunit_file = "{}/xunit.xml".format(suit_artifacts_dir) tests, failures, skipped, errors = 0, 0, 0, 0 err_txt = "" + out_report_row = [suite_name] ts = TestSuite(suite_name) if os.path.exists(xunit_file): xml = JUnitXml.fromfile(xunit_file) @@ -157,6 +331,8 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser # TODO Fix html report with system_err # tc.system_err = err_txt[:-4] ts.add_testcase(tc) + out_report_row.extend([name, result._tag if result else "passed"]) + out_report_rows.append(out_report_row) else: tc = TestCase(suite_name) if code: @@ -187,9 +363,33 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser stop_monitoring_server = "python {}/agents/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) + with open('final_report.csv', 'w', newline='') as csvfile: + csv_writer = csv.writer(csvfile, delimiter=',', + quotechar='|', quoting=csv.QUOTE_MINIMAL) + csv_writer.writerows(out_report_rows) + + path = pathlib.Path(artifacts_dir) + commit_id = subprocess.check_output('git rev-parse --short HEAD'.split()).decode("utf-8")[:-1] + new_name = "{}_{}_{}".format(env_name, commit_id, suite_start) + + # new_name_path = "{}/{}/".format(path.parent, new_name) + # os.rename(artifacts_dir, new_name_path) + + # latest_dir = get_latest_dir("{}/comp_data".format(artifacts_dir) , env_name) + # result = compare_artifacts(artifacts_dir, latest_dir, artifacts_dir, diff_percent=diff_percent) + + compare_dir = download_s3_files(env_name, "{}/comp_data".format(artifacts_dir)) + compare_result = True + if compare_dir: + compare_result = compare_artifacts(artifacts_dir, compare_dir, artifacts_dir, diff_percent=diff_percent) + + run_process("aws s3 cp {} s3://{}/{} --recursive".format(artifacts_dir, S3_BUCKET, new_name)) + if junit_xml.errors or junit_xml.failures or junit_xml.skipped: sys.exit(3) + if not compare_result: + sys.exit(4) if __name__ == "__main__": logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) @@ -197,6 +397,9 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser parser.add_argument('-a', '--artifacts-dir', nargs=1, type=str, dest='artifacts', required=True, help='A artifacts directory') + parser.add_argument('-e', '--env-name', nargs=1, type=bool, dest='env_name', default=[socket.gethostname()], + help='environment on which MMS server is running') + parser.add_argument('-d', '--test-dir', nargs=1, type=str, dest='test_dir', default=[None], help='A test dir') @@ -209,6 +412,9 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser parser.add_argument('-m', '--monitoring-server', nargs=1, type=bool, dest='monitoring_server', default=[True], help='Whether to start monitoring server') + parser.add_argument('-c', '--diff-percent', nargs=1, type=float, dest='diff_percent', default=[None], + help='Acceptable percentage difference between metrics from previous runs') + args = parser.parse_args() run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0], args.jmeter_path[0], - args.monitoring_server[0]) + args.monitoring_server[0], args.env_name[0], args.diff_percent[0]) diff --git a/tests/performance/tests/inference_server_monitoring_criteria.yaml b/tests/performance/tests/inference_server_monitoring_criteria.yaml index 61730a84d..46ea8fbeb 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_server_monitoring_criteria.yaml @@ -38,3 +38,4 @@ reporting: timeframe: 1s fail: true stop: true + diff_percent : 35 \ No newline at end of file diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml index c21a2cd18..4496a979a 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml @@ -22,6 +22,7 @@ services: - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - interval: 1s + logging : True metrics: - cpu - disk-space @@ -41,3 +42,4 @@ reporting: timeframe: 1s stop : true fail : true + diff_percent : 30 diff --git a/tests/performance/tests/upscale_workers.yaml b/tests/performance/tests/upscale_workers.yaml index 862c939ed..9f36a2557 100644 --- a/tests/performance/tests/upscale_workers.yaml +++ b/tests/performance/tests/upscale_workers.yaml @@ -22,6 +22,7 @@ services: - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - interval: 1s + logging : True metrics: - cpu - disk-space @@ -37,4 +38,5 @@ reporting: threshold: 4 timeframe: 1s stop : true - fail : true \ No newline at end of file + fail : true + diff_percent : 30 \ No newline at end of file From 365fe011d0453a899aa9ba12ee5b345841859825 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 18 Jun 2020 20:25:15 +0530 Subject: [PATCH 099/329] chnages --- tests/performance/README.md | 2 +- tests/performance/agents/metrics_collector.py | 2 +- tests/performance/agents/metrics_monitoring_server.py | 10 +++++----- tests/performance/agents/metrics_monitoring_taurus.py | 2 +- tests/performance/run_performance_suite.py | 5 ++--- tests/performance/utils/__init__.py | 0 tests/performance/{ => utils}/config.ini | 0 tests/performance/{ => utils}/configuration.py | 0 tests/performance/{ => utils}/process.py | 3 ++- 9 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 tests/performance/utils/__init__.py rename tests/performance/{ => utils}/config.ini (100%) rename tests/performance/{ => utils}/configuration.py (100%) rename tests/performance/{ => utils}/process.py (98%) diff --git a/tests/performance/README.md b/tests/performance/README.md index f80ed6b28..78a59df30 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -45,7 +45,7 @@ Refer the [link](https://gettaurus.org/docs/Installation/) for more details on i # jpeg download command for quick reference. Set input_filepath in global_config.yaml #curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg - python run_performance_suite.py --artifacts-dir='' --pattern='*criteria*.yaml' + python -m run_performance_suite --artifacts-dir='' --pattern='*criteria*.yaml' ``` ### C. Understanding the test suite artifacts and reports diff --git a/tests/performance/agents/metrics_collector.py b/tests/performance/agents/metrics_collector.py index 5fa7269e5..3270fd39c 100755 --- a/tests/performance/agents/metrics_collector.py +++ b/tests/performance/agents/metrics_collector.py @@ -26,7 +26,7 @@ logger = logging.getLogger(__name__) -from process import find_procs_by_name, get_process_pid_from_file, get_child_processes, \ +from utils.process import get_process_pid_from_file, get_child_processes, \ get_server_processes, get_server_pidfile from metrics import AVAILABLE_METRICS, get_metrics diff --git a/tests/performance/agents/metrics_monitoring_server.py b/tests/performance/agents/metrics_monitoring_server.py index 98fd73155..9d89e23ba 100644 --- a/tests/performance/agents/metrics_monitoring_server.py +++ b/tests/performance/agents/metrics_monitoring_server.py @@ -28,15 +28,15 @@ logger = logging.getLogger(__name__) from metrics_collector import start_metric_collection, stop_process, store_pid, check_is_running -from process import find_procs_by_name, get_process_pid_from_file, \ - get_child_processes, get_server_processes, get_server_pidfile -import configuration +from utils.process import get_process_pid_from_file, \ + get_server_processes, get_server_pidfile +from utils import configuration TMP_DIR = tempfile.gettempdir() METRICS_MON_SERVER_PID_FILE = "{}/.metrics_monitoring_server.pid".format(TMP_DIR) -HOST = str(configuration.get('monitoring','HOST')) -PORT = int(configuration.get('monitoring','PORT', 9009)) +HOST = str(configuration.get('monitoring', 'HOST')) +PORT = int(configuration.get('monitoring', 'PORT', 9009)) SOCKET_LIST = [] diff --git a/tests/performance/agents/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_taurus.py index ac402c7db..df4e1ac57 100644 --- a/tests/performance/agents/metrics_monitoring_taurus.py +++ b/tests/performance/agents/metrics_monitoring_taurus.py @@ -27,7 +27,7 @@ PY3 = sys.version_info[0] == 3 from metrics import get_metrics, AVAILABLE_METRICS as AVAILABLE_SERVER_METRICS -from process import get_process_pid_from_file, get_server_processes, \ +from utils.process import get_process_pid_from_file, get_server_processes, \ get_child_processes, get_server_pidfile diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 74cd77a1c..a6133cf02 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -26,7 +26,6 @@ import subprocess import yaml import requests -import configuration from subprocess import PIPE, STDOUT from tqdm import tqdm from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure @@ -110,7 +109,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser raise Exception("Server is not running. Pinged url {}. Exiting..".format(server_ping_url)) if monitoring_server: - start_monitoring_server = "python {}/agents/metrics_monitoring_server.py --start".format(path) + start_monitoring_server = "python3 {}/agents/metrics_monitoring_server.py --start".format(path) code, output = run_process(start_monitoring_server, wait=False) time.sleep(2) @@ -184,7 +183,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if monitoring_server: - stop_monitoring_server = "python {}/agents/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python3 {}/agents/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: diff --git a/tests/performance/utils/__init__.py b/tests/performance/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/performance/config.ini b/tests/performance/utils/config.ini similarity index 100% rename from tests/performance/config.ini rename to tests/performance/utils/config.ini diff --git a/tests/performance/configuration.py b/tests/performance/utils/configuration.py similarity index 100% rename from tests/performance/configuration.py rename to tests/performance/utils/configuration.py diff --git a/tests/performance/process.py b/tests/performance/utils/process.py similarity index 98% rename from tests/performance/process.py rename to tests/performance/utils/process.py index c8f84bfba..13da1973a 100644 --- a/tests/performance/process.py +++ b/tests/performance/utils/process.py @@ -18,7 +18,8 @@ import os import psutil import tempfile -import configuration +from utils import configuration + def find_procs_by_name(name): """Return a list of processes matching 'name'.""" From c6d99bee7ca6c02c619bec3ae55124ebc7d0f221 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 18 Jun 2020 20:26:30 +0530 Subject: [PATCH 100/329] changes --- tests/performance/run_performance_suite.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index a6133cf02..e7ae2ef04 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -109,7 +109,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser raise Exception("Server is not running. Pinged url {}. Exiting..".format(server_ping_url)) if monitoring_server: - start_monitoring_server = "python3 {}/agents/metrics_monitoring_server.py --start".format(path) + start_monitoring_server = "python {}/agents/metrics_monitoring_server.py --start".format(path) code, output = run_process(start_monitoring_server, wait=False) time.sleep(2) @@ -183,7 +183,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if monitoring_server: - stop_monitoring_server = "python3 {}/agents/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python {}/agents/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: From a57e7cd69a5bb5292c941234cac243c89ff96a0b Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 18 Jun 2020 22:01:24 +0530 Subject: [PATCH 101/329] revert changes --- tests/performance/agents/__init__.py | 0 tests/performance/{utils => }/config.ini | 0 .../performance/{utils => }/configuration.py | 0 tests/performance/final_stats.csv | 3 + .../{agents => }/metrics_collector.py | 2 +- .../{agents => }/metrics_monitoring_server.py | 4 +- .../{agents => }/metrics_monitoring_taurus.py | 2 +- tests/performance/{utils => }/process.py | 2 +- tests/performance/run_performance_suite.py | 6 +- .../tests/modified_register_and_inference.jmx | 176 ++++++++++++++++++ tests/performance/utils/__init__.py | 0 11 files changed, 187 insertions(+), 8 deletions(-) delete mode 100644 tests/performance/agents/__init__.py rename tests/performance/{utils => }/config.ini (100%) rename tests/performance/{utils => }/configuration.py (100%) create mode 100644 tests/performance/final_stats.csv rename tests/performance/{agents => }/metrics_collector.py (98%) rename tests/performance/{agents => }/metrics_monitoring_server.py (98%) rename tests/performance/{agents => }/metrics_monitoring_taurus.py (97%) rename tests/performance/{utils => }/process.py (98%) create mode 100644 tests/performance/tests/modified_register_and_inference.jmx delete mode 100644 tests/performance/utils/__init__.py diff --git a/tests/performance/agents/__init__.py b/tests/performance/agents/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/performance/utils/config.ini b/tests/performance/config.ini similarity index 100% rename from tests/performance/utils/config.ini rename to tests/performance/config.ini diff --git a/tests/performance/utils/configuration.py b/tests/performance/configuration.py similarity index 100% rename from tests/performance/utils/configuration.py rename to tests/performance/configuration.py diff --git a/tests/performance/final_stats.csv b/tests/performance/final_stats.csv new file mode 100644 index 000000000..be752f9a1 --- /dev/null +++ b/tests/performance/final_stats.csv @@ -0,0 +1,3 @@ +label,avg_ct,avg_lt,avg_rt,bytes,concurrency,fail,stdev_rt,succ,throughput,perc_0.0,perc_50.0,perc_90.0,perc_95.0,perc_99.0,perc_99.9,perc_100.0,rc_200 +Unregister squeezenet_v1.1,0.03300,0.05300,0.05500,323,1,0,0.00000,1,1,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,1 +,0.03300,0.05300,0.05500,323,1,0,0.00000,1,1,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,1 diff --git a/tests/performance/agents/metrics_collector.py b/tests/performance/metrics_collector.py similarity index 98% rename from tests/performance/agents/metrics_collector.py rename to tests/performance/metrics_collector.py index 3270fd39c..fb5420c7f 100755 --- a/tests/performance/agents/metrics_collector.py +++ b/tests/performance/metrics_collector.py @@ -26,7 +26,7 @@ logger = logging.getLogger(__name__) -from utils.process import get_process_pid_from_file, get_child_processes, \ +from process import get_process_pid_from_file, get_child_processes, \ get_server_processes, get_server_pidfile from metrics import AVAILABLE_METRICS, get_metrics diff --git a/tests/performance/agents/metrics_monitoring_server.py b/tests/performance/metrics_monitoring_server.py similarity index 98% rename from tests/performance/agents/metrics_monitoring_server.py rename to tests/performance/metrics_monitoring_server.py index 9d89e23ba..7b62d29c3 100644 --- a/tests/performance/agents/metrics_monitoring_server.py +++ b/tests/performance/metrics_monitoring_server.py @@ -28,9 +28,9 @@ logger = logging.getLogger(__name__) from metrics_collector import start_metric_collection, stop_process, store_pid, check_is_running -from utils.process import get_process_pid_from_file, \ +from process import get_process_pid_from_file, \ get_server_processes, get_server_pidfile -from utils import configuration +import configuration TMP_DIR = tempfile.gettempdir() METRICS_MON_SERVER_PID_FILE = "{}/.metrics_monitoring_server.pid".format(TMP_DIR) diff --git a/tests/performance/agents/metrics_monitoring_taurus.py b/tests/performance/metrics_monitoring_taurus.py similarity index 97% rename from tests/performance/agents/metrics_monitoring_taurus.py rename to tests/performance/metrics_monitoring_taurus.py index df4e1ac57..ac402c7db 100644 --- a/tests/performance/agents/metrics_monitoring_taurus.py +++ b/tests/performance/metrics_monitoring_taurus.py @@ -27,7 +27,7 @@ PY3 = sys.version_info[0] == 3 from metrics import get_metrics, AVAILABLE_METRICS as AVAILABLE_SERVER_METRICS -from utils.process import get_process_pid_from_file, get_server_processes, \ +from process import get_process_pid_from_file, get_server_processes, \ get_child_processes, get_server_pidfile diff --git a/tests/performance/utils/process.py b/tests/performance/process.py similarity index 98% rename from tests/performance/utils/process.py rename to tests/performance/process.py index 13da1973a..fe89f6627 100644 --- a/tests/performance/utils/process.py +++ b/tests/performance/process.py @@ -18,7 +18,7 @@ import os import psutil import tempfile -from utils import configuration +import configuration def find_procs_by_name(name): diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index e7ae2ef04..693045987 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -109,14 +109,14 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser raise Exception("Server is not running. Pinged url {}. Exiting..".format(server_ping_url)) if monitoring_server: - start_monitoring_server = "python {}/agents/metrics_monitoring_server.py --start".format(path) + start_monitoring_server = "python3 {}/metrics_monitoring_server.py --start".format(path) code, output = run_process(start_monitoring_server, wait=False) time.sleep(2) # TODO - Add check if server started junit_xml = JUnitXml() - pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(path)) + pre_command = 'export PYTHONPATH={}:$PYTHONPATH; '.format(str(path)) test_yamls = get_test_yamls(test_dir, pattern) for test_file in tqdm(test_yamls, desc="Test Suites"): @@ -183,7 +183,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if monitoring_server: - stop_monitoring_server = "python {}/agents/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python3 {}/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: diff --git a/tests/performance/tests/modified_register_and_inference.jmx b/tests/performance/tests/modified_register_and_inference.jmx new file mode 100644 index 000000000..4dd764c6c --- /dev/null +++ b/tests/performance/tests/modified_register_and_inference.jmx @@ -0,0 +1,176 @@ + + + + + + false + true + false + + + + + + + + + + cnn_url + https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + = + The url from where to fetch noop model from + + + scale_up_workers + ${__P(min_workers,1)} + = + The workers to scale No op model to + + + scale_down_workers + 0 + Offload the No-Op Model + = + + + model + squeezenet_v1.1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${cnn_url} + POST + true + false + true + false + + + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_up_workers} + PUT + true + false + true + false + + + + + + + continue41020S + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_down_workers} + DELETE + true + false + true + false + + + + + + + /tmp/run49/inference_server_monitoring/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/tmp/run49/inference_server_monitoring/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue + + diff --git a/tests/performance/utils/__init__.py b/tests/performance/utils/__init__.py deleted file mode 100644 index e69de29bb..000000000 From 337b07af7545627ac3ef0da8338ca8a21c1e62c3 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 18 Jun 2020 22:03:03 +0530 Subject: [PATCH 102/329] change --- tests/performance/run_performance_suite.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 693045987..2df2d3baa 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -109,7 +109,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser raise Exception("Server is not running. Pinged url {}. Exiting..".format(server_ping_url)) if monitoring_server: - start_monitoring_server = "python3 {}/metrics_monitoring_server.py --start".format(path) + start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) code, output = run_process(start_monitoring_server, wait=False) time.sleep(2) @@ -183,7 +183,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if monitoring_server: - stop_monitoring_server = "python3 {}/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: From c0ea6aa9334f8ceb2da7f8e71fb5cf361641f7e0 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 18 Jun 2020 22:15:03 +0530 Subject: [PATCH 103/329] redirect setup and teardown /tmp/mms_perf --- tests/performance/tests/downscale_workers.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/tests/downscale_workers.yaml b/tests/performance/tests/downscale_workers.yaml index d406a2aad..2cc6ce5a3 100644 --- a/tests/performance/tests/downscale_workers.yaml +++ b/tests/performance/tests/downscale_workers.yaml @@ -12,9 +12,9 @@ scenarios: services: - module: shellexec prepare: - - bzt tests/common/setup.jmx tests/common/global_config.yaml + - bzt tests/common/setup.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/ post-process: - - bzt tests/common/teardown.jmx tests/common/global_config.yaml + - bzt tests/common/teardown.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/ - module: server_local_monitoring ServerLocalClient: - interval: 1s From c3a84f645709d9b1ef3b1481df5608a25d9c132c Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Thu, 18 Jun 2020 22:16:33 +0530 Subject: [PATCH 104/329] set worker_count >=0 --- tests/performance/metrics/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/metrics/__init__.py b/tests/performance/metrics/__init__.py index cb8752681..560abe08e 100644 --- a/tests/performance/metrics/__init__.py +++ b/tests/performance/metrics/__init__.py @@ -119,7 +119,7 @@ def update_metric(metric_name, proc_type, stats): # Total processes result['total_processes'] = len([server_process] + child_processes) - result['total_workers'] = len(child_processes) - 1 + result['total_workers'] = len(child_processes) - 1 if len(child_processes) else 0 ### SYSTEM METRICS ### result['system_disk_used'] = psutil.disk_usage('/').used From 60c51bc67a16bd2d333f252c867096f9f0570a98 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 18 Jun 2020 22:16:38 +0530 Subject: [PATCH 105/329] remove extra files --- tests/performance/final_stats.csv | 3 - .../tests/modified_register_and_inference.jmx | 176 ------------------ 2 files changed, 179 deletions(-) delete mode 100644 tests/performance/final_stats.csv delete mode 100644 tests/performance/tests/modified_register_and_inference.jmx diff --git a/tests/performance/final_stats.csv b/tests/performance/final_stats.csv deleted file mode 100644 index be752f9a1..000000000 --- a/tests/performance/final_stats.csv +++ /dev/null @@ -1,3 +0,0 @@ -label,avg_ct,avg_lt,avg_rt,bytes,concurrency,fail,stdev_rt,succ,throughput,perc_0.0,perc_50.0,perc_90.0,perc_95.0,perc_99.0,perc_99.9,perc_100.0,rc_200 -Unregister squeezenet_v1.1,0.03300,0.05300,0.05500,323,1,0,0.00000,1,1,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,1 -,0.03300,0.05300,0.05500,323,1,0,0.00000,1,1,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,1 diff --git a/tests/performance/tests/modified_register_and_inference.jmx b/tests/performance/tests/modified_register_and_inference.jmx deleted file mode 100644 index 4dd764c6c..000000000 --- a/tests/performance/tests/modified_register_and_inference.jmx +++ /dev/null @@ -1,176 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - cnn_url - https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - = - The url from where to fetch noop model from - - - scale_up_workers - ${__P(min_workers,1)} - = - The workers to scale No op model to - - - scale_down_workers - 0 - Offload the No-Op Model - = - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_up_workers} - PUT - true - false - true - false - - - - - - - continue41020S - - - - - ${__P(input_filepath)} - data - image/jpeg - - - - - - - - - - - /predictions/${model} - POST - true - false - true - true - - - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_down_workers} - DELETE - true - false - true - false - - - - - - - /tmp/run49/inference_server_monitoring/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/tmp/run49/inference_server_monitoring/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue - - From d90336e929b8c389e9df4008c4b8da1896ad2f86 Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 09:40:31 +0530 Subject: [PATCH 106/329] refactored to agents package Signed-off-by: Dhruva Ray --- tests/__init__.py | 13 ---------- tests/performance/__init__.py | 15 ------------ tests/performance/agents/__init__.py | 0 tests/performance/{ => agents}/config.ini | 0 .../performance/{ => agents}/configuration.py | 0 .../{ => agents}/metrics/__init__.py | 0 .../{ => agents}/metrics_collector.py | 15 +++++++----- .../{ => agents}/metrics_monitoring_server.py | 18 +++++++------- .../{ => agents}/metrics_monitoring_taurus.py | 16 +++++++------ tests/performance/agents/utils/__init__.py | 0 .../performance/{ => agents/utils}/process.py | 8 +++---- tests/performance/run_performance_suite.py | 24 ++++++++++--------- 12 files changed, 45 insertions(+), 64 deletions(-) delete mode 100644 tests/__init__.py delete mode 100644 tests/performance/__init__.py create mode 100644 tests/performance/agents/__init__.py rename tests/performance/{ => agents}/config.ini (100%) rename tests/performance/{ => agents}/configuration.py (100%) rename tests/performance/{ => agents}/metrics/__init__.py (100%) rename tests/performance/{ => agents}/metrics_collector.py (95%) rename tests/performance/{ => agents}/metrics_monitoring_server.py (92%) rename tests/performance/{ => agents}/metrics_monitoring_taurus.py (93%) create mode 100644 tests/performance/agents/utils/__init__.py rename tests/performance/{ => agents/utils}/process.py (92%) diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 62cf84245..000000000 --- a/tests/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -MMS Test Suites -""" diff --git a/tests/performance/__init__.py b/tests/performance/__init__.py deleted file mode 100644 index 88db7ad29..000000000 --- a/tests/performance/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -Perfomance Metrics Monitoring suite -""" diff --git a/tests/performance/agents/__init__.py b/tests/performance/agents/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/performance/config.ini b/tests/performance/agents/config.ini similarity index 100% rename from tests/performance/config.ini rename to tests/performance/agents/config.ini diff --git a/tests/performance/configuration.py b/tests/performance/agents/configuration.py similarity index 100% rename from tests/performance/configuration.py rename to tests/performance/agents/configuration.py diff --git a/tests/performance/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py similarity index 100% rename from tests/performance/metrics/__init__.py rename to tests/performance/agents/metrics/__init__.py diff --git a/tests/performance/metrics_collector.py b/tests/performance/agents/metrics_collector.py similarity index 95% rename from tests/performance/metrics_collector.py rename to tests/performance/agents/metrics_collector.py index fb5420c7f..262021e6b 100755 --- a/tests/performance/metrics_collector.py +++ b/tests/performance/agents/metrics_collector.py @@ -15,24 +15,27 @@ """ # pylint: disable=redefined-builtin +import argparse +import logging import os import sys +import tempfile import time -import logging -import psutil + import gevent -import argparse -import tempfile +import psutil logger = logging.getLogger(__name__) -from process import get_process_pid_from_file, get_child_processes, \ +from utils.process import get_process_pid_from_file, get_child_processes, \ get_server_processes, get_server_pidfile from metrics import AVAILABLE_METRICS, get_metrics +import configuration TMP_DIR = tempfile.gettempdir() METRICS_LOG_FILE = "{}/server_metrics_{}.log".format(TMP_DIR, int(time.time())) METRICS_COLLECTOR_PID_FILE = "{}/.metrics_collector.pid".format(TMP_DIR) +PID_FILE = configuration.get('server', 'pid_file', 'model_server.pid') MONITOR_INTERVAL = 1 @@ -127,7 +130,7 @@ def start_metric_collector_process(): check_is_running(METRICS_COLLECTOR_PID_FILE) store_metrics_collector_pid() - server_pid = get_process_pid_from_file(get_server_pidfile()) + server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) server_process = get_server_processes(server_pid) start_metric_collection(server_process, AVAILABLE_METRICS, MONITOR_INTERVAL, None) diff --git a/tests/performance/metrics_monitoring_server.py b/tests/performance/agents/metrics_monitoring_server.py similarity index 92% rename from tests/performance/metrics_monitoring_server.py rename to tests/performance/agents/metrics_monitoring_server.py index 7b62d29c3..197558427 100644 --- a/tests/performance/metrics_monitoring_server.py +++ b/tests/performance/agents/metrics_monitoring_server.py @@ -15,25 +15,27 @@ """ # pylint: disable=redefined-builtin +import argparse import logging import sys -from gevent import socket -from gevent import select -from gevent import monkey -import argparse import tempfile +from gevent import monkey +from gevent import select +from gevent import socket + monkey.patch_select() monkey.patch_socket() logger = logging.getLogger(__name__) from metrics_collector import start_metric_collection, stop_process, store_pid, check_is_running -from process import get_process_pid_from_file, \ +from utils.process import get_process_pid_from_file, \ get_server_processes, get_server_pidfile import configuration TMP_DIR = tempfile.gettempdir() METRICS_MON_SERVER_PID_FILE = "{}/.metrics_monitoring_server.pid".format(TMP_DIR) +PID_FILE = configuration.get('server', 'pid_file', 'model_server.pid') HOST = str(configuration.get('monitoring', 'HOST')) PORT = int(configuration.get('monitoring', 'PORT', 9009)) @@ -80,9 +82,9 @@ def perf_server(): except Exception: send_message(sock, "In-correct interval data") elif data.startswith('metrics'): - metrics = data[:-1].split("metrics:")[1].split("\t") - server_pid = get_process_pid_from_file(get_server_pidfile()) - server_process = get_server_processes(server_pid) + metrics = data[:-1].split("metrics:")[1].split("\t") + server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) + server_process = get_server_processes(server_pid) start_metric_collection(server_process, metrics, interval, sock) else: # TODO - decide what to do here diff --git a/tests/performance/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_taurus.py similarity index 93% rename from tests/performance/metrics_monitoring_taurus.py rename to tests/performance/agents/metrics_monitoring_taurus.py index ac402c7db..5f1ea429b 100644 --- a/tests/performance/metrics_monitoring_taurus.py +++ b/tests/performance/agents/metrics_monitoring_taurus.py @@ -18,17 +18,19 @@ # pylint: disable=redefined-builtin import csv +import sys + +import configuration +from bzt import TaurusConfigError from bzt.modules import monitoring -from bzt import TaurusConfigError from bzt.utils import dehumanize_time -import sys +from metrics import get_metrics, AVAILABLE_METRICS as AVAILABLE_SERVER_METRICS +from utils.process import get_process_pid_from_file, get_server_processes, \ + get_child_processes, get_server_pidfile PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 - -from metrics import get_metrics, AVAILABLE_METRICS as AVAILABLE_SERVER_METRICS -from process import get_process_pid_from_file, get_server_processes, \ - get_child_processes, get_server_pidfile +PID_FILE = configuration.get('server', 'pid_file', 'model_server.pid') class Monitor(monitoring.Monitoring): @@ -81,7 +83,7 @@ class ServerLocalMonitor(monitoring.LocalMonitor): def _calc_resource_stats(self, interval): result = super()._calc_resource_stats(interval) - server_pid = get_process_pid_from_file(get_server_pidfile()) + server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) server_process = get_server_processes(server_pid) result.update(get_metrics(server_process, get_child_processes(server_process))) return result diff --git a/tests/performance/agents/utils/__init__.py b/tests/performance/agents/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/performance/process.py b/tests/performance/agents/utils/process.py similarity index 92% rename from tests/performance/process.py rename to tests/performance/agents/utils/process.py index fe89f6627..9032d46ca 100644 --- a/tests/performance/process.py +++ b/tests/performance/agents/utils/process.py @@ -16,9 +16,9 @@ # pylint: disable=redefined-builtin import os -import psutil import tempfile -import configuration + +import psutil def find_procs_by_name(name): @@ -68,5 +68,5 @@ def get_server_processes(server_process_pid): return server_process -def get_server_pidfile(): - return "{0}/.{1}".format(tempfile.gettempdir(), configuration.get('server', 'pid_file', 'model_server.pid')) +def get_server_pidfile(file): + return "{0}/.{1}".format(tempfile.gettempdir(), file) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 2df2d3baa..3c7787703 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -15,23 +15,25 @@ """ # pylint: disable=redefined-builtin -import os -import sys -import time -import logging -import socket import argparse import glob +import logging +import os import pathlib +import socket import subprocess -import yaml -import requests +import sys +import time from subprocess import PIPE, STDOUT -from tqdm import tqdm + +import requests +import yaml from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure +from tqdm import tqdm logger = logging.getLogger(__name__) code = 0 +metrics_monitoring_server = "agents/metrics_monitoring_server.py" class Timer(object): @@ -109,14 +111,14 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser raise Exception("Server is not running. Pinged url {}. Exiting..".format(server_ping_url)) if monitoring_server: - start_monitoring_server = "python {}/metrics_monitoring_server.py --start".format(path) + start_monitoring_server = "python {}/{} --start".format(path, metrics_monitoring_server) code, output = run_process(start_monitoring_server, wait=False) time.sleep(2) # TODO - Add check if server started junit_xml = JUnitXml() - pre_command = 'export PYTHONPATH={}:$PYTHONPATH; '.format(str(path)) + pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(path)) test_yamls = get_test_yamls(test_dir, pattern) for test_file in tqdm(test_yamls, desc="Test Suites"): @@ -183,7 +185,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_ser run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if monitoring_server: - stop_monitoring_server = "python {}/metrics_monitoring_server.py --stop".format(path) + stop_monitoring_server = "python {}/{} --stop".format(path, metrics_monitoring_server) run_process(stop_monitoring_server) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: From 5d64f11cf15170f2ac31f6d6734a19f17959859f Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 09:43:54 +0530 Subject: [PATCH 107/329] refactored to agents package Signed-off-by: Dhruva Ray --- tests/performance/agents/metrics/__init__.py | 3 ++- tests/performance/agents/metrics_monitoring_server.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index 560abe08e..2c6c46c8a 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -11,8 +11,9 @@ # express or implied. See the License for the specific language governing # permissions and limitations under the License. -from statistics import mean from enum import Enum +from statistics import mean + import psutil diff --git a/tests/performance/agents/metrics_monitoring_server.py b/tests/performance/agents/metrics_monitoring_server.py index 197558427..55bbcca02 100644 --- a/tests/performance/agents/metrics_monitoring_server.py +++ b/tests/performance/agents/metrics_monitoring_server.py @@ -85,7 +85,7 @@ def perf_server(): metrics = data[:-1].split("metrics:")[1].split("\t") server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) server_process = get_server_processes(server_pid) - start_metric_collection(server_process, metrics, interval, sock) + start_metric_collection(server_process, metrics, interval, sock) else: # TODO - decide what to do here pass From bcdd903cd28d22a75009a045573b0810f99e168a Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 11:47:13 +0530 Subject: [PATCH 108/329] changes --- .../tmp/run345/call_jmx/call_jmx.yaml | 10 - .../tmp/run345/call_jmx/effective.json | 332 ---- .../tmp/run345/call_jmx/effective.yml | 222 --- .../performance/tmp/run345/call_jmx/error.jtl | 4 - .../tmp/run345/call_jmx/final_stats.csv | 6 - .../tmp/run345/call_jmx/global_config.yaml | 26 - .../tmp/run345/call_jmx/jmeter-bzt.properties | 14 - .../tmp/run345/call_jmx/jmeter.err | 0 .../tmp/run345/call_jmx/jmeter.out | 5 - tests/performance/tmp/run345/call_jmx/kpi.jtl | 1386 ----------------- .../tmp/run345/call_jmx/merged.json | 48 - .../tmp/run345/call_jmx/merged.yml | 29 - .../modified_register_and_inference.jmx | 176 --- .../tmp/run345/call_jmx/xunit-1.xml | 9 - .../performance/tmp/run345/call_jmx/xunit.xml | 4 - .../downscale_workers/downscale_workers.yaml | 39 - .../run345/downscale_workers/effective.json | 376 ----- .../run345/downscale_workers/effective.yml | 245 --- .../tmp/run345/downscale_workers/error.jtl | 4 - .../run345/downscale_workers/final_stats.csv | 3 - .../downscale_workers/global_config.yaml | 26 - .../downscale_workers/jmeter-bzt.properties | 14 - .../tmp/run345/downscale_workers/jmeter.err | 0 .../tmp/run345/downscale_workers/jmeter.out | 5 - .../tmp/run345/downscale_workers/kpi.jtl | 2 - .../tmp/run345/downscale_workers/merged.json | 90 -- .../tmp/run345/downscale_workers/merged.yml | 53 - .../modified_downscale_workers.jmx | 68 - .../tmp/run345/downscale_workers/xunit-1.xml | 6 - .../tmp/run345/downscale_workers/xunit.xml | 6 - .../SAlogs_localhost_9009.csv | 73 - .../effective.json | 363 ----- .../inference_server_monitoring/effective.yml | 236 --- .../inference_server_monitoring/error.jtl | 4 - .../final_stats.csv | 6 - .../global_config.yaml | 26 - .../inference_server_monitoring.yaml | 26 - .../jmeter-bzt.properties | 14 - .../inference_server_monitoring/jmeter.err | 0 .../inference_server_monitoring/jmeter.out | 5 - .../inference_server_monitoring/kpi.jtl | 780 ---------- .../inference_server_monitoring/merged.json | 67 - .../inference_server_monitoring/merged.yml | 41 - .../modified_register_and_inference.jmx | 176 --- .../inference_server_monitoring/xunit-1.xml | 9 - .../inference_server_monitoring/xunit.xml | 4 - .../SAlogs_localhost_9009.csv | 141 -- .../effective.json | 410 ----- .../effective.yml | 272 ---- .../error.jtl | 4 - .../final_stats.csv | 6 - .../global_config.yaml | 26 - .../inference_server_monitoring_criteria.yaml | 40 - .../jmeter-bzt.properties | 14 - .../jmeter.err | 0 .../jmeter.out | 5 - .../kpi.jtl | 765 --------- .../merged.json | 84 - .../merged.yml | 53 - .../modified_register_and_inference.jmx | 176 --- .../xunit-1.xml | 9 - .../xunit.xml | 9 - .../effective.json | 349 ----- .../effective.yml | 232 --- .../error.jtl | 4 - .../final_stats.csv | 6 - .../global_config.yaml | 26 - .../inference_taurus_local_monitoring.yaml | 24 - .../jmeter-bzt.properties | 14 - .../jmeter.err | 0 .../jmeter.out | 5 - .../inference_taurus_local_monitoring/kpi.jtl | 762 --------- .../merged.json | 67 - .../merged.yml | 40 - .../modified_register_and_inference.jmx | 176 --- .../xunit-1.xml | 9 - .../xunit.xml | 4 - .../effective.json | 399 ----- .../effective.yml | 270 ---- .../error.jtl | 56 - .../final_stats.csv | 6 - .../global_config.yaml | 26 - ...ence_taurus_local_monitoring_criteria.yaml | 43 - .../jmeter-bzt.properties | 14 - .../jmeter.err | 0 .../jmeter.out | 6 - .../kpi.jtl | 52 - .../merged.json | 87 -- .../merged.yml | 54 - .../modified_register_and_inference.jmx | 176 --- .../xunit-1.xml | 13 - .../xunit.xml | 11 - tests/performance/tmp/run345/junit.html | 962 ------------ tests/performance/tmp/run345/junit.xml | 3 - .../tmp/run345/upscale_workers/effective.json | 366 ----- .../tmp/run345/upscale_workers/effective.yml | 243 --- .../tmp/run345/upscale_workers/error.jtl | 4 - .../run345/upscale_workers/final_stats.csv | 5 - .../run345/upscale_workers/global_config.yaml | 26 - .../upscale_workers/jmeter-bzt.properties | 14 - .../tmp/run345/upscale_workers/jmeter.err | 0 .../tmp/run345/upscale_workers/jmeter.out | 5 - .../tmp/run345/upscale_workers/kpi.jtl | 63 - .../tmp/run345/upscale_workers/merged.json | 84 - .../tmp/run345/upscale_workers/merged.yml | 51 - .../modified_upscale_workers.jmx | 148 -- .../upscale_workers/upscale_workers.yaml | 40 - .../tmp/run345/upscale_workers/xunit-1.xml | 8 - .../tmp/run345/upscale_workers/xunit.xml | 6 - 109 files changed, 12011 deletions(-) delete mode 100644 tests/performance/tmp/run345/call_jmx/call_jmx.yaml delete mode 100644 tests/performance/tmp/run345/call_jmx/effective.json delete mode 100644 tests/performance/tmp/run345/call_jmx/effective.yml delete mode 100644 tests/performance/tmp/run345/call_jmx/error.jtl delete mode 100644 tests/performance/tmp/run345/call_jmx/final_stats.csv delete mode 100644 tests/performance/tmp/run345/call_jmx/global_config.yaml delete mode 100644 tests/performance/tmp/run345/call_jmx/jmeter-bzt.properties delete mode 100644 tests/performance/tmp/run345/call_jmx/jmeter.err delete mode 100644 tests/performance/tmp/run345/call_jmx/jmeter.out delete mode 100644 tests/performance/tmp/run345/call_jmx/kpi.jtl delete mode 100644 tests/performance/tmp/run345/call_jmx/merged.json delete mode 100644 tests/performance/tmp/run345/call_jmx/merged.yml delete mode 100644 tests/performance/tmp/run345/call_jmx/modified_register_and_inference.jmx delete mode 100644 tests/performance/tmp/run345/call_jmx/xunit-1.xml delete mode 100644 tests/performance/tmp/run345/call_jmx/xunit.xml delete mode 100644 tests/performance/tmp/run345/downscale_workers/downscale_workers.yaml delete mode 100644 tests/performance/tmp/run345/downscale_workers/effective.json delete mode 100644 tests/performance/tmp/run345/downscale_workers/effective.yml delete mode 100644 tests/performance/tmp/run345/downscale_workers/error.jtl delete mode 100644 tests/performance/tmp/run345/downscale_workers/final_stats.csv delete mode 100644 tests/performance/tmp/run345/downscale_workers/global_config.yaml delete mode 100644 tests/performance/tmp/run345/downscale_workers/jmeter-bzt.properties delete mode 100644 tests/performance/tmp/run345/downscale_workers/jmeter.err delete mode 100644 tests/performance/tmp/run345/downscale_workers/jmeter.out delete mode 100644 tests/performance/tmp/run345/downscale_workers/kpi.jtl delete mode 100644 tests/performance/tmp/run345/downscale_workers/merged.json delete mode 100644 tests/performance/tmp/run345/downscale_workers/merged.yml delete mode 100644 tests/performance/tmp/run345/downscale_workers/modified_downscale_workers.jmx delete mode 100644 tests/performance/tmp/run345/downscale_workers/xunit-1.xml delete mode 100644 tests/performance/tmp/run345/downscale_workers/xunit.xml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/SAlogs_localhost_9009.csv delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/effective.json delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/effective.yml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/error.jtl delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/final_stats.csv delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/global_config.yaml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/inference_server_monitoring.yaml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/jmeter-bzt.properties delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/jmeter.err delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/jmeter.out delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/kpi.jtl delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/merged.json delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/merged.yml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/modified_register_and_inference.jmx delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/xunit-1.xml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring/xunit.xml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/SAlogs_localhost_9009.csv delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/effective.json delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/effective.yml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/error.jtl delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/final_stats.csv delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/global_config.yaml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/jmeter-bzt.properties delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/jmeter.err delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/jmeter.out delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/kpi.jtl delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/merged.json delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/merged.yml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/modified_register_and_inference.jmx delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit-1.xml delete mode 100644 tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit.xml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/effective.json delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/effective.yml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/error.jtl delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/final_stats.csv delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/global_config.yaml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/jmeter-bzt.properties delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/jmeter.err delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/jmeter.out delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/kpi.jtl delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/merged.json delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/merged.yml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/modified_register_and_inference.jmx delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit-1.xml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit.xml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/effective.json delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/effective.yml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/error.jtl delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/final_stats.csv delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/global_config.yaml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/jmeter-bzt.properties delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/jmeter.err delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/jmeter.out delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/kpi.jtl delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/merged.json delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/merged.yml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/modified_register_and_inference.jmx delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit-1.xml delete mode 100644 tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit.xml delete mode 100644 tests/performance/tmp/run345/junit.html delete mode 100644 tests/performance/tmp/run345/junit.xml delete mode 100644 tests/performance/tmp/run345/upscale_workers/effective.json delete mode 100644 tests/performance/tmp/run345/upscale_workers/effective.yml delete mode 100644 tests/performance/tmp/run345/upscale_workers/error.jtl delete mode 100644 tests/performance/tmp/run345/upscale_workers/final_stats.csv delete mode 100644 tests/performance/tmp/run345/upscale_workers/global_config.yaml delete mode 100644 tests/performance/tmp/run345/upscale_workers/jmeter-bzt.properties delete mode 100644 tests/performance/tmp/run345/upscale_workers/jmeter.err delete mode 100644 tests/performance/tmp/run345/upscale_workers/jmeter.out delete mode 100644 tests/performance/tmp/run345/upscale_workers/kpi.jtl delete mode 100644 tests/performance/tmp/run345/upscale_workers/merged.json delete mode 100644 tests/performance/tmp/run345/upscale_workers/merged.yml delete mode 100644 tests/performance/tmp/run345/upscale_workers/modified_upscale_workers.jmx delete mode 100644 tests/performance/tmp/run345/upscale_workers/upscale_workers.yaml delete mode 100644 tests/performance/tmp/run345/upscale_workers/xunit-1.xml delete mode 100644 tests/performance/tmp/run345/upscale_workers/xunit.xml diff --git a/tests/performance/tmp/run345/call_jmx/call_jmx.yaml b/tests/performance/tmp/run345/call_jmx/call_jmx.yaml deleted file mode 100644 index 0332cc6d9..000000000 --- a/tests/performance/tmp/run345/call_jmx/call_jmx.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 1s - hold-for: 40s - scenario: Inference -scenarios: - Inference: - script: register_and_inference.jmx - diff --git a/tests/performance/tmp/run345/call_jmx/effective.json b/tests/performance/tmp/run345/call_jmx/effective.json deleted file mode 100644 index d67af3d52..000000000 --- a/tests/performance/tmp/run345/call_jmx/effective.json +++ /dev/null @@ -1,332 +0,0 @@ -{ - "modules": { - "ab": { - "class": "bzt.modules.ab.ApacheBenchmarkExecutor" - }, - "gatling": { - "class": "bzt.modules.gatling.GatlingExecutor", - "properties": { - "gatling.data.file.bufferSize": 256 - } - }, - "grinder": { - "class": "bzt.modules.grinder.GrinderExecutor", - "properties": { - "grinder.useConsole": false - } - }, - "jmeter": { - "class": "bzt.modules.jmeter.JMeterExecutor", - "protocol-handlers": { - "http": "bzt.jmx.http.HTTPProtocolHandler" - }, - "plugins": [ - "jpgc-casutg", - "jpgc-dummy", - "jpgc-ffw", - "jpgc-fifo", - "jpgc-functions", - "jpgc-json", - "jpgc-perfmon", - "jpgc-prmctl", - "jpgc-tst" - ], - "properties": { - "jmeter.save.saveservice.autoflush": "true", - "jmeter.save.saveservice.connect_time": "true", - "jmeterengine.force.system.exit": "true", - "summariser.name": "", - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - }, - "version": "5.2.1", - "path": "/Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh" - }, - "locust": { - "class": "bzt.modules.locustio.LocustIOExecutor" - }, - "molotov": { - "class": "bzt.modules.molotov.MolotovExecutor" - }, - "pbench": { - "class": "bzt.modules.pbench.PBenchExecutor" - }, - "siege": { - "class": "bzt.modules.siege.SiegeExecutor" - }, - "tsung": { - "class": "bzt.modules.tsung.TsungExecutor" - }, - "selenium": { - "class": "bzt.modules.selenium.SeleniumExecutor" - }, - "junit": { - "class": "bzt.modules.java.JUnitTester" - }, - "testng": { - "class": "bzt.modules.java.TestNGTester" - }, - "rspec": { - "class": "bzt.modules.ruby.RSpecTester" - }, - "mocha": { - "class": "bzt.modules.javascript.MochaTester" - }, - "nunit": { - "class": "bzt.modules.csharp.NUnitExecutor" - }, - "pytest": { - "class": "bzt.modules.pytest.PyTestExecutor" - }, - "wdio": { - "class": "bzt.modules.javascript.WebdriverIOExecutor" - }, - "robot": { - "class": "bzt.modules.robot.RobotExecutor" - }, - "newman": { - "class": "bzt.modules.javascript.NewmanExecutor" - }, - "apiritif": { - "class": "bzt.modules.apiritif.ApiritifNoseExecutor" - }, - "local": { - "class": "bzt.modules.provisioning.Local" - }, - "monitoring": { - "class": "bzt.modules.monitoring.Monitoring" - }, - "passfail": { - "class": "bzt.modules.passfail.PassFailStatus" - }, - "shellexec": { - "class": "bzt.modules.shellexec.ShellExecutor" - }, - "junit-xml": { - "class": "bzt.modules.reporting.JUnitXMLReporter" - }, - "install-checker": { - "class": "bzt.modules.services.InstallChecker" - }, - "console": { - "class": "bzt.modules.console.ConsoleStatusReporter", - "disable": true - }, - "consolidator": { - "class": "bzt.modules.aggregator.ConsolidatingAggregator", - "percentiles": [ - 0.0, - 50.0, - 90.0, - 95.0, - 99.0, - 99.9, - 100.0 - ] - }, - "final-stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "functional-consolidator": { - "class": "bzt.modules.functional.FunctionalAggregator" - }, - "android-emulator": { - "class": "bzt.modules.services.AndroidEmulatorLoader" - }, - "appium": { - "class": "bzt.modules.services.AppiumLoader" - }, - "virtual-display": { - "class": "bzt.modules.services.VirtualDisplay" - }, - "external-results-loader": { - "class": "bzt.modules.external.ExternalResultsLoader" - }, - "blazemeter": { - "class": "bzt.modules.blazemeter.BlazeMeterUploader" - }, - "cloud": { - "class": "bzt.modules.blazemeter.CloudProvisioning", - "default-location": "us-central1-a" - }, - "proxy2jmx": { - "class": "bzt.modules.proxy2jmx.Proxy2JMX" - }, - "unpacker": { - "class": "bzt.modules.services.Unpacker" - }, - "screenshoter": { - "class": "bzt.modules.blazemeter.ServiceStubScreenshoter" - }, - "capturehar": { - "class": "bzt.modules.blazemeter.ServiceStubCaptureHAR" - }, - "final_stats": { - "class": "bzt.modules.reporting.FinalStatus" - } - }, - "cli-aliases": { - "cloud": { - "provisioning": "cloud" - }, - "detach": { - "modules": { - "cloud": { - "detach": true - } - } - }, - "func": { - "modules": { - "console": { - "disable": true - } - }, - "settings": { - "aggregator": "functional-consolidator" - } - }, - "gui": { - "modules": { - "jmeter": { - "gui": true - } - } - }, - "install-tools": { - "services": [ - "install-checker" - ] - }, - "local": { - "provisioning": "local" - }, - "locations": { - "modules": { - "cloud": { - "dump-locations": true - } - }, - "provisioning": "cloud" - }, - "report": { - "reporting": [ - "blazemeter" - ] - }, - "public": { - "modules": { - "blazemeter": { - "public-report": true - } - } - }, - "sequential": { - "modules": { - "local": { - "sequential": true - } - } - }, - "lint": { - "cli": { - "linter": { - "lint-and-exit": true - } - } - }, - "nolint": { - "cli": { - "linter": { - "disable": true - } - } - } - }, - "cli": { - "linter": { - "disable": false, - "warn-on-unfamiliar-fields": false, - "ignored-warnings": [], - "lint-and-exit": false - } - }, - "settings": { - "aggregator": "consolidator", - "default-executor": "jmeter", - "env": { - "OBJC_DISABLE_INITIALIZE_FORK_SAFETY": "YES", - "BASEDIR": "tmp/run345/call_jmx", - "TAURUS_ARTIFACTS_DIR": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/call_jmx" - }, - "artifacts-dir": "tmp/run345/call_jmx" - }, - "provisioning": "local", - "reporting": [ - { - "module": "final-stats" - }, - { - "module": "console" - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/call_jmx/xunit.xml" - }, - { - "module": "junit-xml", - "data-source": "sample-labels", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/call_jmx/xunit-1.xml" - }, - { - "module": "final-stats", - "dump-csv": "tmp/run345/call_jmx/final_stats.csv" - } - ], - "services": [ - { - "module": "monitoring", - "local": [ - { - "metrics": [ - "cpu", - "mem", - "bytes-sent", - "bytes-recv", - "disk-read", - "disk-write", - "disk-space", - "engine-loop", - "conn-all" - ] - } - ] - } - ], - "execution": [ - { - "concurrency": { - "local": 1 - }, - "ramp-up": "1s", - "hold-for": "40s", - "scenario": "Inference", - "executor": "jmeter", - "throughput": {} - } - ], - "scenarios": { - "Inference": { - "script": "/Users/mahesh/git/multi-model-server/tests/performance/tests/register_and_inference.jmx" - } - }, - "included-configs": [], - "version": "1.14.2" -} diff --git a/tests/performance/tmp/run345/call_jmx/effective.yml b/tests/performance/tmp/run345/call_jmx/effective.yml deleted file mode 100644 index c86ed11d4..000000000 --- a/tests/performance/tmp/run345/call_jmx/effective.yml +++ /dev/null @@ -1,222 +0,0 @@ ---- -cli: - linter: - disable: false - ignored-warnings: [] - lint-and-exit: false - warn-on-unfamiliar-fields: false -cli-aliases: - cloud: - provisioning: cloud - detach: - modules: - cloud: - detach: true - func: - modules: - console: - disable: true - settings: - aggregator: functional-consolidator - gui: - modules: - jmeter: - gui: true - install-tools: - services: - - install-checker - lint: - cli: - linter: - lint-and-exit: true - local: - provisioning: local - locations: - modules: - cloud: - dump-locations: true - provisioning: cloud - nolint: - cli: - linter: - disable: true - public: - modules: - blazemeter: - public-report: true - report: - reporting: - - blazemeter - sequential: - modules: - local: - sequential: true -execution: -- concurrency: - local: 1 - executor: jmeter - hold-for: 40s - ramp-up: 1s - scenario: Inference - throughput: {} -included-configs: [] -modules: - ab: - class: bzt.modules.ab.ApacheBenchmarkExecutor - android-emulator: - class: bzt.modules.services.AndroidEmulatorLoader - apiritif: - class: bzt.modules.apiritif.ApiritifNoseExecutor - appium: - class: bzt.modules.services.AppiumLoader - blazemeter: - class: bzt.modules.blazemeter.BlazeMeterUploader - capturehar: - class: bzt.modules.blazemeter.ServiceStubCaptureHAR - cloud: - class: bzt.modules.blazemeter.CloudProvisioning - default-location: us-central1-a - console: - class: bzt.modules.console.ConsoleStatusReporter - disable: true - consolidator: - class: bzt.modules.aggregator.ConsolidatingAggregator - percentiles: - - 0.0 - - 50.0 - - 90.0 - - 95.0 - - 99.0 - - 99.9 - - 100.0 - external-results-loader: - class: bzt.modules.external.ExternalResultsLoader - final-stats: - class: bzt.modules.reporting.FinalStatus - final_stats: - class: bzt.modules.reporting.FinalStatus - functional-consolidator: - class: bzt.modules.functional.FunctionalAggregator - gatling: - class: bzt.modules.gatling.GatlingExecutor - properties: - gatling.data.file.bufferSize: 256 - grinder: - class: bzt.modules.grinder.GrinderExecutor - properties: - grinder.useConsole: false - install-checker: - class: bzt.modules.services.InstallChecker - jmeter: - class: bzt.modules.jmeter.JMeterExecutor - path: /Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh - plugins: - - jpgc-casutg - - jpgc-dummy - - jpgc-ffw - - jpgc-fifo - - jpgc-functions - - jpgc-json - - jpgc-perfmon - - jpgc-prmctl - - jpgc-tst - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - jmeter.save.saveservice.autoflush: 'true' - jmeter.save.saveservice.connect_time: 'true' - jmeterengine.force.system.exit: 'true' - management_port: 8081 - port: 8080 - protocol: http - summariser.name: '' - protocol-handlers: - http: bzt.jmx.http.HTTPProtocolHandler - version: 5.2.1 - junit: - class: bzt.modules.java.JUnitTester - junit-xml: - class: bzt.modules.reporting.JUnitXMLReporter - local: - class: bzt.modules.provisioning.Local - locust: - class: bzt.modules.locustio.LocustIOExecutor - mocha: - class: bzt.modules.javascript.MochaTester - molotov: - class: bzt.modules.molotov.MolotovExecutor - monitoring: - class: bzt.modules.monitoring.Monitoring - newman: - class: bzt.modules.javascript.NewmanExecutor - nunit: - class: bzt.modules.csharp.NUnitExecutor - passfail: - class: bzt.modules.passfail.PassFailStatus - pbench: - class: bzt.modules.pbench.PBenchExecutor - proxy2jmx: - class: bzt.modules.proxy2jmx.Proxy2JMX - pytest: - class: bzt.modules.pytest.PyTestExecutor - robot: - class: bzt.modules.robot.RobotExecutor - rspec: - class: bzt.modules.ruby.RSpecTester - screenshoter: - class: bzt.modules.blazemeter.ServiceStubScreenshoter - selenium: - class: bzt.modules.selenium.SeleniumExecutor - shellexec: - class: bzt.modules.shellexec.ShellExecutor - siege: - class: bzt.modules.siege.SiegeExecutor - testng: - class: bzt.modules.java.TestNGTester - tsung: - class: bzt.modules.tsung.TsungExecutor - unpacker: - class: bzt.modules.services.Unpacker - virtual-display: - class: bzt.modules.services.VirtualDisplay - wdio: - class: bzt.modules.javascript.WebdriverIOExecutor -provisioning: local -reporting: -- module: final-stats -- module: console -- module: passfail -- data-source: pass-fail - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/call_jmx/xunit.xml - module: junit-xml -- data-source: sample-labels - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/call_jmx/xunit-1.xml - module: junit-xml -- dump-csv: tmp/run345/call_jmx/final_stats.csv - module: final-stats -scenarios: - Inference: - script: /Users/mahesh/git/multi-model-server/tests/performance/tests/register_and_inference.jmx -services: -- local: - - metrics: - - cpu - - mem - - bytes-sent - - bytes-recv - - disk-read - - disk-write - - disk-space - - engine-loop - - conn-all - module: monitoring -settings: - aggregator: consolidator - artifacts-dir: tmp/run345/call_jmx - default-executor: jmeter - env: - BASEDIR: tmp/run345/call_jmx - OBJC_DISABLE_INITIALIZE_FORK_SAFETY: 'YES' - TAURUS_ARTIFACTS_DIR: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/call_jmx -version: 1.14.2 - diff --git a/tests/performance/tmp/run345/call_jmx/error.jtl b/tests/performance/tmp/run345/call_jmx/error.jtl deleted file mode 100644 index 32bb536a8..000000000 --- a/tests/performance/tmp/run345/call_jmx/error.jtl +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/tmp/run345/call_jmx/final_stats.csv b/tests/performance/tmp/run345/call_jmx/final_stats.csv deleted file mode 100644 index 1aedcc41d..000000000 --- a/tests/performance/tmp/run345/call_jmx/final_stats.csv +++ /dev/null @@ -1,6 +0,0 @@ -label,avg_ct,avg_lt,avg_rt,bytes,concurrency,fail,stdev_rt,succ,throughput,perc_0.0,perc_50.0,perc_90.0,perc_95.0,perc_99.0,perc_99.9,perc_100.0,rc_200,rc_202 -Setup squeezenet_v1.1 Model,0.03600,24.81900,24.82100,321,1,0,0.00300,1,1,24.81600,24.81600,24.81600,24.81600,24.81600,24.81600,24.81600,1, -,0.00060,0.04735,0.04736,950397,1,0,0.66631,1385,1385,0.00300,0.02900,0.03100,0.03300,0.03500,0.78700,24.81600,1384,1 -Scale Up squeezenet_v1.1 Model,0.00100,0.00300,0.00300,319,1,0,0.00000,1,1,0.00300,0.00300,0.00300,0.00300,0.00300,0.00300,0.00300,,1 -Inference Request,0.00058,0.02946,0.02948,949434,1,0,0.02047,1382,1382,0.02600,0.02900,0.03100,0.03300,0.03500,0.05600,0.78700,1382, -Scale Down squeezenet_v1.1,0.00100,0.03300,0.03300,323,1,0,0.00000,1,1,0.03300,0.03300,0.03300,0.03300,0.03300,0.03300,0.03300,1, diff --git a/tests/performance/tmp/run345/call_jmx/global_config.yaml b/tests/performance/tmp/run345/call_jmx/global_config.yaml deleted file mode 100644 index 94731f4a3..000000000 --- a/tests/performance/tmp/run345/call_jmx/global_config.yaml +++ /dev/null @@ -1,26 +0,0 @@ -modules: - jmeter: - # These are JMeter test case properties. These variables are used in jmx files. - # Change the vaues as per your setup - properties: - hostname : 127.0.0.1 # MMS properties - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg # make sure jpg is available at this path - # if relative path is provided this will be relative to current working directory - -# DO-NOT change properties below unless you know what you are doing. -# They are needed for performance test suite runner script. -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -settings: - env: - BASEDIR : '.' diff --git a/tests/performance/tmp/run345/call_jmx/jmeter-bzt.properties b/tests/performance/tmp/run345/call_jmx/jmeter-bzt.properties deleted file mode 100644 index 13530bd79..000000000 --- a/tests/performance/tmp/run345/call_jmx/jmeter-bzt.properties +++ /dev/null @@ -1,14 +0,0 @@ -jmeter.save.saveservice.autoflush=true -jmeter.save.saveservice.connect_time=true -jmeterengine.force.system.exit=true -summariser.name= -hostname=127.0.0.1 -port=8080 -management_port=8081 -protocol=http -input_filepath=kitten.jpg -jmeterengine.nongui.port=4445 -jmeterengine.nongui.maxport=4445 -jmeter.save.saveservice.timestamp_format=ms -sampleresult.default.encoding=UTF-8 -user.classpath=/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/call_jmx:/Users/mahesh/git/multi-model-server/tests/performance/tests diff --git a/tests/performance/tmp/run345/call_jmx/jmeter.err b/tests/performance/tmp/run345/call_jmx/jmeter.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/performance/tmp/run345/call_jmx/jmeter.out b/tests/performance/tmp/run345/call_jmx/jmeter.out deleted file mode 100644 index 0a22c484b..000000000 --- a/tests/performance/tmp/run345/call_jmx/jmeter.out +++ /dev/null @@ -1,5 +0,0 @@ -Created the tree successfully using /Users/mahesh/git/multi-model-server/tests/performance/tests/modified_register_and_inference.jmx -Starting standalone test @ Thu Jun 18 22:25:03 IST 2020 (1592499303733) -Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445 -Tidying up ... @ Thu Jun 18 22:26:10 IST 2020 (1592499370853) -... end of run diff --git a/tests/performance/tmp/run345/call_jmx/kpi.jtl b/tests/performance/tmp/run345/call_jmx/kpi.jtl deleted file mode 100644 index c0f903ceb..000000000 --- a/tests/performance/tmp/run345/call_jmx/kpi.jtl +++ /dev/null @@ -1,1386 +0,0 @@ -timeStamp,elapsed,label,responseCode,responseMessage,threadName,success,bytes,grpThreads,allThreads,Latency,Hostname,Connect -1592499304237,24821,Setup squeezenet_v1.1 Model,200,OK,setup squeezenet_v1.1 1-1,true,321,1,1,24819,BRL06570.local,36 -1592499329061,3,Scale Up squeezenet_v1.1 Model,202,Accepted,setup squeezenet_v1.1 1-1,true,319,1,1,3,BRL06570.local,1 -1592499329648,787,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,787,BRL06570.local,7 -1592499330441,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499330472,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499330502,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499330533,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499330562,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499330592,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499330622,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499330652,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499330682,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499330711,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499330741,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499330772,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499330802,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499330831,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499330859,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499330888,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499330917,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499330947,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499330976,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499331005,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499331034,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499331063,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499331097,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499331127,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499331156,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499331186,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499331216,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499331247,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499331281,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499331310,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499331339,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499331369,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499331399,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499331430,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499331459,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499331487,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499331515,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499331547,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499331577,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499331606,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499331635,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499331664,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499331697,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499331726,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499331756,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499331784,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499331812,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499331840,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499331870,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499331898,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499331925,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499331953,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499331981,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499332010,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499332039,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499332068,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499332097,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499332125,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499332156,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,0 -1592499332190,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499332221,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499332252,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499332281,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499332312,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499332342,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499332373,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499332403,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499332431,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499332461,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499332492,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499332524,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499332556,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499332588,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499332617,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499332649,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499332681,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499332710,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499332740,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499332769,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499332799,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499332830,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499332861,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499332892,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499332923,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499332954,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499332987,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499333017,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499333047,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499333076,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499333107,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499333139,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499333169,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499333198,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499333228,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499333258,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499333289,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499333318,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499333350,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499333380,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499333410,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499333441,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499333472,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499333503,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499333534,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499333565,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499333597,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499333628,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499333659,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499333691,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499333720,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499333750,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499333781,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499333811,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499333841,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499333872,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499333904,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499333936,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499333965,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499333997,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499334029,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499334060,56,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,56,BRL06570.local,1 -1592499334117,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499334148,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499334178,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499334208,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499334238,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499334268,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499334298,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499334327,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499334357,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499334388,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499334417,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499334447,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499334475,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499334504,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499334534,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499334566,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499334597,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499334626,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499334655,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499334686,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499334716,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499334747,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499334775,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499334806,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499334838,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499334868,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499334898,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499334929,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499334958,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499334989,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499335019,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499335048,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499335077,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499335107,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499335139,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499335169,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499335198,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499335228,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499335257,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499335288,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499335318,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499335347,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499335376,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499335407,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499335437,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499335471,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499335502,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499335535,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499335570,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499335600,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499335631,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499335661,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499335691,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499335720,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499335750,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499335779,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499335808,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499335839,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499335870,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499335901,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499335930,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499335959,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499335989,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499336019,36,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,0 -1592499336057,36,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,36,BRL06570.local,1 -1592499336094,36,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,36,BRL06570.local,0 -1592499336130,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499336164,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,2 -1592499336197,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499336226,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499336257,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499336287,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499336317,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499336347,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499336377,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499336407,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499336435,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499336462,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499336490,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499336519,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499336548,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499336576,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499336604,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499336633,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499336661,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499336691,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499336719,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499336748,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499336777,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499336805,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499336834,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499336864,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499336896,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499336925,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499336953,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499336982,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499337012,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499337041,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499337069,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499337097,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499337126,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499337155,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499337183,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499337212,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499337241,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499337270,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499337301,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499337329,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499337358,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499337386,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499337414,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499337447,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499337477,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499337505,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499337535,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499337564,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499337597,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499337625,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499337654,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499337682,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499337711,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499337740,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499337770,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499337798,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499337826,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499337855,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499337884,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499337914,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499337947,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499337975,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499338003,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499338033,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499338062,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499338093,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499338121,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499338150,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499338178,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499338207,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499338236,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499338264,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499338296,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499338326,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499338354,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499338384,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499338414,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499338447,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499338475,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499338504,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499338533,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499338562,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499338590,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499338618,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499338647,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499338677,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499338706,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499338734,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499338762,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499338790,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499338821,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499338851,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499338879,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499338908,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499338936,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499338965,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499338996,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499339025,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499339053,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499339082,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499339111,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499339140,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499339168,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499339197,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499339225,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499339254,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,0 -1592499339289,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499339321,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499339349,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499339377,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499339404,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499339433,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499339461,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499339489,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499339517,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499339547,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499339577,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499339605,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499339633,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499339661,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499339690,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499339718,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499339747,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499339776,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499339804,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499339832,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499339861,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499339890,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499339919,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499339948,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499339976,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499340005,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499340034,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499340063,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499340096,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499340125,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499340152,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499340182,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499340211,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499340240,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499340268,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499340296,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499340325,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499340355,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499340383,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499340411,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499340440,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499340468,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499340499,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499340528,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499340555,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499340582,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499340609,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499340637,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499340666,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499340696,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499340723,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499340751,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499340780,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499340809,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499340836,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499340864,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499340896,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499340924,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499340952,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499340981,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499341009,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499341036,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499341066,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499341096,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499341124,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499341151,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499341181,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499341210,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499341239,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499341268,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499341296,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499341323,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499341351,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499341380,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499341409,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499341436,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499341464,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499341497,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499341524,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499341554,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499341583,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499341611,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499341637,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499341665,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499341697,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499341725,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499341754,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499341782,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499341809,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499341839,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499341867,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499341896,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499341924,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499341951,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499341980,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342009,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499342036,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342064,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499342096,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342124,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342153,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499342181,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499342208,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342236,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499342264,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499342296,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342325,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499342353,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499342380,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499342407,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499342437,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499342466,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499342496,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342525,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342553,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499342582,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499342613,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499342646,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342675,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499342703,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499342732,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499342762,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499342789,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342817,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499342846,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342875,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499342905,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499342934,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499342962,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499342990,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499343019,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499343048,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499343077,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499343104,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499343132,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499343160,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499343189,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499343219,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499343247,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499343275,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499343303,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499343332,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499343362,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499343391,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499343419,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499343447,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499343477,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499343507,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499343536,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499343565,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499343596,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499343625,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499343655,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499343684,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499343712,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499343739,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499343765,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499343796,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499343825,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499343853,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499343880,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499343908,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499343936,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499343965,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499343997,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499344023,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499344050,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499344078,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499344107,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499344134,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499344163,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499344196,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499344223,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499344253,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499344283,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499344310,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499344337,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499344364,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499344395,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499344424,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499344451,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499344479,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499344507,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499344535,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499344563,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499344595,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499344624,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499344651,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499344678,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499344708,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499344736,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499344763,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499344795,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499344825,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499344854,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499344884,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499344913,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499344945,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499344973,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499345002,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499345031,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499345059,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499345087,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499345116,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499345145,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499345175,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499345204,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499345231,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499345261,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499345290,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499345319,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499345348,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499345375,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499345403,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,2 -1592499345434,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499345464,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499345496,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499345525,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499345552,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499345580,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499345608,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499345637,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499345664,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499345695,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499345723,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499345749,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499345779,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499345808,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499345835,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499345863,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499345896,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499345928,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499345955,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499345983,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499346011,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499346045,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499346075,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499346105,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499346133,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499346161,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499346189,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499346218,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499346248,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499346276,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499346304,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499346331,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499346359,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499346388,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499346416,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499346445,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499346474,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499346504,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499346533,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499346561,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499346589,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499346616,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499346645,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499346674,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499346703,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499346730,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499346758,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499346784,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499346811,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499346839,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499346866,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499346895,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499346922,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499346949,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499346977,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499347004,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499347030,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499347058,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499347086,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499347115,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499347145,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499347173,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499347201,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499347229,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499347257,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499347284,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499347312,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499347345,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499347373,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499347403,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499347432,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499347461,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499347490,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499347519,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499347547,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499347574,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499347603,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499347631,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499347658,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499347686,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499347714,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499347745,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499347773,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499347800,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499347827,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499347857,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499347884,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499347911,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499347939,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499347966,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499347995,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499348024,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499348051,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499348079,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499348107,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499348135,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499348163,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499348195,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499348223,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499348251,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499348278,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499348307,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499348335,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499348362,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499348395,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499348423,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499348451,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499348480,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499348507,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499348535,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499348562,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499348595,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499348624,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499348652,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499348680,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499348708,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499348736,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499348766,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499348795,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499348823,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499348852,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499348881,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499348911,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499348945,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499348973,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499349002,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499349030,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499349061,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499349089,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499349118,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499349145,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499349173,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499349203,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499349231,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499349258,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499349286,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499349313,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499349345,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499349375,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499349403,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499349431,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499349461,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499349489,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499349518,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499349547,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499349576,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499349605,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499349633,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499349661,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499349689,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499349717,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499349745,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499349774,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499349802,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499349831,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499349858,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499349885,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499349913,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499349945,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499349976,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499350006,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499350034,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499350062,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499350095,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499350125,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499350154,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499350182,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499350209,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499350237,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499350266,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499350295,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499350322,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499350350,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499350377,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499350405,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499350435,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499350464,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499350495,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499350524,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499350553,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499350581,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499350611,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499350639,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499350666,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499350695,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499350725,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499350753,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499350779,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499350806,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499350833,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499350862,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499350895,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499350924,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499350950,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499350977,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499351005,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499351035,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499351061,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499351095,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499351123,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499351150,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499351178,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499351206,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499351234,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499351262,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499351295,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499351325,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499351353,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499351382,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499351410,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499351439,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499351469,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499351497,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499351525,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499351554,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499351582,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499351612,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499351645,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499351673,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499351701,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499351729,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499351758,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499351788,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499351816,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499351845,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499351874,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499351902,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499351932,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499351960,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499351988,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499352016,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499352045,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499352075,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499352104,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499352132,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499352159,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499352186,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499352214,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499352245,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499352273,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499352300,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499352328,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499352358,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499352389,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499352417,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499352445,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499352473,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499352500,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499352528,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499352556,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499352583,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499352612,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499352645,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499352674,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499352701,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499352729,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499352758,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499352786,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499352815,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499352845,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499352873,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499352900,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499352928,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499352957,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499352985,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499353012,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499353045,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499353073,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499353101,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499353130,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499353158,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499353185,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499353212,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499353245,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499353275,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499353303,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499353332,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499353362,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499353395,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499353424,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499353455,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499353482,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499353508,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499353535,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499353564,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499353596,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499353625,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499353653,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499353682,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499353712,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499353745,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499353774,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499353801,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499353828,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499353855,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499353885,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499353912,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499353945,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499353973,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499354001,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499354029,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499354058,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499354087,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499354113,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499354145,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499354174,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499354205,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499354232,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499354259,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499354287,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499354314,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499354345,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499354373,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499354401,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499354429,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499354457,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499354487,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499354516,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499354545,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499354573,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499354602,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499354632,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499354660,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499354687,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499354715,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499354745,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499354773,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499354802,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499354829,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499354857,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499354884,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499354912,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499354945,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499354974,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499355002,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499355033,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499355061,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499355095,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499355125,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499355152,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499355179,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499355207,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499355235,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499355263,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499355295,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499355325,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499355352,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499355380,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499355410,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499355438,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499355465,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499355495,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499355524,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499355553,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499355580,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499355609,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499355638,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499355666,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499355695,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499355725,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499355756,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499355783,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499355811,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499355842,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499355871,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499355900,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499355928,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499355955,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499355983,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499356012,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499356045,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499356074,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499356102,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499356133,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499356162,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499356195,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499356225,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499356252,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499356280,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499356309,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499356341,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499356368,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499356395,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499356425,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499356455,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499356483,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499356514,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499356545,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499356572,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499356604,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499356636,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499356663,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499356695,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499356724,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499356752,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499356782,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499356812,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499356845,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499356873,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499356902,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499356932,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499356961,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499356990,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499357017,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499357045,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499357074,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499357102,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499357130,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499357158,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499357189,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499357217,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499357246,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499357275,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499357306,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499357333,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499357362,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499357395,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499357422,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499357450,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499357479,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499357510,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499357538,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499357567,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499357595,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499357622,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499357650,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499357678,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499357708,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499357735,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499357762,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499357795,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499357822,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499357851,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499357878,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499357905,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499357932,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499357959,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499357989,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499358016,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499358045,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499358073,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499358103,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499358131,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499358157,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499358184,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499358213,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499358245,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499358275,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499358308,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499358336,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499358365,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499358395,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499358427,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499358457,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499358486,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499358515,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499358545,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499358577,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499358606,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499358634,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499358661,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499358695,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499358724,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499358753,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499358781,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499358810,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499358841,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499358870,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499358898,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499358928,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499358960,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499358988,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499359015,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499359046,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499359079,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499359112,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499359145,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499359174,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499359204,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499359237,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499359269,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499359300,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499359330,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499359359,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499359389,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499359418,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499359447,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499359476,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499359506,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499359534,36,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,36,BRL06570.local,1 -1592499359571,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499359606,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499359637,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499359670,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499359701,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499359732,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499359761,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499359790,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499359818,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499359850,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499359884,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499359913,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499359945,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499359975,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499360006,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499360036,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499360065,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499360095,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499360125,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499360155,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499360184,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499360215,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499360248,36,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499360285,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499360316,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499360346,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499360378,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499360408,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499360437,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499360467,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499360497,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499360532,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499360563,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499360595,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499360626,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499360659,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499360690,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499360720,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499360750,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,0 -1592499360785,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499360818,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499360848,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499360880,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499360911,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499360941,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499360971,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499361006,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,0 -1592499361042,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,0 -1592499361076,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499361111,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499361143,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499361173,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499361204,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499361234,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499361263,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499361295,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499361325,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499361355,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499361386,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499361415,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499361445,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499361475,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499361504,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499361535,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499361564,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499361595,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499361624,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499361655,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499361685,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499361714,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499361745,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499361775,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499361803,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499361832,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499361863,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499361895,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499361925,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499361954,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499361988,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499362019,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499362048,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499362079,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499362109,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499362141,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499362171,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499362199,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499362228,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499362258,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499362288,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499362319,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499362348,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499362378,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499362408,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499362438,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499362467,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499362495,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499362526,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499362554,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499362583,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499362616,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499362646,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499362679,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499362710,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499362738,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499362768,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499362799,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499362828,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499362860,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499362892,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499362923,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499362953,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499362983,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499363012,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499363045,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499363076,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499363109,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499363139,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499363168,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499363198,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499363228,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499363259,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499363291,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499363320,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499363355,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499363386,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499363416,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499363447,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499363476,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499363505,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499363533,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499363562,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499363595,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499363627,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499363659,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499363689,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499363719,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499363750,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499363779,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499363809,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499363840,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499363871,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499363901,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499363931,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499363961,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499363992,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499364022,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499364053,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499364084,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499364115,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499364146,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499364177,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499364209,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499364241,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499364268,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499364298,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499364331,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499364363,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499364396,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499364425,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499364451,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499364480,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499364509,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499364541,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499364570,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499364597,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499364625,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499364654,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499364682,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499364711,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499364737,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499364763,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499364796,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499364825,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499364852,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499364881,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499364909,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499364936,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499364963,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499364996,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499365026,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499365053,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499365080,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499365106,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499365133,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499365160,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499365189,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499365218,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499365248,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499365275,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499365304,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499365333,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499365361,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499365389,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499365415,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499365446,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499365475,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499365504,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499365531,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499365559,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499365587,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499365617,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499365646,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499365675,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499365703,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499365731,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499365759,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499365787,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499365815,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499365846,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499365874,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499365903,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499365931,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499365960,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499365991,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499366024,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499366052,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499366081,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499366110,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499366139,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499366167,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499366196,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499366225,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499366254,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499366284,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499366312,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499366339,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499366366,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499366396,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499366425,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499366454,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499366481,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499366509,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499366538,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499366566,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499366596,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499366624,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499366652,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499366682,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499366712,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499366738,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499366766,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499366796,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499366824,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499366856,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499366884,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499366913,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499366946,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499366976,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499367004,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499367034,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499367061,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499367088,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499367116,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499367146,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499367177,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499367204,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499367231,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499367259,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499367287,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499367317,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499367346,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499367375,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499367402,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499367431,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499367459,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499367486,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499367514,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499367546,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499367577,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499367605,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499367633,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499367660,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499367690,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499367718,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499367746,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499367774,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499367802,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499367830,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499367860,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499367888,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499367918,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499367946,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499367974,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499368002,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499368030,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499368058,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499368087,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499368115,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499368146,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499368174,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499368203,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499368232,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499368261,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499368290,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499368318,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499368346,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499368376,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499368404,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499368432,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499368460,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499368489,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499368522,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499368551,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499368578,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499368606,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499368635,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499368665,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499368696,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499368724,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499368751,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499368779,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499368805,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499368832,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499368859,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499368886,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499368912,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499368946,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499368974,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499369003,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499369030,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499369058,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499369085,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499369113,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499369146,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499369176,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499369204,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499369231,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499369256,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499369284,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499369311,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499369345,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499369372,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499369399,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499369427,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499369456,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499369482,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499369510,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499369539,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499369567,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499369595,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499369625,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499369651,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499369678,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499369706,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499369734,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499369762,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499369795,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499369824,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499369852,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499369881,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499369909,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499369935,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499369962,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499369995,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499370023,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499370052,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499370080,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499370107,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,1 -1592499370134,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499370160,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499370190,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499370217,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499370245,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499370273,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499370302,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499370331,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499370360,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499370390,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499370418,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499370447,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499370475,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499370505,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499370533,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499370560,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499370588,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499370617,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499370646,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499370675,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499370703,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499370730,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499370757,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499370786,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499370819,33,Scale Down squeezenet_v1.1,200,OK,tearDown squeezenet_v1.1 1-1,true,323,1,1,33,BRL06570.local,1 diff --git a/tests/performance/tmp/run345/call_jmx/merged.json b/tests/performance/tmp/run345/call_jmx/merged.json deleted file mode 100644 index 6907651ab..000000000 --- a/tests/performance/tmp/run345/call_jmx/merged.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "execution": [ - { - "concurrency": 1, - "ramp-up": "1s", - "hold-for": "40s", - "scenario": "Inference" - } - ], - "scenarios": { - "Inference": { - "script": "register_and_inference.jmx" - } - }, - "modules": { - "jmeter": { - "properties": { - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - } - } - }, - "reporting": [ - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail" - }, - { - "module": "junit-xml", - "data-source": "sample-labels" - }, - { - "module": "final-stats", - "dump-csv": "${BASEDIR}/final_stats.csv" - } - ], - "settings": { - "env": { - "BASEDIR": "." - } - } -} diff --git a/tests/performance/tmp/run345/call_jmx/merged.yml b/tests/performance/tmp/run345/call_jmx/merged.yml deleted file mode 100644 index 3a1ea52bf..000000000 --- a/tests/performance/tmp/run345/call_jmx/merged.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -execution: -- concurrency: 1 - hold-for: 40s - ramp-up: 1s - scenario: Inference -modules: - jmeter: - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - management_port: 8081 - port: 8080 - protocol: http -reporting: -- module: passfail -- data-source: pass-fail - module: junit-xml -- data-source: sample-labels - module: junit-xml -- dump-csv: ${BASEDIR}/final_stats.csv - module: final-stats -scenarios: - Inference: - script: register_and_inference.jmx -settings: - env: - BASEDIR: . - diff --git a/tests/performance/tmp/run345/call_jmx/modified_register_and_inference.jmx b/tests/performance/tmp/run345/call_jmx/modified_register_and_inference.jmx deleted file mode 100644 index ab9206013..000000000 --- a/tests/performance/tmp/run345/call_jmx/modified_register_and_inference.jmx +++ /dev/null @@ -1,176 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - cnn_url - https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - = - The url from where to fetch noop model from - - - scale_up_workers - ${__P(min_workers,1)} - = - The workers to scale No op model to - - - scale_down_workers - 0 - Offload the No-Op Model - = - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_up_workers} - PUT - true - false - true - false - - - - - - - continue11040S - - - - - ${__P(input_filepath)} - data - image/jpeg - - - - - - - - - - - /predictions/${model} - POST - true - false - true - true - - - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_down_workers} - DELETE - true - false - true - false - - - - - - - /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/call_jmx/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/call_jmx/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue - - diff --git a/tests/performance/tmp/run345/call_jmx/xunit-1.xml b/tests/performance/tmp/run345/call_jmx/xunit-1.xml deleted file mode 100644 index aaa49d5a4..000000000 --- a/tests/performance/tmp/run345/call_jmx/xunit-1.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/tests/performance/tmp/run345/call_jmx/xunit.xml b/tests/performance/tmp/run345/call_jmx/xunit.xml deleted file mode 100644 index 8bdd58dbc..000000000 --- a/tests/performance/tmp/run345/call_jmx/xunit.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/tmp/run345/downscale_workers/downscale_workers.yaml b/tests/performance/tmp/run345/downscale_workers/downscale_workers.yaml deleted file mode 100644 index 2cc6ce5a3..000000000 --- a/tests/performance/tmp/run345/downscale_workers/downscale_workers.yaml +++ /dev/null @@ -1,39 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 0s - iterations: 1 - scenario: Downscale - -scenarios: - Downscale: - script: downscale_workers.jmx - -services: - - module: shellexec - prepare: - - bzt tests/common/setup.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/ - post-process: - - bzt tests/common/teardown.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/ - - module: server_local_monitoring - ServerLocalClient: - - interval: 1s - metrics: - - total_workers - -modules: - server_local_monitoring: - class : metrics_monitoring_taurus.Monitor # monitoring class. - new_passfail: - class: new_passfail.PassFailStatus - -reporting: -- module: passfail - criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers - condition: '<' - threshold: 2 - timeframe: 1s - stop : true - fail : true \ No newline at end of file diff --git a/tests/performance/tmp/run345/downscale_workers/effective.json b/tests/performance/tmp/run345/downscale_workers/effective.json deleted file mode 100644 index 64e867e36..000000000 --- a/tests/performance/tmp/run345/downscale_workers/effective.json +++ /dev/null @@ -1,376 +0,0 @@ -{ - "modules": { - "ab": { - "class": "bzt.modules.ab.ApacheBenchmarkExecutor" - }, - "gatling": { - "class": "bzt.modules.gatling.GatlingExecutor", - "properties": { - "gatling.data.file.bufferSize": 256 - } - }, - "grinder": { - "class": "bzt.modules.grinder.GrinderExecutor", - "properties": { - "grinder.useConsole": false - } - }, - "jmeter": { - "class": "bzt.modules.jmeter.JMeterExecutor", - "protocol-handlers": { - "http": "bzt.jmx.http.HTTPProtocolHandler" - }, - "plugins": [ - "jpgc-casutg", - "jpgc-dummy", - "jpgc-ffw", - "jpgc-fifo", - "jpgc-functions", - "jpgc-json", - "jpgc-perfmon", - "jpgc-prmctl", - "jpgc-tst" - ], - "properties": { - "jmeter.save.saveservice.autoflush": "true", - "jmeter.save.saveservice.connect_time": "true", - "jmeterengine.force.system.exit": "true", - "summariser.name": "", - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - }, - "version": "5.2.1", - "path": "/Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh" - }, - "locust": { - "class": "bzt.modules.locustio.LocustIOExecutor" - }, - "molotov": { - "class": "bzt.modules.molotov.MolotovExecutor" - }, - "pbench": { - "class": "bzt.modules.pbench.PBenchExecutor" - }, - "siege": { - "class": "bzt.modules.siege.SiegeExecutor" - }, - "tsung": { - "class": "bzt.modules.tsung.TsungExecutor" - }, - "selenium": { - "class": "bzt.modules.selenium.SeleniumExecutor" - }, - "junit": { - "class": "bzt.modules.java.JUnitTester" - }, - "testng": { - "class": "bzt.modules.java.TestNGTester" - }, - "rspec": { - "class": "bzt.modules.ruby.RSpecTester" - }, - "mocha": { - "class": "bzt.modules.javascript.MochaTester" - }, - "nunit": { - "class": "bzt.modules.csharp.NUnitExecutor" - }, - "pytest": { - "class": "bzt.modules.pytest.PyTestExecutor" - }, - "wdio": { - "class": "bzt.modules.javascript.WebdriverIOExecutor" - }, - "robot": { - "class": "bzt.modules.robot.RobotExecutor" - }, - "newman": { - "class": "bzt.modules.javascript.NewmanExecutor" - }, - "apiritif": { - "class": "bzt.modules.apiritif.ApiritifNoseExecutor" - }, - "local": { - "class": "bzt.modules.provisioning.Local" - }, - "monitoring": { - "class": "bzt.modules.monitoring.Monitoring" - }, - "passfail": { - "class": "bzt.modules.passfail.PassFailStatus" - }, - "shellexec": { - "class": "bzt.modules.shellexec.ShellExecutor" - }, - "junit-xml": { - "class": "bzt.modules.reporting.JUnitXMLReporter" - }, - "install-checker": { - "class": "bzt.modules.services.InstallChecker" - }, - "console": { - "class": "bzt.modules.console.ConsoleStatusReporter", - "disable": true - }, - "consolidator": { - "class": "bzt.modules.aggregator.ConsolidatingAggregator", - "percentiles": [ - 0.0, - 50.0, - 90.0, - 95.0, - 99.0, - 99.9, - 100.0 - ] - }, - "final-stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "functional-consolidator": { - "class": "bzt.modules.functional.FunctionalAggregator" - }, - "android-emulator": { - "class": "bzt.modules.services.AndroidEmulatorLoader" - }, - "appium": { - "class": "bzt.modules.services.AppiumLoader" - }, - "virtual-display": { - "class": "bzt.modules.services.VirtualDisplay" - }, - "external-results-loader": { - "class": "bzt.modules.external.ExternalResultsLoader" - }, - "blazemeter": { - "class": "bzt.modules.blazemeter.BlazeMeterUploader" - }, - "cloud": { - "class": "bzt.modules.blazemeter.CloudProvisioning", - "default-location": "us-central1-a" - }, - "proxy2jmx": { - "class": "bzt.modules.proxy2jmx.Proxy2JMX" - }, - "unpacker": { - "class": "bzt.modules.services.Unpacker" - }, - "screenshoter": { - "class": "bzt.modules.blazemeter.ServiceStubScreenshoter" - }, - "capturehar": { - "class": "bzt.modules.blazemeter.ServiceStubCaptureHAR" - }, - "final_stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "server_local_monitoring": { - "class": "metrics_monitoring_taurus.Monitor" - }, - "new_passfail": { - "class": "new_passfail.PassFailStatus" - } - }, - "cli-aliases": { - "cloud": { - "provisioning": "cloud" - }, - "detach": { - "modules": { - "cloud": { - "detach": true - } - } - }, - "func": { - "modules": { - "console": { - "disable": true - } - }, - "settings": { - "aggregator": "functional-consolidator" - } - }, - "gui": { - "modules": { - "jmeter": { - "gui": true - } - } - }, - "install-tools": { - "services": [ - "install-checker" - ] - }, - "local": { - "provisioning": "local" - }, - "locations": { - "modules": { - "cloud": { - "dump-locations": true - } - }, - "provisioning": "cloud" - }, - "report": { - "reporting": [ - "blazemeter" - ] - }, - "public": { - "modules": { - "blazemeter": { - "public-report": true - } - } - }, - "sequential": { - "modules": { - "local": { - "sequential": true - } - } - }, - "lint": { - "cli": { - "linter": { - "lint-and-exit": true - } - } - }, - "nolint": { - "cli": { - "linter": { - "disable": true - } - } - } - }, - "cli": { - "linter": { - "disable": false, - "warn-on-unfamiliar-fields": false, - "ignored-warnings": [], - "lint-and-exit": false - } - }, - "settings": { - "aggregator": "consolidator", - "default-executor": "jmeter", - "env": { - "OBJC_DISABLE_INITIALIZE_FORK_SAFETY": "YES", - "BASEDIR": "tmp/run345/downscale_workers", - "TAURUS_ARTIFACTS_DIR": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/downscale_workers" - }, - "artifacts-dir": "tmp/run345/downscale_workers" - }, - "provisioning": "local", - "reporting": [ - { - "module": "final-stats" - }, - { - "module": "console" - }, - { - "module": "passfail", - "criteria": [ - { - "class": "bzt.modules.monitoring.MonitoringCriteria", - "subject": "ServerLocalClient/total_workers", - "condition": "<", - "threshold": 2, - "timeframe": "1s", - "stop": true, - "fail": true - } - ] - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/downscale_workers/xunit.xml" - }, - { - "module": "junit-xml", - "data-source": "sample-labels", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/downscale_workers/xunit-1.xml" - }, - { - "module": "final-stats", - "dump-csv": "tmp/run345/downscale_workers/final_stats.csv" - } - ], - "services": [ - { - "module": "monitoring", - "local": [ - { - "metrics": [ - "cpu", - "mem", - "bytes-sent", - "bytes-recv", - "disk-read", - "disk-write", - "disk-space", - "engine-loop", - "conn-all" - ] - } - ] - }, - { - "module": "shellexec", - "prepare": [ - { - "command": "bzt tests/common/setup.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/" - } - ], - "post-process": [ - { - "command": "bzt tests/common/teardown.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/" - } - ] - }, - { - "module": "server_local_monitoring", - "ServerLocalClient": [ - { - "interval": "1s", - "metrics": [ - "total_workers" - ] - } - ] - } - ], - "execution": [ - { - "concurrency": { - "local": 1 - }, - "ramp-up": "0s", - "iterations": 1, - "scenario": "Downscale", - "executor": "jmeter", - "throughput": {} - } - ], - "scenarios": { - "Downscale": { - "script": "/Users/mahesh/git/multi-model-server/tests/performance/tests/downscale_workers.jmx" - } - }, - "included-configs": [], - "version": "1.14.2" -} diff --git a/tests/performance/tmp/run345/downscale_workers/effective.yml b/tests/performance/tmp/run345/downscale_workers/effective.yml deleted file mode 100644 index 3aee6e8c2..000000000 --- a/tests/performance/tmp/run345/downscale_workers/effective.yml +++ /dev/null @@ -1,245 +0,0 @@ ---- -cli: - linter: - disable: false - ignored-warnings: [] - lint-and-exit: false - warn-on-unfamiliar-fields: false -cli-aliases: - cloud: - provisioning: cloud - detach: - modules: - cloud: - detach: true - func: - modules: - console: - disable: true - settings: - aggregator: functional-consolidator - gui: - modules: - jmeter: - gui: true - install-tools: - services: - - install-checker - lint: - cli: - linter: - lint-and-exit: true - local: - provisioning: local - locations: - modules: - cloud: - dump-locations: true - provisioning: cloud - nolint: - cli: - linter: - disable: true - public: - modules: - blazemeter: - public-report: true - report: - reporting: - - blazemeter - sequential: - modules: - local: - sequential: true -execution: -- concurrency: - local: 1 - executor: jmeter - iterations: 1 - ramp-up: 0s - scenario: Downscale - throughput: {} -included-configs: [] -modules: - ab: - class: bzt.modules.ab.ApacheBenchmarkExecutor - android-emulator: - class: bzt.modules.services.AndroidEmulatorLoader - apiritif: - class: bzt.modules.apiritif.ApiritifNoseExecutor - appium: - class: bzt.modules.services.AppiumLoader - blazemeter: - class: bzt.modules.blazemeter.BlazeMeterUploader - capturehar: - class: bzt.modules.blazemeter.ServiceStubCaptureHAR - cloud: - class: bzt.modules.blazemeter.CloudProvisioning - default-location: us-central1-a - console: - class: bzt.modules.console.ConsoleStatusReporter - disable: true - consolidator: - class: bzt.modules.aggregator.ConsolidatingAggregator - percentiles: - - 0.0 - - 50.0 - - 90.0 - - 95.0 - - 99.0 - - 99.9 - - 100.0 - external-results-loader: - class: bzt.modules.external.ExternalResultsLoader - final-stats: - class: bzt.modules.reporting.FinalStatus - final_stats: - class: bzt.modules.reporting.FinalStatus - functional-consolidator: - class: bzt.modules.functional.FunctionalAggregator - gatling: - class: bzt.modules.gatling.GatlingExecutor - properties: - gatling.data.file.bufferSize: 256 - grinder: - class: bzt.modules.grinder.GrinderExecutor - properties: - grinder.useConsole: false - install-checker: - class: bzt.modules.services.InstallChecker - jmeter: - class: bzt.modules.jmeter.JMeterExecutor - path: /Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh - plugins: - - jpgc-casutg - - jpgc-dummy - - jpgc-ffw - - jpgc-fifo - - jpgc-functions - - jpgc-json - - jpgc-perfmon - - jpgc-prmctl - - jpgc-tst - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - jmeter.save.saveservice.autoflush: 'true' - jmeter.save.saveservice.connect_time: 'true' - jmeterengine.force.system.exit: 'true' - management_port: 8081 - port: 8080 - protocol: http - summariser.name: '' - protocol-handlers: - http: bzt.jmx.http.HTTPProtocolHandler - version: 5.2.1 - junit: - class: bzt.modules.java.JUnitTester - junit-xml: - class: bzt.modules.reporting.JUnitXMLReporter - local: - class: bzt.modules.provisioning.Local - locust: - class: bzt.modules.locustio.LocustIOExecutor - mocha: - class: bzt.modules.javascript.MochaTester - molotov: - class: bzt.modules.molotov.MolotovExecutor - monitoring: - class: bzt.modules.monitoring.Monitoring - new_passfail: - class: new_passfail.PassFailStatus - newman: - class: bzt.modules.javascript.NewmanExecutor - nunit: - class: bzt.modules.csharp.NUnitExecutor - passfail: - class: bzt.modules.passfail.PassFailStatus - pbench: - class: bzt.modules.pbench.PBenchExecutor - proxy2jmx: - class: bzt.modules.proxy2jmx.Proxy2JMX - pytest: - class: bzt.modules.pytest.PyTestExecutor - robot: - class: bzt.modules.robot.RobotExecutor - rspec: - class: bzt.modules.ruby.RSpecTester - screenshoter: - class: bzt.modules.blazemeter.ServiceStubScreenshoter - selenium: - class: bzt.modules.selenium.SeleniumExecutor - server_local_monitoring: - class: metrics_monitoring_taurus.Monitor - shellexec: - class: bzt.modules.shellexec.ShellExecutor - siege: - class: bzt.modules.siege.SiegeExecutor - testng: - class: bzt.modules.java.TestNGTester - tsung: - class: bzt.modules.tsung.TsungExecutor - unpacker: - class: bzt.modules.services.Unpacker - virtual-display: - class: bzt.modules.services.VirtualDisplay - wdio: - class: bzt.modules.javascript.WebdriverIOExecutor -provisioning: local -reporting: -- module: final-stats -- module: console -- criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - condition: < - fail: true - stop: true - subject: ServerLocalClient/total_workers - threshold: 2 - timeframe: 1s - module: passfail -- module: passfail -- data-source: pass-fail - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/downscale_workers/xunit.xml - module: junit-xml -- data-source: sample-labels - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/downscale_workers/xunit-1.xml - module: junit-xml -- dump-csv: tmp/run345/downscale_workers/final_stats.csv - module: final-stats -scenarios: - Downscale: - script: /Users/mahesh/git/multi-model-server/tests/performance/tests/downscale_workers.jmx -services: -- local: - - metrics: - - cpu - - mem - - bytes-sent - - bytes-recv - - disk-read - - disk-write - - disk-space - - engine-loop - - conn-all - module: monitoring -- module: shellexec - post-process: - - command: bzt tests/common/teardown.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/ - prepare: - - command: bzt tests/common/setup.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/ -- ServerLocalClient: - - interval: 1s - metrics: - - total_workers - module: server_local_monitoring -settings: - aggregator: consolidator - artifacts-dir: tmp/run345/downscale_workers - default-executor: jmeter - env: - BASEDIR: tmp/run345/downscale_workers - OBJC_DISABLE_INITIALIZE_FORK_SAFETY: 'YES' - TAURUS_ARTIFACTS_DIR: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/downscale_workers -version: 1.14.2 - diff --git a/tests/performance/tmp/run345/downscale_workers/error.jtl b/tests/performance/tmp/run345/downscale_workers/error.jtl deleted file mode 100644 index 32bb536a8..000000000 --- a/tests/performance/tmp/run345/downscale_workers/error.jtl +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/tmp/run345/downscale_workers/final_stats.csv b/tests/performance/tmp/run345/downscale_workers/final_stats.csv deleted file mode 100644 index b658f1a6b..000000000 --- a/tests/performance/tmp/run345/downscale_workers/final_stats.csv +++ /dev/null @@ -1,3 +0,0 @@ -label,avg_ct,avg_lt,avg_rt,bytes,concurrency,fail,stdev_rt,succ,throughput,perc_0.0,perc_50.0,perc_90.0,perc_95.0,perc_99.0,perc_99.9,perc_100.0,rc_202 -Scale Down Workers squeezenet_v1.1,0.03700,0.05200,0.05500,319,1,0,0.00000,1,1,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,1 -,0.03700,0.05200,0.05500,319,1,0,0.00000,1,1,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,0.05500,1 diff --git a/tests/performance/tmp/run345/downscale_workers/global_config.yaml b/tests/performance/tmp/run345/downscale_workers/global_config.yaml deleted file mode 100644 index 94731f4a3..000000000 --- a/tests/performance/tmp/run345/downscale_workers/global_config.yaml +++ /dev/null @@ -1,26 +0,0 @@ -modules: - jmeter: - # These are JMeter test case properties. These variables are used in jmx files. - # Change the vaues as per your setup - properties: - hostname : 127.0.0.1 # MMS properties - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg # make sure jpg is available at this path - # if relative path is provided this will be relative to current working directory - -# DO-NOT change properties below unless you know what you are doing. -# They are needed for performance test suite runner script. -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -settings: - env: - BASEDIR : '.' diff --git a/tests/performance/tmp/run345/downscale_workers/jmeter-bzt.properties b/tests/performance/tmp/run345/downscale_workers/jmeter-bzt.properties deleted file mode 100644 index 4e30a5fa5..000000000 --- a/tests/performance/tmp/run345/downscale_workers/jmeter-bzt.properties +++ /dev/null @@ -1,14 +0,0 @@ -jmeter.save.saveservice.autoflush=true -jmeter.save.saveservice.connect_time=true -jmeterengine.force.system.exit=true -summariser.name= -hostname=127.0.0.1 -port=8080 -management_port=8081 -protocol=http -input_filepath=kitten.jpg -jmeterengine.nongui.port=4445 -jmeterengine.nongui.maxport=4445 -jmeter.save.saveservice.timestamp_format=ms -sampleresult.default.encoding=UTF-8 -user.classpath=/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/downscale_workers:/Users/mahesh/git/multi-model-server/tests/performance/tests diff --git a/tests/performance/tmp/run345/downscale_workers/jmeter.err b/tests/performance/tmp/run345/downscale_workers/jmeter.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/performance/tmp/run345/downscale_workers/jmeter.out b/tests/performance/tmp/run345/downscale_workers/jmeter.out deleted file mode 100644 index 105deb34c..000000000 --- a/tests/performance/tmp/run345/downscale_workers/jmeter.out +++ /dev/null @@ -1,5 +0,0 @@ -Created the tree successfully using /Users/mahesh/git/multi-model-server/tests/performance/tests/modified_downscale_workers.jmx -Starting standalone test @ Thu Jun 18 22:21:52 IST 2020 (1592499112975) -Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445 -Tidying up ... @ Thu Jun 18 22:21:53 IST 2020 (1592499113595) -... end of run diff --git a/tests/performance/tmp/run345/downscale_workers/kpi.jtl b/tests/performance/tmp/run345/downscale_workers/kpi.jtl deleted file mode 100644 index 40436c483..000000000 --- a/tests/performance/tmp/run345/downscale_workers/kpi.jtl +++ /dev/null @@ -1,2 +0,0 @@ -timeStamp,elapsed,label,responseCode,responseMessage,threadName,success,bytes,grpThreads,allThreads,Latency,Hostname,Connect -1592499113536,55,Scale Down Workers squeezenet_v1.1,202,Accepted,Scale Down Workers squeezenet_v1.1 1-1,true,319,1,1,52,BRL06570.local,37 diff --git a/tests/performance/tmp/run345/downscale_workers/merged.json b/tests/performance/tmp/run345/downscale_workers/merged.json deleted file mode 100644 index c97fff7cd..000000000 --- a/tests/performance/tmp/run345/downscale_workers/merged.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "execution": [ - { - "concurrency": 1, - "ramp-up": "0s", - "iterations": 1, - "scenario": "Downscale" - } - ], - "scenarios": { - "Downscale": { - "script": "downscale_workers.jmx" - } - }, - "services": [ - { - "module": "shellexec", - "prepare": [ - "bzt tests/common/setup.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/" - ], - "post-process": [ - "bzt tests/common/teardown.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/" - ] - }, - { - "module": "server_local_monitoring", - "ServerLocalClient": [ - { - "interval": "1s", - "metrics": [ - "total_workers" - ] - } - ] - } - ], - "modules": { - "server_local_monitoring": { - "class": "metrics_monitoring_taurus.Monitor" - }, - "new_passfail": { - "class": "new_passfail.PassFailStatus" - }, - "jmeter": { - "properties": { - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - } - } - }, - "reporting": [ - { - "module": "passfail", - "criteria": [ - { - "class": "bzt.modules.monitoring.MonitoringCriteria", - "subject": "ServerLocalClient/total_workers", - "condition": "<", - "threshold": 2, - "timeframe": "1s", - "stop": true, - "fail": true - } - ] - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail" - }, - { - "module": "junit-xml", - "data-source": "sample-labels" - }, - { - "module": "final-stats", - "dump-csv": "${BASEDIR}/final_stats.csv" - } - ], - "settings": { - "env": { - "BASEDIR": "." - } - } -} diff --git a/tests/performance/tmp/run345/downscale_workers/merged.yml b/tests/performance/tmp/run345/downscale_workers/merged.yml deleted file mode 100644 index c2aac9e47..000000000 --- a/tests/performance/tmp/run345/downscale_workers/merged.yml +++ /dev/null @@ -1,53 +0,0 @@ ---- -execution: -- concurrency: 1 - iterations: 1 - ramp-up: 0s - scenario: Downscale -modules: - jmeter: - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - management_port: 8081 - port: 8080 - protocol: http - new_passfail: - class: new_passfail.PassFailStatus - server_local_monitoring: - class: metrics_monitoring_taurus.Monitor -reporting: -- criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - condition: < - fail: true - stop: true - subject: ServerLocalClient/total_workers - threshold: 2 - timeframe: 1s - module: passfail -- module: passfail -- data-source: pass-fail - module: junit-xml -- data-source: sample-labels - module: junit-xml -- dump-csv: ${BASEDIR}/final_stats.csv - module: final-stats -scenarios: - Downscale: - script: downscale_workers.jmx -services: -- module: shellexec - post-process: - - bzt tests/common/teardown.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/ - prepare: - - bzt tests/common/setup.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/ -- ServerLocalClient: - - interval: 1s - metrics: - - total_workers - module: server_local_monitoring -settings: - env: - BASEDIR: . - diff --git a/tests/performance/tmp/run345/downscale_workers/modified_downscale_workers.jmx b/tests/performance/tmp/run345/downscale_workers/modified_downscale_workers.jmx deleted file mode 100644 index 21e156254..000000000 --- a/tests/performance/tmp/run345/downscale_workers/modified_downscale_workers.jmx +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - scale_down_workers - 2 - Offload the No-Op Model - = - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - continuefalse110false0 - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_down_workers} - PUT - true - false - true - false - - - - - - - /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/downscale_workers/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/downscale_workers/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue - - diff --git a/tests/performance/tmp/run345/downscale_workers/xunit-1.xml b/tests/performance/tmp/run345/downscale_workers/xunit-1.xml deleted file mode 100644 index d739fb65b..000000000 --- a/tests/performance/tmp/run345/downscale_workers/xunit-1.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/tests/performance/tmp/run345/downscale_workers/xunit.xml b/tests/performance/tmp/run345/downscale_workers/xunit.xml deleted file mode 100644 index b0728211c..000000000 --- a/tests/performance/tmp/run345/downscale_workers/xunit.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/tests/performance/tmp/run345/inference_server_monitoring/SAlogs_localhost_9009.csv b/tests/performance/tmp/run345/inference_server_monitoring/SAlogs_localhost_9009.csv deleted file mode 100644 index af3a23ace..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/SAlogs_localhost_9009.csv +++ /dev/null @@ -1,73 +0,0 @@ -ts,frontend_file_descriptors,sum_all_cpu_percent,sum_workers_memory_percent,total_workers -1592498984,0.0,0,63, -1592498985,0.1,0,63, -1592498986,0.2,0,65, -1592498987,8.4,0,65, -1592498989,1.4,0,66, -1592498990,1.0,0,66, -1592498991,0.8,0,66, -1592498992,1.1,0,66, -1592498993,0.5,0,66, -1592498994,1.3,0,66, -1592498995,1.1,0,66, -1592498996,1.0,0,66, -1592498997,1.3,0,66, -1592498998,1.1,0,66, -1592498999,0.2,0,66, -1592499000,1.1,0,66, -1592499001,0.8,0,66, -1592499002,1.0,0,66, -1592499003,1.6,0,66, -1592499004,0.5,0,66, -1592499005,0.5,0,66, -1592499006,0.9,0,66, -1592499007,1.1,0,66, -1592499008,1.2,0,66, -1592499009,0.2,0,66, -1592499010,0.7,0,66, -1592499011,1.4,0,66, -1592499012,1.6,0,66, -1592499013,1.1,0,66, -1592499014,0.6,0,66, -1592499015,0.5,0,66, -1592499016,0.6,0,66, -1592499017,1.0,0,66, -1592499018,0.9,0,66, -1592499019,0.9,0,66, -1592499020,0.7,0,66, -1592499021,0.8,0,66, -1592499022,0.7,0,66, -1592499023,0.8,0,66, -1592499024,1.3,0,66, -1592499025,0.6,0,66, -1592499026,0.9,0,66, -1592499027,1.1,0,66, -1592499028,1.4,0,66, -1592499029,0.5,0,66, -1592499030,1.2,0,66, -1592499031,0.9,0,66, -1592499032,1.3,0,66, -1592499033,1.1,0,66, -1592499034,1.1,0,66, -1592499035,1.9,0.3190279006958008,67, -1592499036,0.4,0.6034374237060547,71, -1592499037,5.8,0.8042573928833008,73, -1592499038,6.4,0.8053302764892578,73, -1592499039,5.9,0.8058071136474609,73, -1592499040,8.0,0.8053064346313477,73, -1592499041,6.3,0.8068323135375977,73, -1592499042,5.9,0.806427001953125,73, -1592499043,6.0,0.8073568344116211,73, -1592499044,5.9,0.8061408996582031,73, -1592499045,5.9,0.8072614669799805,73, -1592499046,5.9,0.8058309555053711,73, -1592499047,5.9,0.8078336715698242,73, -1592499048,7.4,0.8059024810791016,73, -1592499049,5.8,0.8071422576904297,73, -1592499050,6.2,0.8059501647949219,73, -1592499051,5.9,0.8068084716796875,73, -1592499052,5.9,0.8070230484008789,73, -1592499053,5.9,0.8072376251220703,73, -1592499054,6.0,0.8063077926635742,73, -1592499055,6.0,0.8073091506958008,73, -1592499056,6.4,0,63, diff --git a/tests/performance/tmp/run345/inference_server_monitoring/effective.json b/tests/performance/tmp/run345/inference_server_monitoring/effective.json deleted file mode 100644 index 0077d2719..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/effective.json +++ /dev/null @@ -1,363 +0,0 @@ -{ - "modules": { - "ab": { - "class": "bzt.modules.ab.ApacheBenchmarkExecutor" - }, - "gatling": { - "class": "bzt.modules.gatling.GatlingExecutor", - "properties": { - "gatling.data.file.bufferSize": 256 - } - }, - "grinder": { - "class": "bzt.modules.grinder.GrinderExecutor", - "properties": { - "grinder.useConsole": false - } - }, - "jmeter": { - "class": "bzt.modules.jmeter.JMeterExecutor", - "protocol-handlers": { - "http": "bzt.jmx.http.HTTPProtocolHandler" - }, - "plugins": [ - "jpgc-casutg", - "jpgc-dummy", - "jpgc-ffw", - "jpgc-fifo", - "jpgc-functions", - "jpgc-json", - "jpgc-perfmon", - "jpgc-prmctl", - "jpgc-tst" - ], - "properties": { - "jmeter.save.saveservice.autoflush": "true", - "jmeter.save.saveservice.connect_time": "true", - "jmeterengine.force.system.exit": "true", - "summariser.name": "", - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - }, - "version": "5.2.1", - "path": "/Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh" - }, - "locust": { - "class": "bzt.modules.locustio.LocustIOExecutor" - }, - "molotov": { - "class": "bzt.modules.molotov.MolotovExecutor" - }, - "pbench": { - "class": "bzt.modules.pbench.PBenchExecutor" - }, - "siege": { - "class": "bzt.modules.siege.SiegeExecutor" - }, - "tsung": { - "class": "bzt.modules.tsung.TsungExecutor" - }, - "selenium": { - "class": "bzt.modules.selenium.SeleniumExecutor" - }, - "junit": { - "class": "bzt.modules.java.JUnitTester" - }, - "testng": { - "class": "bzt.modules.java.TestNGTester" - }, - "rspec": { - "class": "bzt.modules.ruby.RSpecTester" - }, - "mocha": { - "class": "bzt.modules.javascript.MochaTester" - }, - "nunit": { - "class": "bzt.modules.csharp.NUnitExecutor" - }, - "pytest": { - "class": "bzt.modules.pytest.PyTestExecutor" - }, - "wdio": { - "class": "bzt.modules.javascript.WebdriverIOExecutor" - }, - "robot": { - "class": "bzt.modules.robot.RobotExecutor" - }, - "newman": { - "class": "bzt.modules.javascript.NewmanExecutor" - }, - "apiritif": { - "class": "bzt.modules.apiritif.ApiritifNoseExecutor" - }, - "local": { - "class": "bzt.modules.provisioning.Local" - }, - "monitoring": { - "class": "bzt.modules.monitoring.Monitoring" - }, - "passfail": { - "class": "bzt.modules.passfail.PassFailStatus" - }, - "shellexec": { - "class": "bzt.modules.shellexec.ShellExecutor" - }, - "junit-xml": { - "class": "bzt.modules.reporting.JUnitXMLReporter" - }, - "install-checker": { - "class": "bzt.modules.services.InstallChecker" - }, - "console": { - "class": "bzt.modules.console.ConsoleStatusReporter", - "disable": true - }, - "consolidator": { - "class": "bzt.modules.aggregator.ConsolidatingAggregator", - "percentiles": [ - 0.0, - 50.0, - 90.0, - 95.0, - 99.0, - 99.9, - 100.0 - ] - }, - "final-stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "functional-consolidator": { - "class": "bzt.modules.functional.FunctionalAggregator" - }, - "android-emulator": { - "class": "bzt.modules.services.AndroidEmulatorLoader" - }, - "appium": { - "class": "bzt.modules.services.AppiumLoader" - }, - "virtual-display": { - "class": "bzt.modules.services.VirtualDisplay" - }, - "external-results-loader": { - "class": "bzt.modules.external.ExternalResultsLoader" - }, - "blazemeter": { - "class": "bzt.modules.blazemeter.BlazeMeterUploader" - }, - "cloud": { - "class": "bzt.modules.blazemeter.CloudProvisioning", - "default-location": "us-central1-a" - }, - "proxy2jmx": { - "class": "bzt.modules.proxy2jmx.Proxy2JMX" - }, - "unpacker": { - "class": "bzt.modules.services.Unpacker" - }, - "screenshoter": { - "class": "bzt.modules.blazemeter.ServiceStubScreenshoter" - }, - "capturehar": { - "class": "bzt.modules.blazemeter.ServiceStubCaptureHAR" - }, - "final_stats": { - "class": "bzt.modules.reporting.FinalStatus" - } - }, - "cli-aliases": { - "cloud": { - "provisioning": "cloud" - }, - "detach": { - "modules": { - "cloud": { - "detach": true - } - } - }, - "func": { - "modules": { - "console": { - "disable": true - } - }, - "settings": { - "aggregator": "functional-consolidator" - } - }, - "gui": { - "modules": { - "jmeter": { - "gui": true - } - } - }, - "install-tools": { - "services": [ - "install-checker" - ] - }, - "local": { - "provisioning": "local" - }, - "locations": { - "modules": { - "cloud": { - "dump-locations": true - } - }, - "provisioning": "cloud" - }, - "report": { - "reporting": [ - "blazemeter" - ] - }, - "public": { - "modules": { - "blazemeter": { - "public-report": true - } - } - }, - "sequential": { - "modules": { - "local": { - "sequential": true - } - } - }, - "lint": { - "cli": { - "linter": { - "lint-and-exit": true - } - } - }, - "nolint": { - "cli": { - "linter": { - "disable": true - } - } - } - }, - "cli": { - "linter": { - "disable": false, - "warn-on-unfamiliar-fields": false, - "ignored-warnings": [], - "lint-and-exit": false - } - }, - "settings": { - "aggregator": "consolidator", - "default-executor": "jmeter", - "env": { - "OBJC_DISABLE_INITIALIZE_FORK_SAFETY": "YES", - "BASEDIR": "tmp/run345/inference_server_monitoring", - "TAURUS_ARTIFACTS_DIR": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring" - }, - "artifacts-dir": "tmp/run345/inference_server_monitoring" - }, - "provisioning": "local", - "reporting": [ - { - "module": "final-stats" - }, - { - "module": "console" - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring/xunit.xml" - }, - { - "module": "junit-xml", - "data-source": "sample-labels", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring/xunit-1.xml" - }, - { - "module": "final-stats", - "dump-csv": "tmp/run345/inference_server_monitoring/final_stats.csv" - } - ], - "services": [ - { - "module": "monitoring", - "local": [ - { - "metrics": [ - "cpu", - "mem", - "bytes-sent", - "bytes-recv", - "disk-read", - "disk-write", - "disk-space", - "engine-loop", - "conn-all" - ] - } - ], - "server-agent": [ - { - "address": "localhost:9009", - "label": "mms-inference-server", - "interval": "1s", - "logging": true, - "metrics": [ - "sum_all_cpu_percent", - "sum_workers_memory_percent", - "frontend_file_descriptors", - "total_workers" - ] - } - ] - }, - { - "module": "monitoring", - "server-agent": [ - { - "address": "localhost:9009", - "label": "mms-inference-server", - "interval": "1s", - "logging": true, - "metrics": [ - "sum_all_cpu_percent", - "sum_workers_memory_percent", - "frontend_file_descriptors", - "total_workers" - ] - } - ] - } - ], - "execution": [ - { - "concurrency": { - "local": 4 - }, - "ramp-up": "1s", - "hold-for": "20s", - "scenario": "Inference", - "executor": "jmeter", - "throughput": {} - } - ], - "scenarios": { - "Inference": { - "script": "/Users/mahesh/git/multi-model-server/tests/performance/tests/register_and_inference.jmx" - } - }, - "included-configs": [], - "version": "1.14.2" -} diff --git a/tests/performance/tmp/run345/inference_server_monitoring/effective.yml b/tests/performance/tmp/run345/inference_server_monitoring/effective.yml deleted file mode 100644 index acd91e1aa..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/effective.yml +++ /dev/null @@ -1,236 +0,0 @@ ---- -cli: - linter: - disable: false - ignored-warnings: [] - lint-and-exit: false - warn-on-unfamiliar-fields: false -cli-aliases: - cloud: - provisioning: cloud - detach: - modules: - cloud: - detach: true - func: - modules: - console: - disable: true - settings: - aggregator: functional-consolidator - gui: - modules: - jmeter: - gui: true - install-tools: - services: - - install-checker - lint: - cli: - linter: - lint-and-exit: true - local: - provisioning: local - locations: - modules: - cloud: - dump-locations: true - provisioning: cloud - nolint: - cli: - linter: - disable: true - public: - modules: - blazemeter: - public-report: true - report: - reporting: - - blazemeter - sequential: - modules: - local: - sequential: true -execution: -- concurrency: - local: 4 - executor: jmeter - hold-for: 20s - ramp-up: 1s - scenario: Inference - throughput: {} -included-configs: [] -modules: - ab: - class: bzt.modules.ab.ApacheBenchmarkExecutor - android-emulator: - class: bzt.modules.services.AndroidEmulatorLoader - apiritif: - class: bzt.modules.apiritif.ApiritifNoseExecutor - appium: - class: bzt.modules.services.AppiumLoader - blazemeter: - class: bzt.modules.blazemeter.BlazeMeterUploader - capturehar: - class: bzt.modules.blazemeter.ServiceStubCaptureHAR - cloud: - class: bzt.modules.blazemeter.CloudProvisioning - default-location: us-central1-a - console: - class: bzt.modules.console.ConsoleStatusReporter - disable: true - consolidator: - class: bzt.modules.aggregator.ConsolidatingAggregator - percentiles: - - 0.0 - - 50.0 - - 90.0 - - 95.0 - - 99.0 - - 99.9 - - 100.0 - external-results-loader: - class: bzt.modules.external.ExternalResultsLoader - final-stats: - class: bzt.modules.reporting.FinalStatus - final_stats: - class: bzt.modules.reporting.FinalStatus - functional-consolidator: - class: bzt.modules.functional.FunctionalAggregator - gatling: - class: bzt.modules.gatling.GatlingExecutor - properties: - gatling.data.file.bufferSize: 256 - grinder: - class: bzt.modules.grinder.GrinderExecutor - properties: - grinder.useConsole: false - install-checker: - class: bzt.modules.services.InstallChecker - jmeter: - class: bzt.modules.jmeter.JMeterExecutor - path: /Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh - plugins: - - jpgc-casutg - - jpgc-dummy - - jpgc-ffw - - jpgc-fifo - - jpgc-functions - - jpgc-json - - jpgc-perfmon - - jpgc-prmctl - - jpgc-tst - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - jmeter.save.saveservice.autoflush: 'true' - jmeter.save.saveservice.connect_time: 'true' - jmeterengine.force.system.exit: 'true' - management_port: 8081 - port: 8080 - protocol: http - summariser.name: '' - protocol-handlers: - http: bzt.jmx.http.HTTPProtocolHandler - version: 5.2.1 - junit: - class: bzt.modules.java.JUnitTester - junit-xml: - class: bzt.modules.reporting.JUnitXMLReporter - local: - class: bzt.modules.provisioning.Local - locust: - class: bzt.modules.locustio.LocustIOExecutor - mocha: - class: bzt.modules.javascript.MochaTester - molotov: - class: bzt.modules.molotov.MolotovExecutor - monitoring: - class: bzt.modules.monitoring.Monitoring - newman: - class: bzt.modules.javascript.NewmanExecutor - nunit: - class: bzt.modules.csharp.NUnitExecutor - passfail: - class: bzt.modules.passfail.PassFailStatus - pbench: - class: bzt.modules.pbench.PBenchExecutor - proxy2jmx: - class: bzt.modules.proxy2jmx.Proxy2JMX - pytest: - class: bzt.modules.pytest.PyTestExecutor - robot: - class: bzt.modules.robot.RobotExecutor - rspec: - class: bzt.modules.ruby.RSpecTester - screenshoter: - class: bzt.modules.blazemeter.ServiceStubScreenshoter - selenium: - class: bzt.modules.selenium.SeleniumExecutor - shellexec: - class: bzt.modules.shellexec.ShellExecutor - siege: - class: bzt.modules.siege.SiegeExecutor - testng: - class: bzt.modules.java.TestNGTester - tsung: - class: bzt.modules.tsung.TsungExecutor - unpacker: - class: bzt.modules.services.Unpacker - virtual-display: - class: bzt.modules.services.VirtualDisplay - wdio: - class: bzt.modules.javascript.WebdriverIOExecutor -provisioning: local -reporting: -- module: final-stats -- module: console -- module: passfail -- data-source: pass-fail - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring/xunit.xml - module: junit-xml -- data-source: sample-labels - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring/xunit-1.xml - module: junit-xml -- dump-csv: tmp/run345/inference_server_monitoring/final_stats.csv - module: final-stats -scenarios: - Inference: - script: /Users/mahesh/git/multi-model-server/tests/performance/tests/register_and_inference.jmx -services: -- local: - - metrics: - - cpu - - mem - - bytes-sent - - bytes-recv - - disk-read - - disk-write - - disk-space - - engine-loop - - conn-all - module: monitoring - server-agent: - - &id001 - address: localhost:9009 - interval: 1s - label: mms-inference-server - logging: true - metrics: - - sum_all_cpu_percent - - sum_workers_memory_percent - - frontend_file_descriptors - - total_workers -- module: monitoring - server-agent: - - *id001 -settings: - aggregator: consolidator - artifacts-dir: tmp/run345/inference_server_monitoring - default-executor: jmeter - env: - BASEDIR: tmp/run345/inference_server_monitoring - OBJC_DISABLE_INITIALIZE_FORK_SAFETY: 'YES' - TAURUS_ARTIFACTS_DIR: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring -version: 1.14.2 - diff --git a/tests/performance/tmp/run345/inference_server_monitoring/error.jtl b/tests/performance/tmp/run345/inference_server_monitoring/error.jtl deleted file mode 100644 index 32bb536a8..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/error.jtl +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/tmp/run345/inference_server_monitoring/final_stats.csv b/tests/performance/tmp/run345/inference_server_monitoring/final_stats.csv deleted file mode 100644 index 845cb506f..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/final_stats.csv +++ /dev/null @@ -1,6 +0,0 @@ -label,avg_ct,avg_lt,avg_rt,bytes,concurrency,fail,stdev_rt,succ,throughput,perc_0.0,perc_50.0,perc_90.0,perc_95.0,perc_99.0,perc_99.9,perc_100.0,rc_200,rc_202 -Setup squeezenet_v1.1 Model,0.03000,48.70300,48.70500,321,1,0,0.01500,1,1,48.70400,48.70400,48.70400,48.70400,48.70400,48.70400,48.70400,1, -,0.00053,0.16682,0.16685,534075,1,0,1.74111,779,779,0.00400,0.10300,0.10500,0.10500,0.12400,48.70400,48.70400,778,1 -Scale Up squeezenet_v1.1 Model,0.00200,0.00400,0.00400,319,1,0,0.00000,1,1,0.00400,0.00400,0.00400,0.00400,0.00400,0.00400,0.00400,,1 -Inference Request,0.00049,0.10469,0.10471,533112,1,0,0.03667,776,776,0.10000,0.10300,0.10500,0.10500,0.12400,1.11700,1.11700,776, -Scale Down squeezenet_v1.1,0.00100,0.00800,0.00800,323,1,0,0.00000,1,1,0.00800,0.00800,0.00800,0.00800,0.00800,0.00800,0.00800,1, diff --git a/tests/performance/tmp/run345/inference_server_monitoring/global_config.yaml b/tests/performance/tmp/run345/inference_server_monitoring/global_config.yaml deleted file mode 100644 index 94731f4a3..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/global_config.yaml +++ /dev/null @@ -1,26 +0,0 @@ -modules: - jmeter: - # These are JMeter test case properties. These variables are used in jmx files. - # Change the vaues as per your setup - properties: - hostname : 127.0.0.1 # MMS properties - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg # make sure jpg is available at this path - # if relative path is provided this will be relative to current working directory - -# DO-NOT change properties below unless you know what you are doing. -# They are needed for performance test suite runner script. -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -settings: - env: - BASEDIR : '.' diff --git a/tests/performance/tmp/run345/inference_server_monitoring/inference_server_monitoring.yaml b/tests/performance/tmp/run345/inference_server_monitoring/inference_server_monitoring.yaml deleted file mode 100644 index 020a1ce2a..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/inference_server_monitoring.yaml +++ /dev/null @@ -1,26 +0,0 @@ - -execution: -- concurrency: 4 - ramp-up: 1s - hold-for: 20s - scenario: Inference - -scenarios: - Inference: - script: register_and_inference.jmx - - - -services: - - module: monitoring - server-agent: - - address: localhost:9009 # metric monitoring service address - label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port - interval: 1s # polling interval - logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir - metrics: # metrics should be supported by monitoring service - - sum_all_cpu_percent # cpu percent used by all the mms server processes and workers - - sum_workers_memory_percent - - frontend_file_descriptors - - total_workers # no of mms workers - diff --git a/tests/performance/tmp/run345/inference_server_monitoring/jmeter-bzt.properties b/tests/performance/tmp/run345/inference_server_monitoring/jmeter-bzt.properties deleted file mode 100644 index c186fd6f1..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/jmeter-bzt.properties +++ /dev/null @@ -1,14 +0,0 @@ -jmeter.save.saveservice.autoflush=true -jmeter.save.saveservice.connect_time=true -jmeterengine.force.system.exit=true -summariser.name= -hostname=127.0.0.1 -port=8080 -management_port=8081 -protocol=http -input_filepath=kitten.jpg -jmeterengine.nongui.port=4445 -jmeterengine.nongui.maxport=4445 -jmeter.save.saveservice.timestamp_format=ms -sampleresult.default.encoding=UTF-8 -user.classpath=/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring:/Users/mahesh/git/multi-model-server/tests/performance/tests diff --git a/tests/performance/tmp/run345/inference_server_monitoring/jmeter.err b/tests/performance/tmp/run345/inference_server_monitoring/jmeter.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/performance/tmp/run345/inference_server_monitoring/jmeter.out b/tests/performance/tmp/run345/inference_server_monitoring/jmeter.out deleted file mode 100644 index d1ca604bb..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/jmeter.out +++ /dev/null @@ -1,5 +0,0 @@ -Created the tree successfully using /Users/mahesh/git/multi-model-server/tests/performance/tests/modified_register_and_inference.jmx -Starting standalone test @ Thu Jun 18 22:19:44 IST 2020 (1592498984948) -Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445 -Tidying up ... @ Thu Jun 18 22:20:55 IST 2020 (1592499055493) -... end of run diff --git a/tests/performance/tmp/run345/inference_server_monitoring/kpi.jtl b/tests/performance/tmp/run345/inference_server_monitoring/kpi.jtl deleted file mode 100644 index 34c4bccf3..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/kpi.jtl +++ /dev/null @@ -1,780 +0,0 @@ -timeStamp,elapsed,label,responseCode,responseMessage,threadName,success,bytes,grpThreads,allThreads,Latency,Hostname,Connect -1592498985396,48705,Setup squeezenet_v1.1 Model,200,OK,setup squeezenet_v1.1 1-1,true,321,1,1,48703,BRL06570.local,30 -1592499034102,4,Scale Up squeezenet_v1.1 Model,202,Accepted,setup squeezenet_v1.1 1-1,true,319,1,1,4,BRL06570.local,2 -1592499034315,1117,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,1117,BRL06570.local,7 -1592499035321,138,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,138,BRL06570.local,1 -1592499035320,169,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,169,BRL06570.local,1 -1592499035320,196,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,196,BRL06570.local,1 -1592499035439,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499035460,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,111,BRL06570.local,1 -1592499035490,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,1 -1592499035517,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,107,BRL06570.local,1 -1592499035545,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,0 -1592499035571,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499035599,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499035625,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499035651,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499035676,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499035702,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499035729,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499035756,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499035780,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499035806,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499035831,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499035857,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499035884,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499035910,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499035935,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499035960,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,1 -1592499035986,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,1 -1592499036013,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,1 -1592499036038,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,111,BRL06570.local,0 -1592499036066,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,1 -1592499036093,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,1 -1592499036121,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499036149,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499036175,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499036200,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499036226,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499036251,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499036278,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499036303,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499036328,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499036354,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499036379,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499036405,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499036433,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499036457,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499036483,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499036508,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499036534,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499036562,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499036588,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499036613,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499036639,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499036664,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499036690,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499036716,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499036741,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499036766,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499036792,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499036818,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499036846,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499036872,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499036897,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,1 -1592499036923,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499036950,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499036976,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499037003,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499037028,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499037054,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499037079,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499037106,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499037132,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499037157,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499037183,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499037208,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499037235,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499037261,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499037287,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499037312,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499037337,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499037365,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499037391,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499037417,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499037442,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499037468,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499037493,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499037520,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499037545,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499037572,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499037596,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499037622,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499037648,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499037674,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499037699,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499037724,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499037750,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499037777,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499037803,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499037829,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499037853,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499037880,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499037907,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499037933,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499037958,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499037984,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499038009,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499038037,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499038063,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,0 -1592499038088,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499038113,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499038138,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499038163,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499038191,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499038216,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499038242,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499038267,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499038293,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499038319,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499038346,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499038371,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499038397,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499038424,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499038451,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499038476,124,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,124,BRL06570.local,1 -1592499038502,125,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,125,BRL06570.local,1 -1592499038528,125,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,125,BRL06570.local,0 -1592499038554,124,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,124,BRL06570.local,0 -1592499038601,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499038627,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499038653,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499038678,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499038705,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499038730,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499038756,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499038781,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499038807,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499038833,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499038859,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499038884,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499038910,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499038936,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499038963,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499038989,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499039015,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499039040,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499039066,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499039092,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499039119,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499039144,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499039169,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499039194,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499039221,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499039247,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499039273,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499039297,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499039323,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499039350,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499039377,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499039402,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499039428,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499039452,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499039480,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499039505,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499039531,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499039556,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499039582,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499039610,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499039636,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499039660,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499039686,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499039711,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499039738,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499039763,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499039789,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,106,BRL06570.local,0 -1592499039815,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,0 -1592499039842,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499039869,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499039895,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499039921,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499039948,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499039973,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499040000,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499040026,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499040052,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,1 -1592499040078,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499040104,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,0 -1592499040131,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499040158,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499040183,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499040209,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499040235,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499040262,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499040289,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499040315,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,0 -1592499040340,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,0 -1592499040366,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499040393,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499040420,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499040445,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499040471,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499040496,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499040522,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499040548,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499040576,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499040601,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499040626,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499040652,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499040680,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499040705,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499040731,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499040756,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499040782,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499040808,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499040834,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499040860,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499040886,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499040911,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499040939,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499040965,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499040991,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499041016,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499041041,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499041067,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499041094,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499041119,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499041144,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499041169,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499041196,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499041222,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499041249,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499041273,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499041299,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,1 -1592499041324,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,106,BRL06570.local,1 -1592499041352,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499041378,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499041405,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499041430,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499041455,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499041481,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499041509,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499041534,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499041560,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499041585,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499041611,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499041637,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499041663,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499041688,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499041714,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499041739,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499041766,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499041791,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499041817,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499041842,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499041868,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499041895,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499041923,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499041948,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499041973,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,106,BRL06570.local,1 -1592499041998,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,0 -1592499042027,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499042053,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499042079,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499042105,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499042130,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499042156,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499042183,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499042208,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499042234,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499042259,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499042285,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499042312,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499042339,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499042364,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499042389,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,106,BRL06570.local,1 -1592499042416,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499042443,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499042470,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499042495,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499042520,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499042546,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499042572,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499042600,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499042625,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,0 -1592499042651,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,1 -1592499042676,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499042704,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499042731,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499042757,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499042782,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499042807,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499042832,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499042860,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499042885,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499042911,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499042936,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499042962,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499042989,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499043014,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499043039,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499043065,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499043091,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499043118,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499043143,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499043169,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499043195,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499043220,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499043246,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499043273,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499043297,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499043323,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499043349,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499043375,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499043401,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499043427,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499043452,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499043478,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499043503,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499043530,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499043555,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499043582,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499043606,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499043633,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499043660,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499043687,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499043712,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499043737,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499043763,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499043790,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499043816,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499043842,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499043867,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,0 -1592499043892,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499043919,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499043947,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499043972,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499043997,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,1 -1592499044023,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499044050,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499044076,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499044103,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499044128,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499044154,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499044179,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499044206,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499044232,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499044257,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499044283,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,0 -1592499044309,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499044336,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499044363,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499044388,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499044414,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499044440,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499044466,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,0 -1592499044492,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499044519,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,0 -1592499044545,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499044571,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499044596,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499044625,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499044650,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499044675,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499044701,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499044726,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499044753,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499044779,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499044804,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499044829,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499044854,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499044883,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499044907,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499044934,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499044959,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499044985,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499045010,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499045037,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499045063,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499045088,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499045113,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499045139,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499045165,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499045191,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499045216,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499045242,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499045268,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499045295,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499045320,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499045346,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499045372,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499045398,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499045424,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499045450,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499045477,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499045503,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499045527,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499045555,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499045580,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499045605,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499045630,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499045657,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499045682,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499045709,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499045734,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499045760,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499045785,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499045810,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499045837,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499045863,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499045888,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499045913,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499045939,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499045966,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499045991,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499046017,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499046042,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499046068,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499046093,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499046119,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,0 -1592499046144,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499046169,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499046194,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499046220,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499046246,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499046272,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499046296,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499046322,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499046348,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499046375,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499046401,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499046426,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499046451,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499046477,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499046503,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499046529,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499046554,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499046580,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499046605,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499046631,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499046658,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499046685,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499046711,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499046737,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499046762,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499046789,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499046814,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499046839,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499046864,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499046890,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499046916,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499046942,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,106,BRL06570.local,1 -1592499046969,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499046995,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499047021,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499047048,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499047073,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499047099,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499047124,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499047149,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499047176,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499047201,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499047226,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499047252,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499047276,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499047302,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499047329,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499047354,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499047379,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499047405,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499047431,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499047458,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499047483,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499047508,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499047533,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499047560,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499047586,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499047611,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499047636,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499047662,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499047687,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499047714,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499047738,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499047764,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499047789,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499047815,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499047840,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499047868,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499047893,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499047919,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499047945,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499047971,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499047997,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499048023,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499048048,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499048073,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499048099,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499048126,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499048151,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499048176,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499048202,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499048227,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499048253,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499048281,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499048306,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499048331,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499048356,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499048382,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499048409,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499048434,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499048459,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499048485,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499048509,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499048537,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,2 -1592499048562,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499048588,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499048612,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499048639,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499048666,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499048692,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499048717,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499048743,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499048768,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499048795,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499048820,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499048846,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499048871,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499048897,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499048922,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499048950,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499048975,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499049002,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499049027,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499049053,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499049080,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499049105,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499049131,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499049156,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499049181,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499049208,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499049234,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499049259,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499049284,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499049310,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499049335,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499049362,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499049386,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499049412,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499049437,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499049463,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499049490,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499049516,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499049541,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499049567,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499049592,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499049619,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499049644,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499049669,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499049695,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,0 -1592499049720,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,0 -1592499049745,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,1 -1592499049771,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499049795,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499049821,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499049846,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499049872,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499049899,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499049924,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499049950,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499049975,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499050001,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499050027,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499050052,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499050078,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499050103,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499050129,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499050155,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499050182,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499050207,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499050233,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499050258,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499050285,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499050311,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499050336,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499050361,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499050387,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499050411,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499050437,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499050462,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499050488,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499050513,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499050539,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499050565,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499050591,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499050616,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499050642,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499050668,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499050695,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499050720,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499050745,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499050770,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499050796,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499050822,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499050848,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499050873,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499050899,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499050924,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499050951,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499050976,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499051001,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499051027,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499051053,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499051078,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499051105,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499051130,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499051157,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499051182,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499051208,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499051234,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499051260,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499051286,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499051312,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499051337,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499051365,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499051390,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499051416,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499051440,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499051466,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499051492,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499051519,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499051544,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499051570,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499051595,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499051622,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499051648,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499051674,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,0 -1592499051700,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499051726,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499051752,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499051779,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499051805,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499051831,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499051857,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499051883,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499051909,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499051935,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499051960,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499051985,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499052010,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499052038,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499052063,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499052089,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499052114,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499052140,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499052165,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499052193,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499052218,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499052244,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499052268,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499052294,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499052320,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499052346,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499052371,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499052397,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499052422,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499052449,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499052474,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499052500,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499052525,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499052551,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499052576,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499052602,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499052626,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499052652,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499052678,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499052704,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499052730,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499052755,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499052780,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499052806,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499052831,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499052858,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499052883,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499052908,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499052934,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499052960,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499052986,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499053011,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499053037,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499053063,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499053088,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499053113,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499053140,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499053165,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499053190,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499053215,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499053241,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499053267,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499053292,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499053319,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499053344,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499053370,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499053396,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499053422,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499053447,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499053473,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499053498,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499053525,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499053551,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499053576,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499053602,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499053628,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499053654,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499053680,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,1 -1592499053705,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499053730,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499053755,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499053781,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499053807,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499053833,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499053858,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499053883,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499053909,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499053936,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499053961,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499053987,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499054012,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499054038,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499054064,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499054090,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499054115,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499054140,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499054165,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499054192,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499054217,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499054243,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499054267,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499054293,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499054318,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499054346,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499054371,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499054396,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499054422,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499054448,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499054474,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499054500,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499054525,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499054551,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499054576,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499054603,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499054628,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499054653,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499054679,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499054704,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499054730,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499054757,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499054782,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499054808,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499054833,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499054860,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499054886,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499054912,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499054937,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499054964,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499054989,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499055016,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499055041,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499055067,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499055092,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499055118,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499055145,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499055171,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499055196,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499055223,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499055248,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499055275,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499055300,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499055326,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,3,3,106,BRL06570.local,1 -1592499055352,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,2,2,105,BRL06570.local,0 -1592499055379,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,1,1,104,BRL06570.local,0 -1592499055483,8,Scale Down squeezenet_v1.1,200,OK,tearDown squeezenet_v1.1 1-1,true,323,1,1,8,BRL06570.local,1 diff --git a/tests/performance/tmp/run345/inference_server_monitoring/merged.json b/tests/performance/tmp/run345/inference_server_monitoring/merged.json deleted file mode 100644 index f4be3be46..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/merged.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "execution": [ - { - "concurrency": 4, - "ramp-up": "1s", - "hold-for": "20s", - "scenario": "Inference" - } - ], - "scenarios": { - "Inference": { - "script": "register_and_inference.jmx" - } - }, - "services": [ - { - "module": "monitoring", - "server-agent": [ - { - "address": "localhost:9009", - "label": "mms-inference-server", - "interval": "1s", - "logging": true, - "metrics": [ - "sum_all_cpu_percent", - "sum_workers_memory_percent", - "frontend_file_descriptors", - "total_workers" - ] - } - ] - } - ], - "modules": { - "jmeter": { - "properties": { - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - } - } - }, - "reporting": [ - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail" - }, - { - "module": "junit-xml", - "data-source": "sample-labels" - }, - { - "module": "final-stats", - "dump-csv": "${BASEDIR}/final_stats.csv" - } - ], - "settings": { - "env": { - "BASEDIR": "." - } - } -} diff --git a/tests/performance/tmp/run345/inference_server_monitoring/merged.yml b/tests/performance/tmp/run345/inference_server_monitoring/merged.yml deleted file mode 100644 index 52ac27cb4..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/merged.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- -execution: -- concurrency: 4 - hold-for: 20s - ramp-up: 1s - scenario: Inference -modules: - jmeter: - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - management_port: 8081 - port: 8080 - protocol: http -reporting: -- module: passfail -- data-source: pass-fail - module: junit-xml -- data-source: sample-labels - module: junit-xml -- dump-csv: ${BASEDIR}/final_stats.csv - module: final-stats -scenarios: - Inference: - script: register_and_inference.jmx -services: -- module: monitoring - server-agent: - - address: localhost:9009 - interval: 1s - label: mms-inference-server - logging: true - metrics: - - sum_all_cpu_percent - - sum_workers_memory_percent - - frontend_file_descriptors - - total_workers -settings: - env: - BASEDIR: . - diff --git a/tests/performance/tmp/run345/inference_server_monitoring/modified_register_and_inference.jmx b/tests/performance/tmp/run345/inference_server_monitoring/modified_register_and_inference.jmx deleted file mode 100644 index f7ad57369..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/modified_register_and_inference.jmx +++ /dev/null @@ -1,176 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - cnn_url - https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - = - The url from where to fetch noop model from - - - scale_up_workers - ${__P(min_workers,1)} - = - The workers to scale No op model to - - - scale_down_workers - 0 - Offload the No-Op Model - = - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_up_workers} - PUT - true - false - true - false - - - - - - - continue41020S - - - - - ${__P(input_filepath)} - data - image/jpeg - - - - - - - - - - - /predictions/${model} - POST - true - false - true - true - - - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_down_workers} - DELETE - true - false - true - false - - - - - - - /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue - - diff --git a/tests/performance/tmp/run345/inference_server_monitoring/xunit-1.xml b/tests/performance/tmp/run345/inference_server_monitoring/xunit-1.xml deleted file mode 100644 index 46d276b3a..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/xunit-1.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/tests/performance/tmp/run345/inference_server_monitoring/xunit.xml b/tests/performance/tmp/run345/inference_server_monitoring/xunit.xml deleted file mode 100644 index 8bdd58dbc..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring/xunit.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/SAlogs_localhost_9009.csv b/tests/performance/tmp/run345/inference_server_monitoring_criteria/SAlogs_localhost_9009.csv deleted file mode 100644 index e1084b60b..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/SAlogs_localhost_9009.csv +++ /dev/null @@ -1,141 +0,0 @@ -ts,sum_workers_cpu_percent,sum_workers_file_descriptors,sum_workers_memory_percent,total_workers -1592499378,0,0,0, -1592499379,0,0,0, -1592499380,0,0,0, -1592499381,0,0,0, -1592499383,0,0,0, -1592499384,0,0,0, -1592499385,0,0,0, -1592499386,0,0,0, -1592499387,0,0,0, -1592499388,0,0,0, -1592499389,0,0,0, -1592499390,0,0,0, -1592499391,0,0,0, -1592499392,0,0,0, -1592499393,0,0,0, -1592499394,0,0,0, -1592499395,0,0,0, -1592499396,0,0,0, -1592499397,0,0,0, -1592499398,0,0,0, -1592499399,0,0,0, -1592499400,0,0,0, -1592499401,0,0,0, -1592499402,0,0,0, -1592499403,0,0,0, -1592499404,0,0,0, -1592499405,0,0,0, -1592499406,0,0,0, -1592499407,0,0,0, -1592499408,0,0,0, -1592499409,0,0,0, -1592499410,0,0,0, -1592499411,0,0,0, -1592499412,0,0,0, -1592499413,0,0,0, -1592499414,0,0,0, -1592499415,0,0,0, -1592499416,0,0,0, -1592499417,0,0,0, -1592499418,0,0,0, -1592499419,0,0,0, -1592499420,0,0,0, -1592499421,0,0,0, -1592499422,0,0,0, -1592499423,0,0,0, -1592499424,0,0,0, -1592499425,0,0,0, -1592499426,0,0,0, -1592499427,0,0,0, -1592499428,0,0,0, -1592499429,0,0,0, -1592499430,0,0,0, -1592499431,0,0,0, -1592499432,0,0,0, -1592499433,0,0,0, -1592499434,0,0,0, -1592499435,0,0,0, -1592499436,0,0,0, -1592499437,0,0,0, -1592499438,0,0,0, -1592499439,0,0,0, -1592499440,0,0,0, -1592499441,0,0,0, -1592499442,0,0,0, -1592499443,0,0,0, -1592499444,0,0,0, -1592499445,0,0,0, -1592499446,0,0,0, -1592499447,0,0,0, -1592499448,0,0,0, -1592499449,0,0,0, -1592499450,0,0,0, -1592499451,0,0,0, -1592499452,0,0,0, -1592499453,0,0,0, -1592499454,0,0,0, -1592499455,0,0,0, -1592499456,0,0,0, -1592499457,0,0,0, -1592499458,0,0,0, -1592499459,0,0,0, -1592499460,0,0,0, -1592499461,0,0,0, -1592499462,0,0,0, -1592499463,0,0,0, -1592499464,0,0,0, -1592499465,0,0,0, -1592499466,0,0,0, -1592499467,0,0,0, -1592499468,0,0,0, -1592499469,0,0,0, -1592499470,0,0,0, -1592499471,0,0,0, -1592499472,0,0,0, -1592499473,0,0,0, -1592499474,0,0,0, -1592499475,0,0,0, -1592499476,0,0,0, -1592499477,0,0,0, -1592499478,0,0,0, -1592499479,0,0,0, -1592499480,0,0,0, -1592499481,0,0,0, -1592499482,0,0,0, -1592499483,0,0,0, -1592499484,0,0,0, -1592499485,0,0,0, -1592499486,0,0,0, -1592499487,0,0,0, -1592499488,0,0,0, -1592499489,0,0,0, -1592499491,0,0,0, -1592499492,0,0,0, -1592499493,0,0,0, -1592499494,0,0,0, -1592499495,0,0,0, -1592499496,0,0,0, -1592499497,0,0,0, -1592499498,0.0,0.06985664367675781,3, -1592499498,0.0,0.46858787536621094,11, -1592499500,0.0,0.803828239440918,11, -1592499501,0.0,0.7988929748535156,11, -1592499502,0.0,0.8048295974731445,11, -1592499503,0.0,0.8052825927734375,11, -1592499504,0.0,0.8053779602050781,11, -1592499505,0.0,0.8058547973632812,11, -1592499506,0.0,0.8049249649047852,11, -1592499507,0.0,0.797581672668457,11, -1592499508,0.0,0.8055448532104492,11, -1592499509,0.0,0.806117057800293,11, -1592499510,0.0,0.8051395416259766,11, -1592499511,0.0,0.7939577102661133,11, -1592499512,0.0,0.8060693740844727,11, -1592499513,0.0,0.8057832717895508,11, -1592499514,0.0,0.8066415786743164,11, -1592499515,0.0,0.8064031600952148,11, -1592499516,0.0,0.7895708084106445,11, -1592499517,0.0,0.8056402206420898,11, -1592499518,0.0,0.794529914855957,11, -1592499519,0.0,0.8079290390014648,11, diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/effective.json b/tests/performance/tmp/run345/inference_server_monitoring_criteria/effective.json deleted file mode 100644 index aeeb61d3b..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/effective.json +++ /dev/null @@ -1,410 +0,0 @@ -{ - "modules": { - "ab": { - "class": "bzt.modules.ab.ApacheBenchmarkExecutor" - }, - "gatling": { - "class": "bzt.modules.gatling.GatlingExecutor", - "properties": { - "gatling.data.file.bufferSize": 256 - } - }, - "grinder": { - "class": "bzt.modules.grinder.GrinderExecutor", - "properties": { - "grinder.useConsole": false - } - }, - "jmeter": { - "class": "bzt.modules.jmeter.JMeterExecutor", - "protocol-handlers": { - "http": "bzt.jmx.http.HTTPProtocolHandler" - }, - "plugins": [ - "jpgc-casutg", - "jpgc-dummy", - "jpgc-ffw", - "jpgc-fifo", - "jpgc-functions", - "jpgc-json", - "jpgc-perfmon", - "jpgc-prmctl", - "jpgc-tst" - ], - "properties": { - "jmeter.save.saveservice.autoflush": "true", - "jmeter.save.saveservice.connect_time": "true", - "jmeterengine.force.system.exit": "true", - "summariser.name": "", - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - }, - "version": "5.2.1", - "path": "/Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh" - }, - "locust": { - "class": "bzt.modules.locustio.LocustIOExecutor" - }, - "molotov": { - "class": "bzt.modules.molotov.MolotovExecutor" - }, - "pbench": { - "class": "bzt.modules.pbench.PBenchExecutor" - }, - "siege": { - "class": "bzt.modules.siege.SiegeExecutor" - }, - "tsung": { - "class": "bzt.modules.tsung.TsungExecutor" - }, - "selenium": { - "class": "bzt.modules.selenium.SeleniumExecutor" - }, - "junit": { - "class": "bzt.modules.java.JUnitTester" - }, - "testng": { - "class": "bzt.modules.java.TestNGTester" - }, - "rspec": { - "class": "bzt.modules.ruby.RSpecTester" - }, - "mocha": { - "class": "bzt.modules.javascript.MochaTester" - }, - "nunit": { - "class": "bzt.modules.csharp.NUnitExecutor" - }, - "pytest": { - "class": "bzt.modules.pytest.PyTestExecutor" - }, - "wdio": { - "class": "bzt.modules.javascript.WebdriverIOExecutor" - }, - "robot": { - "class": "bzt.modules.robot.RobotExecutor" - }, - "newman": { - "class": "bzt.modules.javascript.NewmanExecutor" - }, - "apiritif": { - "class": "bzt.modules.apiritif.ApiritifNoseExecutor" - }, - "local": { - "class": "bzt.modules.provisioning.Local" - }, - "monitoring": { - "class": "bzt.modules.monitoring.Monitoring" - }, - "passfail": { - "class": "bzt.modules.passfail.PassFailStatus" - }, - "shellexec": { - "class": "bzt.modules.shellexec.ShellExecutor" - }, - "junit-xml": { - "class": "bzt.modules.reporting.JUnitXMLReporter" - }, - "install-checker": { - "class": "bzt.modules.services.InstallChecker" - }, - "console": { - "class": "bzt.modules.console.ConsoleStatusReporter", - "disable": true - }, - "consolidator": { - "class": "bzt.modules.aggregator.ConsolidatingAggregator", - "percentiles": [ - 0.0, - 50.0, - 90.0, - 95.0, - 99.0, - 99.9, - 100.0 - ] - }, - "final-stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "functional-consolidator": { - "class": "bzt.modules.functional.FunctionalAggregator" - }, - "android-emulator": { - "class": "bzt.modules.services.AndroidEmulatorLoader" - }, - "appium": { - "class": "bzt.modules.services.AppiumLoader" - }, - "virtual-display": { - "class": "bzt.modules.services.VirtualDisplay" - }, - "external-results-loader": { - "class": "bzt.modules.external.ExternalResultsLoader" - }, - "blazemeter": { - "class": "bzt.modules.blazemeter.BlazeMeterUploader" - }, - "cloud": { - "class": "bzt.modules.blazemeter.CloudProvisioning", - "default-location": "us-central1-a" - }, - "proxy2jmx": { - "class": "bzt.modules.proxy2jmx.Proxy2JMX" - }, - "unpacker": { - "class": "bzt.modules.services.Unpacker" - }, - "screenshoter": { - "class": "bzt.modules.blazemeter.ServiceStubScreenshoter" - }, - "capturehar": { - "class": "bzt.modules.blazemeter.ServiceStubCaptureHAR" - }, - "final_stats": { - "class": "bzt.modules.reporting.FinalStatus" - } - }, - "cli-aliases": { - "cloud": { - "provisioning": "cloud" - }, - "detach": { - "modules": { - "cloud": { - "detach": true - } - } - }, - "func": { - "modules": { - "console": { - "disable": true - } - }, - "settings": { - "aggregator": "functional-consolidator" - } - }, - "gui": { - "modules": { - "jmeter": { - "gui": true - } - } - }, - "install-tools": { - "services": [ - "install-checker" - ] - }, - "local": { - "provisioning": "local" - }, - "locations": { - "modules": { - "cloud": { - "dump-locations": true - } - }, - "provisioning": "cloud" - }, - "report": { - "reporting": [ - "blazemeter" - ] - }, - "public": { - "modules": { - "blazemeter": { - "public-report": true - } - } - }, - "sequential": { - "modules": { - "local": { - "sequential": true - } - } - }, - "lint": { - "cli": { - "linter": { - "lint-and-exit": true - } - } - }, - "nolint": { - "cli": { - "linter": { - "disable": true - } - } - } - }, - "cli": { - "linter": { - "disable": false, - "warn-on-unfamiliar-fields": false, - "ignored-warnings": [], - "lint-and-exit": false - } - }, - "settings": { - "aggregator": "consolidator", - "default-executor": "jmeter", - "env": { - "OBJC_DISABLE_INITIALIZE_FORK_SAFETY": "YES", - "BASEDIR": "tmp/run345/inference_server_monitoring_criteria", - "TAURUS_ARTIFACTS_DIR": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring_criteria" - }, - "artifacts-dir": "tmp/run345/inference_server_monitoring_criteria" - }, - "provisioning": "local", - "reporting": [ - { - "module": "final-stats" - }, - { - "module": "console" - }, - { - "module": "passfail", - "criteria": [ - { - "subject": "fail", - "condition": ">", - "threshold": "50%", - "logic": "for", - "timeframe": 0, - "label": "s2_fail ", - "stop": true, - "fail": true, - "message": null - }, - { - "subject": "p90", - "condition": ">", - "threshold": "250ms", - "logic": "for", - "timeframe": 0, - "label": "s2_p90 ", - "stop": true, - "fail": true, - "message": null - }, - { - "subject": "avg-rt", - "condition": ">", - "threshold": "1s", - "logic": "for", - "timeframe": 0, - "label": "s2_avg_rt ", - "stop": true, - "fail": true, - "message": null - }, - { - "class": "bzt.modules.monitoring.MonitoringCriteria", - "subject": "mms-inference-server/sum_workers_file_descriptors", - "condition": ">", - "threshold": 80, - "timeframe": "1s", - "fail": true, - "stop": true - } - ] - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit.xml" - }, - { - "module": "junit-xml", - "data-source": "sample-labels", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit-1.xml" - }, - { - "module": "final-stats", - "dump-csv": "tmp/run345/inference_server_monitoring_criteria/final_stats.csv" - } - ], - "services": [ - { - "module": "monitoring", - "local": [ - { - "metrics": [ - "cpu", - "mem", - "bytes-sent", - "bytes-recv", - "disk-read", - "disk-write", - "disk-space", - "engine-loop", - "conn-all" - ] - } - ], - "server-agent": [ - { - "address": "localhost:9009", - "label": "mms-inference-server", - "interval": "1s", - "logging": true, - "metrics": [ - "sum_workers_cpu_percent", - "sum_workers_memory_percent", - "sum_workers_file_descriptors", - "total_workers" - ] - } - ] - }, - { - "module": "monitoring", - "server-agent": [ - { - "address": "localhost:9009", - "label": "mms-inference-server", - "interval": "1s", - "logging": true, - "metrics": [ - "sum_workers_cpu_percent", - "sum_workers_memory_percent", - "sum_workers_file_descriptors", - "total_workers" - ] - } - ] - } - ], - "execution": [ - { - "concurrency": { - "local": 4 - }, - "ramp-up": "1s", - "hold-for": "20s", - "scenario": "Inference", - "executor": "jmeter", - "throughput": {} - } - ], - "scenarios": { - "Inference": { - "script": "/Users/mahesh/git/multi-model-server/tests/performance/tests/register_and_inference.jmx" - } - }, - "included-configs": [], - "version": "1.14.2" -} diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/effective.yml b/tests/performance/tmp/run345/inference_server_monitoring_criteria/effective.yml deleted file mode 100644 index 4f30c3a11..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/effective.yml +++ /dev/null @@ -1,272 +0,0 @@ ---- -cli: - linter: - disable: false - ignored-warnings: [] - lint-and-exit: false - warn-on-unfamiliar-fields: false -cli-aliases: - cloud: - provisioning: cloud - detach: - modules: - cloud: - detach: true - func: - modules: - console: - disable: true - settings: - aggregator: functional-consolidator - gui: - modules: - jmeter: - gui: true - install-tools: - services: - - install-checker - lint: - cli: - linter: - lint-and-exit: true - local: - provisioning: local - locations: - modules: - cloud: - dump-locations: true - provisioning: cloud - nolint: - cli: - linter: - disable: true - public: - modules: - blazemeter: - public-report: true - report: - reporting: - - blazemeter - sequential: - modules: - local: - sequential: true -execution: -- concurrency: - local: 4 - executor: jmeter - hold-for: 20s - ramp-up: 1s - scenario: Inference - throughput: {} -included-configs: [] -modules: - ab: - class: bzt.modules.ab.ApacheBenchmarkExecutor - android-emulator: - class: bzt.modules.services.AndroidEmulatorLoader - apiritif: - class: bzt.modules.apiritif.ApiritifNoseExecutor - appium: - class: bzt.modules.services.AppiumLoader - blazemeter: - class: bzt.modules.blazemeter.BlazeMeterUploader - capturehar: - class: bzt.modules.blazemeter.ServiceStubCaptureHAR - cloud: - class: bzt.modules.blazemeter.CloudProvisioning - default-location: us-central1-a - console: - class: bzt.modules.console.ConsoleStatusReporter - disable: true - consolidator: - class: bzt.modules.aggregator.ConsolidatingAggregator - percentiles: - - 0.0 - - 50.0 - - 90.0 - - 95.0 - - 99.0 - - 99.9 - - 100.0 - external-results-loader: - class: bzt.modules.external.ExternalResultsLoader - final-stats: - class: bzt.modules.reporting.FinalStatus - final_stats: - class: bzt.modules.reporting.FinalStatus - functional-consolidator: - class: bzt.modules.functional.FunctionalAggregator - gatling: - class: bzt.modules.gatling.GatlingExecutor - properties: - gatling.data.file.bufferSize: 256 - grinder: - class: bzt.modules.grinder.GrinderExecutor - properties: - grinder.useConsole: false - install-checker: - class: bzt.modules.services.InstallChecker - jmeter: - class: bzt.modules.jmeter.JMeterExecutor - path: /Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh - plugins: - - jpgc-casutg - - jpgc-dummy - - jpgc-ffw - - jpgc-fifo - - jpgc-functions - - jpgc-json - - jpgc-perfmon - - jpgc-prmctl - - jpgc-tst - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - jmeter.save.saveservice.autoflush: 'true' - jmeter.save.saveservice.connect_time: 'true' - jmeterengine.force.system.exit: 'true' - management_port: 8081 - port: 8080 - protocol: http - summariser.name: '' - protocol-handlers: - http: bzt.jmx.http.HTTPProtocolHandler - version: 5.2.1 - junit: - class: bzt.modules.java.JUnitTester - junit-xml: - class: bzt.modules.reporting.JUnitXMLReporter - local: - class: bzt.modules.provisioning.Local - locust: - class: bzt.modules.locustio.LocustIOExecutor - mocha: - class: bzt.modules.javascript.MochaTester - molotov: - class: bzt.modules.molotov.MolotovExecutor - monitoring: - class: bzt.modules.monitoring.Monitoring - newman: - class: bzt.modules.javascript.NewmanExecutor - nunit: - class: bzt.modules.csharp.NUnitExecutor - passfail: - class: bzt.modules.passfail.PassFailStatus - pbench: - class: bzt.modules.pbench.PBenchExecutor - proxy2jmx: - class: bzt.modules.proxy2jmx.Proxy2JMX - pytest: - class: bzt.modules.pytest.PyTestExecutor - robot: - class: bzt.modules.robot.RobotExecutor - rspec: - class: bzt.modules.ruby.RSpecTester - screenshoter: - class: bzt.modules.blazemeter.ServiceStubScreenshoter - selenium: - class: bzt.modules.selenium.SeleniumExecutor - shellexec: - class: bzt.modules.shellexec.ShellExecutor - siege: - class: bzt.modules.siege.SiegeExecutor - testng: - class: bzt.modules.java.TestNGTester - tsung: - class: bzt.modules.tsung.TsungExecutor - unpacker: - class: bzt.modules.services.Unpacker - virtual-display: - class: bzt.modules.services.VirtualDisplay - wdio: - class: bzt.modules.javascript.WebdriverIOExecutor -provisioning: local -reporting: -- module: final-stats -- module: console -- criteria: - - condition: '>' - fail: true - label: 's2_fail ' - logic: for - message: null - stop: true - subject: fail - threshold: 50% - timeframe: 0 - - condition: '>' - fail: true - label: 's2_p90 ' - logic: for - message: null - stop: true - subject: p90 - threshold: 250ms - timeframe: 0 - - condition: '>' - fail: true - label: 's2_avg_rt ' - logic: for - message: null - stop: true - subject: avg-rt - threshold: 1s - timeframe: 0 - - class: bzt.modules.monitoring.MonitoringCriteria - condition: '>' - fail: true - stop: true - subject: mms-inference-server/sum_workers_file_descriptors - threshold: 80 - timeframe: 1s - module: passfail -- module: passfail -- data-source: pass-fail - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit.xml - module: junit-xml -- data-source: sample-labels - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit-1.xml - module: junit-xml -- dump-csv: tmp/run345/inference_server_monitoring_criteria/final_stats.csv - module: final-stats -scenarios: - Inference: - script: /Users/mahesh/git/multi-model-server/tests/performance/tests/register_and_inference.jmx -services: -- local: - - metrics: - - cpu - - mem - - bytes-sent - - bytes-recv - - disk-read - - disk-write - - disk-space - - engine-loop - - conn-all - module: monitoring - server-agent: - - &id001 - address: localhost:9009 - interval: 1s - label: mms-inference-server - logging: true - metrics: - - sum_workers_cpu_percent - - sum_workers_memory_percent - - sum_workers_file_descriptors - - total_workers -- module: monitoring - server-agent: - - *id001 -settings: - aggregator: consolidator - artifacts-dir: tmp/run345/inference_server_monitoring_criteria - default-executor: jmeter - env: - BASEDIR: tmp/run345/inference_server_monitoring_criteria - OBJC_DISABLE_INITIALIZE_FORK_SAFETY: 'YES' - TAURUS_ARTIFACTS_DIR: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring_criteria -version: 1.14.2 - diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/error.jtl b/tests/performance/tmp/run345/inference_server_monitoring_criteria/error.jtl deleted file mode 100644 index 32bb536a8..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/error.jtl +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/final_stats.csv b/tests/performance/tmp/run345/inference_server_monitoring_criteria/final_stats.csv deleted file mode 100644 index 58148ab25..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/final_stats.csv +++ /dev/null @@ -1,6 +0,0 @@ -label,avg_ct,avg_lt,avg_rt,bytes,concurrency,fail,stdev_rt,succ,throughput,perc_0.0,perc_50.0,perc_90.0,perc_95.0,perc_99.0,perc_99.9,perc_100.0,rc_200,rc_202 -Setup squeezenet_v1.1 Model,0.02900,117.84600,117.84800,321,1,0,0.00800,1,1,117.82400,117.82400,117.82400,117.82400,117.82400,117.82400,117.82400,1, -,0.00059,0.26053,0.26056,523770,1,0,4.25743,764,764,0.00300,0.10300,0.11100,0.11400,0.13200,117.82400,117.82400,763,1 -Scale Up squeezenet_v1.1 Model,0.00200,0.00300,0.00300,319,1,0,0.00000,1,1,0.00300,0.00300,0.00300,0.00300,0.00300,0.00300,0.00300,,1 -Inference Request,0.00055,0.10669,0.10671,522807,1,0,0.04000,761,761,0.09800,0.10300,0.11100,0.11400,0.13000,1.18100,1.18100,761, -Scale Down squeezenet_v1.1,0.00000,0.00800,0.00800,323,1,0,0.00000,1,1,0.00800,0.00800,0.00800,0.00800,0.00800,0.00800,0.00800,1, diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/global_config.yaml b/tests/performance/tmp/run345/inference_server_monitoring_criteria/global_config.yaml deleted file mode 100644 index 94731f4a3..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/global_config.yaml +++ /dev/null @@ -1,26 +0,0 @@ -modules: - jmeter: - # These are JMeter test case properties. These variables are used in jmx files. - # Change the vaues as per your setup - properties: - hostname : 127.0.0.1 # MMS properties - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg # make sure jpg is available at this path - # if relative path is provided this will be relative to current working directory - -# DO-NOT change properties below unless you know what you are doing. -# They are needed for performance test suite runner script. -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -settings: - env: - BASEDIR : '.' diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml b/tests/performance/tmp/run345/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml deleted file mode 100644 index 61730a84d..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml +++ /dev/null @@ -1,40 +0,0 @@ - -execution: -- concurrency: 4 - ramp-up: 1s - hold-for: 20s - scenario: Inference - -scenarios: - Inference: - script: register_and_inference.jmx - - - -services: - - module: monitoring - server-agent: - - address: localhost:9009 # metric monitoring service address - label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port - interval: 1s # polling interval - logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir - metrics: # metrics should be supported by monitoring service - - sum_workers_cpu_percent # cpu percent used by all the mms server processes and workers - - sum_workers_memory_percent - - sum_workers_file_descriptors - - total_workers # no of mms workers - - -reporting: -- module: passfail - criteria: - - fail of s2_fail >50%, stop as failed - - p90 of s2_p90 >250ms , stop as failed - - avg-rt of s2_avg_rt >1s , stop as failed - - class: bzt.modules.monitoring.MonitoringCriteria - subject: mms-inference-server/sum_workers_file_descriptors - condition: '>' - threshold: 80 - timeframe: 1s - fail: true - stop: true diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/jmeter-bzt.properties b/tests/performance/tmp/run345/inference_server_monitoring_criteria/jmeter-bzt.properties deleted file mode 100644 index fde38ee18..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/jmeter-bzt.properties +++ /dev/null @@ -1,14 +0,0 @@ -jmeter.save.saveservice.autoflush=true -jmeter.save.saveservice.connect_time=true -jmeterengine.force.system.exit=true -summariser.name= -hostname=127.0.0.1 -port=8080 -management_port=8081 -protocol=http -input_filepath=kitten.jpg -jmeterengine.nongui.port=4445 -jmeterengine.nongui.maxport=4445 -jmeter.save.saveservice.timestamp_format=ms -sampleresult.default.encoding=UTF-8 -user.classpath=/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring_criteria:/Users/mahesh/git/multi-model-server/tests/performance/tests diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/jmeter.err b/tests/performance/tmp/run345/inference_server_monitoring_criteria/jmeter.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/jmeter.out b/tests/performance/tmp/run345/inference_server_monitoring_criteria/jmeter.out deleted file mode 100644 index 859e452e0..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/jmeter.out +++ /dev/null @@ -1,5 +0,0 @@ -Created the tree successfully using /Users/mahesh/git/multi-model-server/tests/performance/tests/modified_register_and_inference.jmx -Starting standalone test @ Thu Jun 18 22:26:19 IST 2020 (1592499379022) -Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445 -Tidying up ... @ Thu Jun 18 22:28:38 IST 2020 (1592499518693) -... end of run diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/kpi.jtl b/tests/performance/tmp/run345/inference_server_monitoring_criteria/kpi.jtl deleted file mode 100644 index c7e024088..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/kpi.jtl +++ /dev/null @@ -1,765 +0,0 @@ -timeStamp,elapsed,label,responseCode,responseMessage,threadName,success,bytes,grpThreads,allThreads,Latency,Hostname,Connect -1592499379454,117848,Setup squeezenet_v1.1 Model,200,OK,setup squeezenet_v1.1 1-1,true,321,1,1,117846,BRL06570.local,29 -1592499497302,3,Scale Up squeezenet_v1.1 Model,202,Accepted,setup squeezenet_v1.1 1-1,true,319,1,1,3,BRL06570.local,2 -1592499497517,1181,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,1181,BRL06570.local,8 -1592499498525,200,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,200,BRL06570.local,1 -1592499498526,225,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,225,BRL06570.local,1 -1592499498525,252,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,252,BRL06570.local,2 -1592499498704,98,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,98,BRL06570.local,1 -1592499498726,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499498752,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499498778,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,106,BRL06570.local,0 -1592499498803,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,107,BRL06570.local,1 -1592499498830,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,0 -1592499498858,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499498885,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499498910,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499498935,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499498961,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499498987,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,1 -1592499499013,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,1 -1592499499038,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499499063,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499499088,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499499113,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499499141,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,99,BRL06570.local,1 -1592499499166,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499499191,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499499216,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499499241,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499499267,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499499293,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499499318,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499499343,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499499369,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499499395,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,1 -1592499499421,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499499445,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499499470,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499499495,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499499521,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,1 -1592499499547,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,0 -1592499499571,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499499596,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499499622,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499499647,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499499674,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,1 -1592499499700,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499499725,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499499750,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499499775,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499499801,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499499827,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499499853,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499499879,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499499905,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499499932,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499499958,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499499982,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499500007,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499500033,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499500059,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499500086,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499500111,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499500137,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499500162,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499500187,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499500213,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499500240,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499500265,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499500290,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499500315,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499500341,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499500367,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499500392,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499500417,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499500442,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499500469,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,0 -1592499500494,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499500520,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499500545,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499500570,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499500596,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,0 -1592499500622,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,99,BRL06570.local,0 -1592499500647,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499500671,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499500697,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499500722,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499500748,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499500774,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499500799,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,109,BRL06570.local,1 -1592499500824,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,110,BRL06570.local,0 -1592499500851,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,110,BRL06570.local,0 -1592499500880,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,107,BRL06570.local,0 -1592499500908,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,1 -1592499500934,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,109,BRL06570.local,1 -1592499500961,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,1 -1592499500988,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,1 -1592499501016,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,0 -1592499501043,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,1 -1592499501070,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,109,BRL06570.local,0 -1592499501097,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,0 -1592499501124,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,108,BRL06570.local,0 -1592499501152,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,0 -1592499501179,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,107,BRL06570.local,1 -1592499501206,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,1 -1592499501233,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,108,BRL06570.local,0 -1592499501261,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499501287,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,1 -1592499501314,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499501341,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,1 -1592499501367,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,1 -1592499501393,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,1 -1592499501420,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,1 -1592499501448,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,1 -1592499501475,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,107,BRL06570.local,1 -1592499501502,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,1 -1592499501528,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,1 -1592499501556,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,0 -1592499501582,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,106,BRL06570.local,1 -1592499501610,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,1 -1592499501637,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,107,BRL06570.local,0 -1592499501663,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,0 -1592499501689,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,1 -1592499501717,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,107,BRL06570.local,1 -1592499501744,130,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,130,BRL06570.local,1 -1592499501771,132,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,132,BRL06570.local,1 -1592499501797,134,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,134,BRL06570.local,1 -1592499501824,135,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,135,BRL06570.local,1 -1592499501875,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,111,BRL06570.local,1 -1592499501904,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,109,BRL06570.local,1 -1592499501931,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,110,BRL06570.local,1 -1592499501959,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,1 -1592499501987,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,1 -1592499502013,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,112,BRL06570.local,1 -1592499502042,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,109,BRL06570.local,0 -1592499502068,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,110,BRL06570.local,1 -1592499502096,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,110,BRL06570.local,1 -1592499502126,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,0 -1592499502152,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,113,BRL06570.local,0 -1592499502179,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,113,BRL06570.local,0 -1592499502206,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,113,BRL06570.local,1 -1592499502234,120,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,120,BRL06570.local,1 -1592499502265,119,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,119,BRL06570.local,1 -1592499502292,123,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,123,BRL06570.local,1 -1592499502320,123,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,123,BRL06570.local,1 -1592499502355,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,115,BRL06570.local,1 -1592499502385,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,113,BRL06570.local,0 -1592499502415,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,109,BRL06570.local,1 -1592499502443,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,1 -1592499502470,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,109,BRL06570.local,2 -1592499502498,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,107,BRL06570.local,1 -1592499502524,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,1 -1592499502553,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,106,BRL06570.local,0 -1592499502579,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,1 -1592499502606,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,107,BRL06570.local,1 -1592499502633,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,107,BRL06570.local,0 -1592499502659,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,107,BRL06570.local,1 -1592499502686,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,1 -1592499502714,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,106,BRL06570.local,0 -1592499502741,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,0 -1592499502767,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,0 -1592499502794,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,108,BRL06570.local,0 -1592499502820,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,109,BRL06570.local,1 -1592499502847,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,110,BRL06570.local,1 -1592499502875,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,1 -1592499502902,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,108,BRL06570.local,1 -1592499502929,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,107,BRL06570.local,1 -1592499502958,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499502984,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,107,BRL06570.local,1 -1592499503011,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,0 -1592499503037,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,0 -1592499503064,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,0 -1592499503091,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,107,BRL06570.local,1 -1592499503117,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,108,BRL06570.local,1 -1592499503145,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,106,BRL06570.local,1 -1592499503173,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,1 -1592499503198,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,1 -1592499503226,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,0 -1592499503252,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,110,BRL06570.local,1 -1592499503279,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,109,BRL06570.local,1 -1592499503306,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,106,BRL06570.local,1 -1592499503333,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499503363,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,0 -1592499503388,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499503413,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499503438,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499503463,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499503489,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499503514,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499503540,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499503565,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499503591,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499503619,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499503646,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,99,BRL06570.local,0 -1592499503670,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499503695,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499503721,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,99,BRL06570.local,0 -1592499503746,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,99,BRL06570.local,0 -1592499503771,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,99,BRL06570.local,1 -1592499503796,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499503821,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499503846,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499503871,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499503897,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499503923,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499503948,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499503973,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499503998,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499504025,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499504052,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499504076,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499504101,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499504126,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499504153,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,1 -1592499504179,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499504203,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499504228,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499504254,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499504280,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499504308,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499504333,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499504358,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499504383,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499504408,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499504435,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,99,BRL06570.local,0 -1592499504459,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499504484,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499504509,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499504535,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499504562,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,99,BRL06570.local,0 -1592499504587,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499504612,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499504636,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499504661,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499504688,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,0 -1592499504713,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499504738,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499504763,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499504788,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499504813,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499504841,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,0 -1592499504865,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499504892,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499504917,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499504942,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499504968,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499504995,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,0 -1592499505020,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499505044,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499505069,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499505096,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,0 -1592499505122,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499505146,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499505171,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499505196,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499505223,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499505250,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,1 -1592499505275,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499505300,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499505324,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499505351,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499505377,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,1 -1592499505403,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499505428,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499505453,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499505478,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499505505,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,0 -1592499505529,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499505554,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499505579,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499505605,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499505631,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499505657,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499505682,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499505707,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499505732,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499505758,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499505784,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499505809,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499505834,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499505860,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499505886,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499505912,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499505937,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,107,BRL06570.local,0 -1592499505962,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,106,BRL06570.local,0 -1592499505987,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,1 -1592499506016,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499506044,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,99,BRL06570.local,0 -1592499506069,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,0 -1592499506094,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,0 -1592499506119,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,0 -1592499506144,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499506170,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,0 -1592499506195,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499506219,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499506245,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499506270,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499506296,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499506321,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499506346,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499506372,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499506399,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499506425,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,1 -1592499506451,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,1 -1592499506476,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499506501,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499506525,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499506551,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499506577,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499506605,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,0 -1592499506629,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499506654,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499506679,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499506706,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,99,BRL06570.local,0 -1592499506730,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499506755,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499506780,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499506805,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499506832,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499506859,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499506884,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499506909,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499506934,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499506961,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499506987,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499507013,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499507038,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499507063,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499507091,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499507117,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499507142,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499507168,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499507193,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499507219,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499507246,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499507272,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499507297,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499507322,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499507348,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499507375,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499507400,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499507425,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499507451,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499507476,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499507504,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499507529,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,1 -1592499507555,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499507580,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,1 -1592499507606,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,1 -1592499507635,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499507661,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499507686,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499507714,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499507739,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499507764,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499507790,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,1 -1592499507816,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499507841,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499507865,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499507890,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499507917,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,99,BRL06570.local,0 -1592499507942,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,1 -1592499507967,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499507992,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499508017,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499508043,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,0 -1592499508068,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499508094,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499508118,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499508143,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499508170,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499508198,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,99,BRL06570.local,0 -1592499508222,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499508247,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499508272,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,0 -1592499508297,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499508323,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,99,BRL06570.local,1 -1592499508348,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,0 -1592499508372,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499508397,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499508422,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499508448,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499508474,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499508499,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499508524,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499508549,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499508575,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499508601,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499508626,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499508651,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499508676,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499508703,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,0 -1592499508729,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499508754,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499508779,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499508804,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499508831,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499508857,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,1 -1592499508882,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499508907,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499508932,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499508957,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499508984,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,0 -1592499509009,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499509034,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499509059,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499509084,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499509111,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499509136,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499509161,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499509186,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499509211,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499509237,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499509264,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,0 -1592499509289,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499509313,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499509338,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499509364,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499509390,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499509415,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,0 -1592499509440,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499509465,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499509492,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499509520,99,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,99,BRL06570.local,1 -1592499509545,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,1 -1592499509570,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499509594,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499509619,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499509645,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,0 -1592499509671,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499509696,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499509721,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499509745,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499509772,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499509798,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499509824,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499509851,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499509875,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499509901,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499509927,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499509953,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499509979,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499510005,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499510030,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499510056,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499510082,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499510107,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499510132,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499510157,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499510184,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499510209,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,1 -1592499510234,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499510260,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499510285,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499510312,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499510338,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499510362,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,106,BRL06570.local,1 -1592499510388,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499510415,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,103,BRL06570.local,0 -1592499510441,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499510468,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499510493,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499510518,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499510544,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499510572,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,0 -1592499510598,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,1 -1592499510623,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499510648,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,101,BRL06570.local,1 -1592499510673,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499510698,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499510724,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499510749,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499510776,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499510801,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499510826,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,0 -1592499510852,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,112,BRL06570.local,1 -1592499510879,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,113,BRL06570.local,0 -1592499510905,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,115,BRL06570.local,1 -1592499510934,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,113,BRL06570.local,1 -1592499510964,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,110,BRL06570.local,1 -1592499510992,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,1 -1592499511021,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,0 -1592499511047,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,109,BRL06570.local,1 -1592499511075,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,110,BRL06570.local,0 -1592499511101,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,112,BRL06570.local,0 -1592499511129,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,111,BRL06570.local,0 -1592499511157,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,110,BRL06570.local,1 -1592499511185,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,112,BRL06570.local,1 -1592499511213,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,111,BRL06570.local,1 -1592499511240,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,110,BRL06570.local,1 -1592499511267,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,111,BRL06570.local,1 -1592499511298,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,0 -1592499511324,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,112,BRL06570.local,1 -1592499511350,114,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,114,BRL06570.local,1 -1592499511379,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,112,BRL06570.local,0 -1592499511407,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,110,BRL06570.local,1 -1592499511436,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,0 -1592499511465,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499511491,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,107,BRL06570.local,1 -1592499511518,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,0 -1592499511544,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,1 -1592499511570,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,109,BRL06570.local,1 -1592499511599,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,107,BRL06570.local,0 -1592499511626,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,1 -1592499511652,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,1 -1592499511679,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,108,BRL06570.local,1 -1592499511706,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,1 -1592499511735,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,107,BRL06570.local,0 -1592499511762,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,0 -1592499511788,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,112,BRL06570.local,0 -1592499511814,114,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,114,BRL06570.local,1 -1592499511842,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,112,BRL06570.local,1 -1592499511872,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,110,BRL06570.local,3 -1592499511901,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,110,BRL06570.local,0 -1592499511928,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,112,BRL06570.local,1 -1592499511954,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,113,BRL06570.local,1 -1592499511983,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,111,BRL06570.local,0 -1592499512011,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,110,BRL06570.local,1 -1592499512040,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,110,BRL06570.local,1 -1592499512067,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,110,BRL06570.local,0 -1592499512094,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,1 -1592499512121,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,110,BRL06570.local,1 -1592499512150,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,0 -1592499512178,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,0 -1592499512204,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,111,BRL06570.local,0 -1592499512231,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,110,BRL06570.local,1 -1592499512258,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,109,BRL06570.local,1 -1592499512286,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,109,BRL06570.local,1 -1592499512315,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,110,BRL06570.local,1 -1592499512341,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,115,BRL06570.local,1 -1592499512367,116,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,116,BRL06570.local,1 -1592499512396,114,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,114,BRL06570.local,1 -1592499512426,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,110,BRL06570.local,1 -1592499512457,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,0 -1592499512483,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,109,BRL06570.local,0 -1592499512510,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,110,BRL06570.local,1 -1592499512536,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,113,BRL06570.local,0 -1592499512563,114,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,114,BRL06570.local,0 -1592499512592,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,111,BRL06570.local,1 -1592499512621,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,111,BRL06570.local,0 -1592499512649,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,112,BRL06570.local,1 -1592499512677,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,110,BRL06570.local,1 -1592499512703,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,111,BRL06570.local,1 -1592499512732,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,110,BRL06570.local,1 -1592499512761,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,0 -1592499512787,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,112,BRL06570.local,1 -1592499512814,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,113,BRL06570.local,1 -1592499512842,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,111,BRL06570.local,1 -1592499512870,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,110,BRL06570.local,1 -1592499512900,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,0 -1592499512927,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,107,BRL06570.local,1 -1592499512953,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,109,BRL06570.local,1 -1592499512980,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,1 -1592499513008,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,108,BRL06570.local,0 -1592499513035,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,0 -1592499513063,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,0 -1592499513089,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,1 -1592499513116,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,108,BRL06570.local,1 -1592499513143,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,106,BRL06570.local,0 -1592499513170,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,0 -1592499513198,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499513224,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,0 -1592499513250,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,107,BRL06570.local,0 -1592499513276,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,107,BRL06570.local,1 -1592499513303,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,106,BRL06570.local,1 -1592499513331,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499513358,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499513383,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,107,BRL06570.local,1 -1592499513409,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,107,BRL06570.local,0 -1592499513435,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,1 -1592499513463,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,106,BRL06570.local,0 -1592499513491,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,0 -1592499513516,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,1 -1592499513543,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,108,BRL06570.local,0 -1592499513569,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,110,BRL06570.local,1 -1592499513597,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,108,BRL06570.local,1 -1592499513624,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,0 -1592499513651,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,110,BRL06570.local,1 -1592499513679,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,1 -1592499513706,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,110,BRL06570.local,0 -1592499513733,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,110,BRL06570.local,0 -1592499513761,114,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,114,BRL06570.local,1 -1592499513788,117,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,117,BRL06570.local,0 -1592499513816,116,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,116,BRL06570.local,1 -1592499513844,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,113,BRL06570.local,0 -1592499513875,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,107,BRL06570.local,1 -1592499513906,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499513932,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,1 -1592499513957,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,108,BRL06570.local,0 -1592499513982,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,109,BRL06570.local,1 -1592499514008,108,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,108,BRL06570.local,1 -1592499514038,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499514065,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499514091,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499514117,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499514143,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,1 -1592499514170,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499514197,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499514222,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499514247,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499514274,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499514301,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499514327,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499514352,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,0 -1592499514377,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,105,BRL06570.local,1 -1592499514403,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499514431,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499514457,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,1 -1592499514482,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499514507,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499514531,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499514557,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499514583,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499514608,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499514633,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499514659,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499514685,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499514711,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499514737,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499514763,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,1 -1592499514788,107,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,107,BRL06570.local,1 -1592499514814,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,0 -1592499514841,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499514869,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499514895,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499514920,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499514945,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499514971,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499514998,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499515024,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499515049,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499515076,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499515102,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499515128,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499515153,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,1 -1592499515178,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499515203,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499515229,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499515255,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499515282,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499515308,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499515334,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,0 -1592499515359,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499515385,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499515410,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499515435,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499515460,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,106,BRL06570.local,0 -1592499515485,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,106,BRL06570.local,0 -1592499515512,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,1 -1592499515539,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499515566,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499515591,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499515616,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499515642,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499515668,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499515694,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499515720,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499515745,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,0 -1592499515770,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499515797,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499515823,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499515850,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,105,BRL06570.local,1 -1592499515875,117,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,117,BRL06570.local,0 -1592499515900,119,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,119,BRL06570.local,0 -1592499515930,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,115,BRL06570.local,1 -1592499515955,116,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,116,BRL06570.local,0 -1592499515992,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499516020,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,1 -1592499516045,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499516071,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499516098,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499516122,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499516148,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499516173,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499516199,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499516225,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499516250,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499516275,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499516300,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499516325,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499516352,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,0 -1592499516379,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,0 -1592499516404,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499516429,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499516453,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,1 -1592499516479,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,1 -1592499516506,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,100,BRL06570.local,0 -1592499516532,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499516556,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,101,BRL06570.local,1 -1592499516581,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499516606,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,1 -1592499516633,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499516657,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499516682,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499516708,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499516733,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499516759,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499516785,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,0 -1592499516810,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,0 -1592499516836,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499516863,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499516889,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,101,BRL06570.local,1 -1592499516915,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499516940,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499516965,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,104,BRL06570.local,0 -1592499516990,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,106,BRL06570.local,0 -1592499517017,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,105,BRL06570.local,1 -1592499517044,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,104,BRL06570.local,0 -1592499517069,106,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,106,BRL06570.local,1 -1592499517096,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,104,BRL06570.local,1 -1592499517122,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,0 -1592499517149,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499517175,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,0 -1592499517201,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,0 -1592499517226,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499517251,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,1 -1592499517277,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,102,BRL06570.local,1 -1592499517304,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,100,BRL06570.local,0 -1592499517329,101,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,101,BRL06570.local,0 -1592499517354,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,103,BRL06570.local,0 -1592499517379,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499517405,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,102,BRL06570.local,0 -1592499517431,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499517457,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,102,BRL06570.local,0 -1592499517482,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,103,BRL06570.local,0 -1592499517507,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499517533,102,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,102,BRL06570.local,0 -1592499517560,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,100,BRL06570.local,0 -1592499517586,100,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,100,BRL06570.local,0 -1592499517610,103,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,103,BRL06570.local,1 -1592499517635,104,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,104,BRL06570.local,1 -1592499517660,105,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,105,BRL06570.local,1 -1592499517686,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,109,BRL06570.local,1 -1592499517713,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,115,BRL06570.local,1 -1592499517739,120,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,120,BRL06570.local,1 -1592499517765,122,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,122,BRL06570.local,1 -1592499517795,119,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,119,BRL06570.local,1 -1592499517829,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,112,BRL06570.local,1 -1592499517860,109,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,109,BRL06570.local,1 -1592499517887,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,111,BRL06570.local,1 -1592499517915,110,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,110,BRL06570.local,0 -1592499517942,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,111,BRL06570.local,1 -1592499517969,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,112,BRL06570.local,1 -1592499517998,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,112,BRL06570.local,1 -1592499518025,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,115,BRL06570.local,1 -1592499518054,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,115,BRL06570.local,0 -1592499518082,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,115,BRL06570.local,0 -1592499518110,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,115,BRL06570.local,1 -1592499518141,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,112,BRL06570.local,1 -1592499518169,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,112,BRL06570.local,0 -1592499518197,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,115,BRL06570.local,1 -1592499518226,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,115,BRL06570.local,1 -1592499518253,116,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,115,BRL06570.local,0 -1592499518281,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,115,BRL06570.local,0 -1592499518313,111,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,111,BRL06570.local,0 -1592499518342,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,112,BRL06570.local,1 -1592499518369,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,113,BRL06570.local,0 -1592499518396,115,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,4,4,115,BRL06570.local,1 -1592499518424,114,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,4,4,114,BRL06570.local,1 -1592499518454,112,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,4,4,112,BRL06570.local,1 -1592499518482,114,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-2,true,687,4,4,114,BRL06570.local,1 -1592499518511,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-4,true,687,3,3,113,BRL06570.local,0 -1592499518538,114,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-3,true,687,2,2,114,BRL06570.local,1 -1592499518567,113,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,113,BRL06570.local,0 -1592499518683,8,Scale Down squeezenet_v1.1,200,OK,tearDown squeezenet_v1.1 1-1,true,323,1,1,8,BRL06570.local,0 diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/merged.json b/tests/performance/tmp/run345/inference_server_monitoring_criteria/merged.json deleted file mode 100644 index db8c6b533..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/merged.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "execution": [ - { - "concurrency": 4, - "ramp-up": "1s", - "hold-for": "20s", - "scenario": "Inference" - } - ], - "scenarios": { - "Inference": { - "script": "register_and_inference.jmx" - } - }, - "services": [ - { - "module": "monitoring", - "server-agent": [ - { - "address": "localhost:9009", - "label": "mms-inference-server", - "interval": "1s", - "logging": true, - "metrics": [ - "sum_workers_cpu_percent", - "sum_workers_memory_percent", - "sum_workers_file_descriptors", - "total_workers" - ] - } - ] - } - ], - "reporting": [ - { - "module": "passfail", - "criteria": [ - "fail of s2_fail >50%, stop as failed", - "p90 of s2_p90 >250ms , stop as failed", - "avg-rt of s2_avg_rt >1s , stop as failed", - { - "class": "bzt.modules.monitoring.MonitoringCriteria", - "subject": "mms-inference-server/sum_workers_file_descriptors", - "condition": ">", - "threshold": 80, - "timeframe": "1s", - "fail": true, - "stop": true - } - ] - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail" - }, - { - "module": "junit-xml", - "data-source": "sample-labels" - }, - { - "module": "final-stats", - "dump-csv": "${BASEDIR}/final_stats.csv" - } - ], - "modules": { - "jmeter": { - "properties": { - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - } - } - }, - "settings": { - "env": { - "BASEDIR": "." - } - } -} diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/merged.yml b/tests/performance/tmp/run345/inference_server_monitoring_criteria/merged.yml deleted file mode 100644 index 1c286d1d6..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/merged.yml +++ /dev/null @@ -1,53 +0,0 @@ ---- -execution: -- concurrency: 4 - hold-for: 20s - ramp-up: 1s - scenario: Inference -modules: - jmeter: - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - management_port: 8081 - port: 8080 - protocol: http -reporting: -- criteria: - - fail of s2_fail >50%, stop as failed - - p90 of s2_p90 >250ms , stop as failed - - avg-rt of s2_avg_rt >1s , stop as failed - - class: bzt.modules.monitoring.MonitoringCriteria - condition: '>' - fail: true - stop: true - subject: mms-inference-server/sum_workers_file_descriptors - threshold: 80 - timeframe: 1s - module: passfail -- module: passfail -- data-source: pass-fail - module: junit-xml -- data-source: sample-labels - module: junit-xml -- dump-csv: ${BASEDIR}/final_stats.csv - module: final-stats -scenarios: - Inference: - script: register_and_inference.jmx -services: -- module: monitoring - server-agent: - - address: localhost:9009 - interval: 1s - label: mms-inference-server - logging: true - metrics: - - sum_workers_cpu_percent - - sum_workers_memory_percent - - sum_workers_file_descriptors - - total_workers -settings: - env: - BASEDIR: . - diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/modified_register_and_inference.jmx b/tests/performance/tmp/run345/inference_server_monitoring_criteria/modified_register_and_inference.jmx deleted file mode 100644 index fea9abbe4..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/modified_register_and_inference.jmx +++ /dev/null @@ -1,176 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - cnn_url - https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - = - The url from where to fetch noop model from - - - scale_up_workers - ${__P(min_workers,1)} - = - The workers to scale No op model to - - - scale_down_workers - 0 - Offload the No-Op Model - = - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_up_workers} - PUT - true - false - true - false - - - - - - - continue41020S - - - - - ${__P(input_filepath)} - data - image/jpeg - - - - - - - - - - - /predictions/${model} - POST - true - false - true - true - - - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_down_workers} - DELETE - true - false - true - false - - - - - - - /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring_criteria/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_server_monitoring_criteria/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue - - diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit-1.xml b/tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit-1.xml deleted file mode 100644 index 5c9039211..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit-1.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit.xml b/tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit.xml deleted file mode 100644 index b02dabc02..000000000 --- a/tests/performance/tmp/run345/inference_server_monitoring_criteria/xunit.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/effective.json b/tests/performance/tmp/run345/inference_taurus_local_monitoring/effective.json deleted file mode 100644 index fdb44332c..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/effective.json +++ /dev/null @@ -1,349 +0,0 @@ -{ - "modules": { - "ab": { - "class": "bzt.modules.ab.ApacheBenchmarkExecutor" - }, - "gatling": { - "class": "bzt.modules.gatling.GatlingExecutor", - "properties": { - "gatling.data.file.bufferSize": 256 - } - }, - "grinder": { - "class": "bzt.modules.grinder.GrinderExecutor", - "properties": { - "grinder.useConsole": false - } - }, - "jmeter": { - "class": "bzt.modules.jmeter.JMeterExecutor", - "protocol-handlers": { - "http": "bzt.jmx.http.HTTPProtocolHandler" - }, - "plugins": [ - "jpgc-casutg", - "jpgc-dummy", - "jpgc-ffw", - "jpgc-fifo", - "jpgc-functions", - "jpgc-json", - "jpgc-perfmon", - "jpgc-prmctl", - "jpgc-tst" - ], - "properties": { - "jmeter.save.saveservice.autoflush": "true", - "jmeter.save.saveservice.connect_time": "true", - "jmeterengine.force.system.exit": "true", - "summariser.name": "", - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - }, - "version": "5.2.1", - "path": "/Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh" - }, - "locust": { - "class": "bzt.modules.locustio.LocustIOExecutor" - }, - "molotov": { - "class": "bzt.modules.molotov.MolotovExecutor" - }, - "pbench": { - "class": "bzt.modules.pbench.PBenchExecutor" - }, - "siege": { - "class": "bzt.modules.siege.SiegeExecutor" - }, - "tsung": { - "class": "bzt.modules.tsung.TsungExecutor" - }, - "selenium": { - "class": "bzt.modules.selenium.SeleniumExecutor" - }, - "junit": { - "class": "bzt.modules.java.JUnitTester" - }, - "testng": { - "class": "bzt.modules.java.TestNGTester" - }, - "rspec": { - "class": "bzt.modules.ruby.RSpecTester" - }, - "mocha": { - "class": "bzt.modules.javascript.MochaTester" - }, - "nunit": { - "class": "bzt.modules.csharp.NUnitExecutor" - }, - "pytest": { - "class": "bzt.modules.pytest.PyTestExecutor" - }, - "wdio": { - "class": "bzt.modules.javascript.WebdriverIOExecutor" - }, - "robot": { - "class": "bzt.modules.robot.RobotExecutor" - }, - "newman": { - "class": "bzt.modules.javascript.NewmanExecutor" - }, - "apiritif": { - "class": "bzt.modules.apiritif.ApiritifNoseExecutor" - }, - "local": { - "class": "bzt.modules.provisioning.Local" - }, - "monitoring": { - "class": "bzt.modules.monitoring.Monitoring" - }, - "passfail": { - "class": "bzt.modules.passfail.PassFailStatus" - }, - "shellexec": { - "class": "bzt.modules.shellexec.ShellExecutor" - }, - "junit-xml": { - "class": "bzt.modules.reporting.JUnitXMLReporter" - }, - "install-checker": { - "class": "bzt.modules.services.InstallChecker" - }, - "console": { - "class": "bzt.modules.console.ConsoleStatusReporter", - "disable": true - }, - "consolidator": { - "class": "bzt.modules.aggregator.ConsolidatingAggregator", - "percentiles": [ - 0.0, - 50.0, - 90.0, - 95.0, - 99.0, - 99.9, - 100.0 - ] - }, - "final-stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "functional-consolidator": { - "class": "bzt.modules.functional.FunctionalAggregator" - }, - "android-emulator": { - "class": "bzt.modules.services.AndroidEmulatorLoader" - }, - "appium": { - "class": "bzt.modules.services.AppiumLoader" - }, - "virtual-display": { - "class": "bzt.modules.services.VirtualDisplay" - }, - "external-results-loader": { - "class": "bzt.modules.external.ExternalResultsLoader" - }, - "blazemeter": { - "class": "bzt.modules.blazemeter.BlazeMeterUploader" - }, - "cloud": { - "class": "bzt.modules.blazemeter.CloudProvisioning", - "default-location": "us-central1-a" - }, - "proxy2jmx": { - "class": "bzt.modules.proxy2jmx.Proxy2JMX" - }, - "unpacker": { - "class": "bzt.modules.services.Unpacker" - }, - "screenshoter": { - "class": "bzt.modules.blazemeter.ServiceStubScreenshoter" - }, - "capturehar": { - "class": "bzt.modules.blazemeter.ServiceStubCaptureHAR" - }, - "final_stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "server_local_monitoring": { - "class": "metrics_monitoring_taurus.Monitor" - } - }, - "cli-aliases": { - "cloud": { - "provisioning": "cloud" - }, - "detach": { - "modules": { - "cloud": { - "detach": true - } - } - }, - "func": { - "modules": { - "console": { - "disable": true - } - }, - "settings": { - "aggregator": "functional-consolidator" - } - }, - "gui": { - "modules": { - "jmeter": { - "gui": true - } - } - }, - "install-tools": { - "services": [ - "install-checker" - ] - }, - "local": { - "provisioning": "local" - }, - "locations": { - "modules": { - "cloud": { - "dump-locations": true - } - }, - "provisioning": "cloud" - }, - "report": { - "reporting": [ - "blazemeter" - ] - }, - "public": { - "modules": { - "blazemeter": { - "public-report": true - } - } - }, - "sequential": { - "modules": { - "local": { - "sequential": true - } - } - }, - "lint": { - "cli": { - "linter": { - "lint-and-exit": true - } - } - }, - "nolint": { - "cli": { - "linter": { - "disable": true - } - } - } - }, - "cli": { - "linter": { - "disable": false, - "warn-on-unfamiliar-fields": false, - "ignored-warnings": [], - "lint-and-exit": false - } - }, - "settings": { - "aggregator": "consolidator", - "default-executor": "jmeter", - "env": { - "OBJC_DISABLE_INITIALIZE_FORK_SAFETY": "YES", - "BASEDIR": "tmp/run345/inference_taurus_local_monitoring", - "TAURUS_ARTIFACTS_DIR": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring" - }, - "artifacts-dir": "tmp/run345/inference_taurus_local_monitoring" - }, - "provisioning": "local", - "reporting": [ - { - "module": "final-stats" - }, - { - "module": "console" - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit.xml" - }, - { - "module": "junit-xml", - "data-source": "sample-labels", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit-1.xml" - }, - { - "module": "final-stats", - "dump-csv": "tmp/run345/inference_taurus_local_monitoring/final_stats.csv" - } - ], - "services": [ - { - "module": "monitoring", - "local": [ - { - "metrics": [ - "cpu", - "mem", - "bytes-sent", - "bytes-recv", - "disk-read", - "disk-write", - "disk-space", - "engine-loop", - "conn-all" - ] - } - ] - }, - { - "module": "server_local_monitoring", - "ServerLocalClient": [ - { - "interval": "1s", - "metrics": [ - "cpu", - "disk-space", - "mem", - "sum_workers_memory_percent" - ] - } - ] - } - ], - "execution": [ - { - "concurrency": { - "local": 1 - }, - "ramp-up": "5s", - "hold-for": "20s", - "scenario": "Inference", - "executor": "jmeter", - "throughput": {} - } - ], - "scenarios": { - "Inference": { - "script": "/Users/mahesh/git/multi-model-server/tests/performance/tests/register_and_inference.jmx" - } - }, - "included-configs": [], - "version": "1.14.2" -} diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/effective.yml b/tests/performance/tmp/run345/inference_taurus_local_monitoring/effective.yml deleted file mode 100644 index 10b81e642..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/effective.yml +++ /dev/null @@ -1,232 +0,0 @@ ---- -cli: - linter: - disable: false - ignored-warnings: [] - lint-and-exit: false - warn-on-unfamiliar-fields: false -cli-aliases: - cloud: - provisioning: cloud - detach: - modules: - cloud: - detach: true - func: - modules: - console: - disable: true - settings: - aggregator: functional-consolidator - gui: - modules: - jmeter: - gui: true - install-tools: - services: - - install-checker - lint: - cli: - linter: - lint-and-exit: true - local: - provisioning: local - locations: - modules: - cloud: - dump-locations: true - provisioning: cloud - nolint: - cli: - linter: - disable: true - public: - modules: - blazemeter: - public-report: true - report: - reporting: - - blazemeter - sequential: - modules: - local: - sequential: true -execution: -- concurrency: - local: 1 - executor: jmeter - hold-for: 20s - ramp-up: 5s - scenario: Inference - throughput: {} -included-configs: [] -modules: - ab: - class: bzt.modules.ab.ApacheBenchmarkExecutor - android-emulator: - class: bzt.modules.services.AndroidEmulatorLoader - apiritif: - class: bzt.modules.apiritif.ApiritifNoseExecutor - appium: - class: bzt.modules.services.AppiumLoader - blazemeter: - class: bzt.modules.blazemeter.BlazeMeterUploader - capturehar: - class: bzt.modules.blazemeter.ServiceStubCaptureHAR - cloud: - class: bzt.modules.blazemeter.CloudProvisioning - default-location: us-central1-a - console: - class: bzt.modules.console.ConsoleStatusReporter - disable: true - consolidator: - class: bzt.modules.aggregator.ConsolidatingAggregator - percentiles: - - 0.0 - - 50.0 - - 90.0 - - 95.0 - - 99.0 - - 99.9 - - 100.0 - external-results-loader: - class: bzt.modules.external.ExternalResultsLoader - final-stats: - class: bzt.modules.reporting.FinalStatus - final_stats: - class: bzt.modules.reporting.FinalStatus - functional-consolidator: - class: bzt.modules.functional.FunctionalAggregator - gatling: - class: bzt.modules.gatling.GatlingExecutor - properties: - gatling.data.file.bufferSize: 256 - grinder: - class: bzt.modules.grinder.GrinderExecutor - properties: - grinder.useConsole: false - install-checker: - class: bzt.modules.services.InstallChecker - jmeter: - class: bzt.modules.jmeter.JMeterExecutor - path: /Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh - plugins: - - jpgc-casutg - - jpgc-dummy - - jpgc-ffw - - jpgc-fifo - - jpgc-functions - - jpgc-json - - jpgc-perfmon - - jpgc-prmctl - - jpgc-tst - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - jmeter.save.saveservice.autoflush: 'true' - jmeter.save.saveservice.connect_time: 'true' - jmeterengine.force.system.exit: 'true' - management_port: 8081 - port: 8080 - protocol: http - summariser.name: '' - protocol-handlers: - http: bzt.jmx.http.HTTPProtocolHandler - version: 5.2.1 - junit: - class: bzt.modules.java.JUnitTester - junit-xml: - class: bzt.modules.reporting.JUnitXMLReporter - local: - class: bzt.modules.provisioning.Local - locust: - class: bzt.modules.locustio.LocustIOExecutor - mocha: - class: bzt.modules.javascript.MochaTester - molotov: - class: bzt.modules.molotov.MolotovExecutor - monitoring: - class: bzt.modules.monitoring.Monitoring - newman: - class: bzt.modules.javascript.NewmanExecutor - nunit: - class: bzt.modules.csharp.NUnitExecutor - passfail: - class: bzt.modules.passfail.PassFailStatus - pbench: - class: bzt.modules.pbench.PBenchExecutor - proxy2jmx: - class: bzt.modules.proxy2jmx.Proxy2JMX - pytest: - class: bzt.modules.pytest.PyTestExecutor - robot: - class: bzt.modules.robot.RobotExecutor - rspec: - class: bzt.modules.ruby.RSpecTester - screenshoter: - class: bzt.modules.blazemeter.ServiceStubScreenshoter - selenium: - class: bzt.modules.selenium.SeleniumExecutor - server_local_monitoring: - class: metrics_monitoring_taurus.Monitor - shellexec: - class: bzt.modules.shellexec.ShellExecutor - siege: - class: bzt.modules.siege.SiegeExecutor - testng: - class: bzt.modules.java.TestNGTester - tsung: - class: bzt.modules.tsung.TsungExecutor - unpacker: - class: bzt.modules.services.Unpacker - virtual-display: - class: bzt.modules.services.VirtualDisplay - wdio: - class: bzt.modules.javascript.WebdriverIOExecutor -provisioning: local -reporting: -- module: final-stats -- module: console -- module: passfail -- data-source: pass-fail - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit.xml - module: junit-xml -- data-source: sample-labels - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit-1.xml - module: junit-xml -- dump-csv: tmp/run345/inference_taurus_local_monitoring/final_stats.csv - module: final-stats -scenarios: - Inference: - script: /Users/mahesh/git/multi-model-server/tests/performance/tests/register_and_inference.jmx -services: -- local: - - metrics: - - cpu - - mem - - bytes-sent - - bytes-recv - - disk-read - - disk-write - - disk-space - - engine-loop - - conn-all - module: monitoring -- ServerLocalClient: - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - sum_workers_memory_percent - module: server_local_monitoring -settings: - aggregator: consolidator - artifacts-dir: tmp/run345/inference_taurus_local_monitoring - default-executor: jmeter - env: - BASEDIR: tmp/run345/inference_taurus_local_monitoring - OBJC_DISABLE_INITIALIZE_FORK_SAFETY: 'YES' - TAURUS_ARTIFACTS_DIR: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring -version: 1.14.2 - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/error.jtl b/tests/performance/tmp/run345/inference_taurus_local_monitoring/error.jtl deleted file mode 100644 index 32bb536a8..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/error.jtl +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/final_stats.csv b/tests/performance/tmp/run345/inference_taurus_local_monitoring/final_stats.csv deleted file mode 100644 index 60c86d70d..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/final_stats.csv +++ /dev/null @@ -1,6 +0,0 @@ -label,avg_ct,avg_lt,avg_rt,bytes,concurrency,fail,stdev_rt,succ,throughput,perc_0.0,perc_50.0,perc_90.0,perc_95.0,perc_99.0,perc_99.9,perc_100.0,rc_200,rc_202 -Setup squeezenet_v1.1 Model,0.03800,38.61100,38.61300,321,1,0,0.00500,1,1,38.59200,38.59200,38.59200,38.59200,38.59200,38.59200,38.59200,1, -,0.00071,0.08070,0.08072,521709,1,0,1.39753,761,761,0.00300,0.03000,0.03300,0.03400,0.04000,38.59200,38.59200,760,1 -Scale Up squeezenet_v1.1 Model,0.00200,0.00300,0.00300,319,1,0,0.00000,1,1,0.00300,0.00300,0.00300,0.00300,0.00300,0.00300,0.00300,,1 -Inference Request,0.00066,0.03006,0.03009,520746,1,0,0.00270,758,758,0.02600,0.03000,0.03300,0.03400,0.04000,0.05600,0.05600,758, -Scale Down squeezenet_v1.1,0.00000,0.00700,0.00700,323,1,0,0.00000,1,1,0.00700,0.00700,0.00700,0.00700,0.00700,0.00700,0.00700,1, diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/global_config.yaml b/tests/performance/tmp/run345/inference_taurus_local_monitoring/global_config.yaml deleted file mode 100644 index 94731f4a3..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/global_config.yaml +++ /dev/null @@ -1,26 +0,0 @@ -modules: - jmeter: - # These are JMeter test case properties. These variables are used in jmx files. - # Change the vaues as per your setup - properties: - hostname : 127.0.0.1 # MMS properties - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg # make sure jpg is available at this path - # if relative path is provided this will be relative to current working directory - -# DO-NOT change properties below unless you know what you are doing. -# They are needed for performance test suite runner script. -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -settings: - env: - BASEDIR : '.' diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml b/tests/performance/tmp/run345/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml deleted file mode 100644 index 0369b101c..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml +++ /dev/null @@ -1,24 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 5s - hold-for: 20s - scenario: Inference -scenarios: - Inference: - script: register_and_inference.jmx - -modules: - server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. - -services: - - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - sum_workers_memory_percent \ No newline at end of file diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/jmeter-bzt.properties b/tests/performance/tmp/run345/inference_taurus_local_monitoring/jmeter-bzt.properties deleted file mode 100644 index 58cbd72b2..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/jmeter-bzt.properties +++ /dev/null @@ -1,14 +0,0 @@ -jmeter.save.saveservice.autoflush=true -jmeter.save.saveservice.connect_time=true -jmeterengine.force.system.exit=true -summariser.name= -hostname=127.0.0.1 -port=8080 -management_port=8081 -protocol=http -input_filepath=kitten.jpg -jmeterengine.nongui.port=4445 -jmeterengine.nongui.maxport=4445 -jmeter.save.saveservice.timestamp_format=ms -sampleresult.default.encoding=UTF-8 -user.classpath=/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring:/Users/mahesh/git/multi-model-server/tests/performance/tests diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/jmeter.err b/tests/performance/tmp/run345/inference_taurus_local_monitoring/jmeter.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/jmeter.out b/tests/performance/tmp/run345/inference_taurus_local_monitoring/jmeter.out deleted file mode 100644 index d61c4387b..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/jmeter.out +++ /dev/null @@ -1,5 +0,0 @@ -Created the tree successfully using /Users/mahesh/git/multi-model-server/tests/performance/tests/modified_register_and_inference.jmx -Starting standalone test @ Thu Jun 18 22:23:18 IST 2020 (1592499198830) -Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445 -Tidying up ... @ Thu Jun 18 22:24:23 IST 2020 (1592499263612) -... end of run diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/kpi.jtl b/tests/performance/tmp/run345/inference_taurus_local_monitoring/kpi.jtl deleted file mode 100644 index fb7e63080..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/kpi.jtl +++ /dev/null @@ -1,762 +0,0 @@ -timeStamp,elapsed,label,responseCode,responseMessage,threadName,success,bytes,grpThreads,allThreads,Latency,Hostname,Connect -1592499199294,38613,Setup squeezenet_v1.1 Model,200,OK,setup squeezenet_v1.1 1-1,true,321,1,1,38611,BRL06570.local,38 -1592499237908,3,Scale Up squeezenet_v1.1 Model,202,Accepted,setup squeezenet_v1.1 1-1,true,319,1,1,3,BRL06570.local,2 -1592499240502,55,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,55,BRL06570.local,8 -1592499240563,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499240597,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499240631,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499240665,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499240698,37,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,37,BRL06570.local,1 -1592499240736,41,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,41,BRL06570.local,1 -1592499240778,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499240814,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499240849,36,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,36,BRL06570.local,1 -1592499240887,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499240922,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499240956,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499240992,39,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,39,BRL06570.local,1 -1592499241031,41,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,41,BRL06570.local,2 -1592499241073,40,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,40,BRL06570.local,1 -1592499241114,40,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,40,BRL06570.local,2 -1592499241155,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499241189,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499241220,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499241252,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499241281,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499241312,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499241343,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499241374,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499241403,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499241431,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499241460,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499241490,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499241519,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499241549,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499241577,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499241606,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499241636,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499241665,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499241699,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499241729,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499241757,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499241785,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499241815,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499241845,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499241873,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499241900,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499241928,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499241958,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499241986,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499242014,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499242042,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499242070,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499242102,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499242130,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499242158,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499242187,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499242217,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499242249,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499242279,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499242309,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499242338,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499242368,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499242399,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499242429,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499242462,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499242491,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499242521,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499242552,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499242580,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499242609,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499242637,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499242665,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499242694,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499242725,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499242755,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499242784,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499242813,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499242841,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499242875,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499242908,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499242938,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499242966,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499242998,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499243028,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499243061,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499243092,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499243120,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499243150,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499243182,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499243213,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499243243,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499243272,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499243301,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499243332,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499243365,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499243399,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499243428,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499243457,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499243486,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499243516,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499243548,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499243579,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499243610,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499243642,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499243673,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499243701,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499243732,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499243762,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499243793,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499243825,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499243856,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499243885,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499243914,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499243944,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499243973,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499244004,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499244033,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499244063,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499244095,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499244127,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499244163,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499244194,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499244225,50,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,50,BRL06570.local,0 -1592499244275,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499244306,56,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,56,BRL06570.local,1 -1592499244363,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499244395,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499244427,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499244457,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,2 -1592499244489,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499244520,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499244550,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499244584,40,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,40,BRL06570.local,1 -1592499244625,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499244658,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499244689,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499244721,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499244756,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499244789,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499244821,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499244856,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499244888,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499244919,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499244950,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499244983,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499245016,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499245050,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499245081,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499245113,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499245146,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499245176,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499245206,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499245233,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499245261,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499245292,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499245320,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499245349,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499245378,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499245407,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499245437,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499245466,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499245499,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499245529,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499245559,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499245589,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499245617,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499245649,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499245678,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499245706,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499245738,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499245770,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499245800,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499245832,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499245865,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499245897,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499245928,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499245959,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499245988,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499246017,42,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,42,BRL06570.local,1 -1592499246064,36,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,36,BRL06570.local,0 -1592499246101,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,0 -1592499246135,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499246167,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499246199,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499246230,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499246260,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499246288,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499246316,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499246349,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499246379,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499246410,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499246439,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499246468,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499246499,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499246530,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499246560,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499246589,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499246617,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499246649,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499246679,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499246711,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499246742,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499246777,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499246806,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499246837,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499246872,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499246903,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499246933,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499246963,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499246994,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499247027,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499247058,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499247089,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499247120,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499247151,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499247183,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499247215,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499247247,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499247279,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499247312,38,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,38,BRL06570.local,1 -1592499247350,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499247385,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499247417,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499247449,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499247478,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499247507,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499247538,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499247566,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499247599,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499247631,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499247663,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499247695,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499247727,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499247760,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499247792,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499247822,36,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,36,BRL06570.local,1 -1592499247858,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499247891,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499247923,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499247954,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499247984,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499248015,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499248046,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499248076,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499248106,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499248135,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499248164,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499248195,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499248223,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499248251,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499248280,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499248311,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499248342,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499248370,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499248400,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499248428,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499248457,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499248487,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499248516,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499248543,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499248571,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499248599,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499248628,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499248657,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499248684,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499248713,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499248742,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499248776,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499248805,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499248833,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499248862,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499248889,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499248918,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499248949,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499248978,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499249006,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499249034,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499249063,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499249093,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499249122,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499249150,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499249179,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499249208,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499249240,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499249274,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499249304,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499249334,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499249363,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499249394,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499249425,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499249453,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499249481,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499249510,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499249541,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499249572,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499249601,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499249630,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499249658,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499249688,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499249718,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499249748,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499249779,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499249809,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499249839,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499249871,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499249898,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499249926,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499249955,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499249984,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499250013,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499250043,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499250072,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499250100,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499250128,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499250157,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499250186,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499250215,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499250248,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499250277,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499250304,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499250335,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499250364,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499250394,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499250421,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499250449,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499250481,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499250509,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499250535,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499250563,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499250592,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499250622,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499250650,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499250678,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499250706,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499250733,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499250761,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499250792,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499250820,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499250848,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499250879,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499250907,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499250935,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499250963,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499250992,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499251020,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499251048,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499251079,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499251106,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499251133,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499251162,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499251191,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499251220,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499251248,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499251276,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499251305,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499251332,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499251360,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499251391,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499251419,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499251448,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499251477,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499251506,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499251535,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499251564,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499251592,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499251621,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499251649,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499251679,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499251707,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499251736,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499251765,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499251798,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499251827,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499251859,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499251887,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499251914,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499251942,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499251971,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499252002,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499252031,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499252060,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499252088,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499252117,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499252149,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499252178,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499252207,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499252236,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499252265,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499252298,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499252327,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499252355,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499252384,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499252412,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499252441,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499252470,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499252498,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499252527,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499252556,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499252585,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499252614,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499252641,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499252669,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499252698,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499252728,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499252758,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499252787,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499252815,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499252848,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499252880,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499252909,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499252938,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499252966,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499252998,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499253027,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499253055,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499253085,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499253116,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499253148,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499253177,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499253205,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499253235,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499253264,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499253292,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499253321,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499253350,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499253380,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499253408,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499253437,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499253465,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499253498,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499253529,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499253558,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499253586,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499253615,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499253648,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499253677,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499253707,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499253735,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499253763,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499253793,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499253822,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499253854,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499253886,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499253917,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499253948,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499253979,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499254010,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499254040,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499254073,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499254102,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,2 -1592499254135,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499254166,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499254198,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499254229,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499254260,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499254290,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499254321,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499254350,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499254381,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499254411,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499254441,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499254471,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499254503,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499254533,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,0 -1592499254567,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499254600,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499254635,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499254668,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499254699,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499254729,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499254761,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499254795,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499254829,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499254861,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499254891,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499254921,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499254951,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499254983,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499255013,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499255045,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499255077,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499255108,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499255139,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499255171,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499255202,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499255233,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499255262,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499255292,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499255323,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499255354,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499255387,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499255417,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,0 -1592499255451,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499255484,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499255513,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499255543,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499255572,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499255603,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499255634,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499255664,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499255693,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499255722,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499255752,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499255784,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499255817,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499255848,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499255879,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499255908,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499255937,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499255968,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499255998,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499256028,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499256061,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,2 -1592499256095,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499256128,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499256160,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499256190,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499256221,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499256251,40,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,40,BRL06570.local,1 -1592499256291,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499256324,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499256355,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499256386,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499256416,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499256448,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499256478,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499256507,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499256536,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499256565,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499256598,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499256628,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499256656,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499256685,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499256713,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499256742,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499256772,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499256801,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499256830,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499256859,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499256888,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499256917,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499256948,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499256977,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499257007,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499257035,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499257063,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499257094,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499257122,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499257149,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499257177,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499257207,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499257240,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499257270,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499257298,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499257325,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499257353,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499257381,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499257410,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499257440,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499257467,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499257498,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499257526,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499257555,26,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,26,BRL06570.local,0 -1592499257581,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499257611,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499257639,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499257667,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499257698,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499257725,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499257755,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499257785,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499257815,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499257848,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499257878,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499257907,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499257935,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499257963,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499257993,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499258024,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499258053,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499258081,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499258109,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499258139,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499258167,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499258198,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499258227,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499258256,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499258286,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499258315,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499258348,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499258376,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499258404,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499258432,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499258462,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499258490,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499258518,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499258548,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499258578,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499258609,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499258638,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499258666,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499258698,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499258726,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499258754,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499258782,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499258812,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499258842,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499258875,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499258909,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499258940,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499258973,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499259004,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499259035,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499259067,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499259100,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499259130,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499259161,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499259191,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499259221,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499259254,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499259287,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499259317,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,0 -1592499259351,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499259384,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499259415,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499259448,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499259479,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499259510,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499259540,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499259570,40,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,40,BRL06570.local,0 -1592499259610,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499259642,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499259671,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499259699,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499259730,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499259762,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499259792,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499259824,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499259858,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499259892,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499259924,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499259955,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499259989,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499260020,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499260051,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499260084,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499260118,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499260150,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499260183,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,0 -1592499260217,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499260252,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499260284,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499260317,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499260350,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499260384,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499260415,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499260448,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499260478,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499260508,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499260538,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499260569,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,0 -1592499260603,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499260639,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499260669,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499260699,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499260731,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499260764,35,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,35,BRL06570.local,1 -1592499260800,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499260834,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499260866,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499260898,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499260927,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499260960,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499260992,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499261022,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499261052,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499261080,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499261111,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499261140,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499261170,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499261198,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499261227,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499261258,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499261286,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499261315,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,0 -1592499261348,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499261376,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499261403,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499261433,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499261462,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499261491,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499261519,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499261548,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499261578,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499261610,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499261641,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499261673,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499261703,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499261735,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499261764,34,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,34,BRL06570.local,1 -1592499261798,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499261832,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499261863,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499261892,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499261920,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499261949,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499261978,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499262009,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499262039,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499262071,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499262104,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499262133,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499262163,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499262193,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499262223,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499262251,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499262280,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499262312,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499262343,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499262373,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499262401,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499262429,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499262456,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499262488,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499262517,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499262548,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499262576,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499262604,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499262632,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499262660,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499262689,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499262717,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499262748,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499262776,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499262804,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499262835,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499262866,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,0 -1592499262898,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499262928,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499262959,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499262990,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499263019,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499263049,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499263078,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499263108,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499263138,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499263168,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499263198,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499263226,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499263253,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499263283,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499263312,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499263340,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499263367,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,1 -1592499263398,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499263427,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499263456,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499263485,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499263513,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499263541,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499263570,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499263604,7,Scale Down squeezenet_v1.1,200,OK,tearDown squeezenet_v1.1 1-1,true,323,1,1,7,BRL06570.local,0 diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/merged.json b/tests/performance/tmp/run345/inference_taurus_local_monitoring/merged.json deleted file mode 100644 index 64688733c..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/merged.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "execution": [ - { - "concurrency": 1, - "ramp-up": "5s", - "hold-for": "20s", - "scenario": "Inference" - } - ], - "scenarios": { - "Inference": { - "script": "register_and_inference.jmx" - } - }, - "modules": { - "server_local_monitoring": { - "class": "metrics_monitoring_taurus.Monitor" - }, - "jmeter": { - "properties": { - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - } - } - }, - "services": [ - { - "module": "server_local_monitoring", - "ServerLocalClient": [ - { - "interval": "1s", - "metrics": [ - "cpu", - "disk-space", - "mem", - "sum_workers_memory_percent" - ] - } - ] - } - ], - "reporting": [ - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail" - }, - { - "module": "junit-xml", - "data-source": "sample-labels" - }, - { - "module": "final-stats", - "dump-csv": "${BASEDIR}/final_stats.csv" - } - ], - "settings": { - "env": { - "BASEDIR": "." - } - } -} diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/merged.yml b/tests/performance/tmp/run345/inference_taurus_local_monitoring/merged.yml deleted file mode 100644 index e9bc9dded..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/merged.yml +++ /dev/null @@ -1,40 +0,0 @@ ---- -execution: -- concurrency: 1 - hold-for: 20s - ramp-up: 5s - scenario: Inference -modules: - jmeter: - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - management_port: 8081 - port: 8080 - protocol: http - server_local_monitoring: - class: metrics_monitoring_taurus.Monitor -reporting: -- module: passfail -- data-source: pass-fail - module: junit-xml -- data-source: sample-labels - module: junit-xml -- dump-csv: ${BASEDIR}/final_stats.csv - module: final-stats -scenarios: - Inference: - script: register_and_inference.jmx -services: -- ServerLocalClient: - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - sum_workers_memory_percent - module: server_local_monitoring -settings: - env: - BASEDIR: . - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/modified_register_and_inference.jmx b/tests/performance/tmp/run345/inference_taurus_local_monitoring/modified_register_and_inference.jmx deleted file mode 100644 index 39e99b653..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/modified_register_and_inference.jmx +++ /dev/null @@ -1,176 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - cnn_url - https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - = - The url from where to fetch noop model from - - - scale_up_workers - ${__P(min_workers,1)} - = - The workers to scale No op model to - - - scale_down_workers - 0 - Offload the No-Op Model - = - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_up_workers} - PUT - true - false - true - false - - - - - - - continue15020S - - - - - ${__P(input_filepath)} - data - image/jpeg - - - - - - - - - - - /predictions/${model} - POST - true - false - true - true - - - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_down_workers} - DELETE - true - false - true - false - - - - - - - /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue - - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit-1.xml b/tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit-1.xml deleted file mode 100644 index 47375bac7..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit-1.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit.xml b/tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit.xml deleted file mode 100644 index 8bdd58dbc..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring/xunit.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/effective.json b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/effective.json deleted file mode 100644 index a29659ef5..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/effective.json +++ /dev/null @@ -1,399 +0,0 @@ -{ - "modules": { - "ab": { - "class": "bzt.modules.ab.ApacheBenchmarkExecutor" - }, - "gatling": { - "class": "bzt.modules.gatling.GatlingExecutor", - "properties": { - "gatling.data.file.bufferSize": 256 - } - }, - "grinder": { - "class": "bzt.modules.grinder.GrinderExecutor", - "properties": { - "grinder.useConsole": false - } - }, - "jmeter": { - "class": "bzt.modules.jmeter.JMeterExecutor", - "protocol-handlers": { - "http": "bzt.jmx.http.HTTPProtocolHandler" - }, - "plugins": [ - "jpgc-casutg", - "jpgc-dummy", - "jpgc-ffw", - "jpgc-fifo", - "jpgc-functions", - "jpgc-json", - "jpgc-perfmon", - "jpgc-prmctl", - "jpgc-tst" - ], - "properties": { - "jmeter.save.saveservice.autoflush": "true", - "jmeter.save.saveservice.connect_time": "true", - "jmeterengine.force.system.exit": "true", - "summariser.name": "", - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - }, - "version": "5.2.1", - "path": "/Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh" - }, - "locust": { - "class": "bzt.modules.locustio.LocustIOExecutor" - }, - "molotov": { - "class": "bzt.modules.molotov.MolotovExecutor" - }, - "pbench": { - "class": "bzt.modules.pbench.PBenchExecutor" - }, - "siege": { - "class": "bzt.modules.siege.SiegeExecutor" - }, - "tsung": { - "class": "bzt.modules.tsung.TsungExecutor" - }, - "selenium": { - "class": "bzt.modules.selenium.SeleniumExecutor" - }, - "junit": { - "class": "bzt.modules.java.JUnitTester" - }, - "testng": { - "class": "bzt.modules.java.TestNGTester" - }, - "rspec": { - "class": "bzt.modules.ruby.RSpecTester" - }, - "mocha": { - "class": "bzt.modules.javascript.MochaTester" - }, - "nunit": { - "class": "bzt.modules.csharp.NUnitExecutor" - }, - "pytest": { - "class": "bzt.modules.pytest.PyTestExecutor" - }, - "wdio": { - "class": "bzt.modules.javascript.WebdriverIOExecutor" - }, - "robot": { - "class": "bzt.modules.robot.RobotExecutor" - }, - "newman": { - "class": "bzt.modules.javascript.NewmanExecutor" - }, - "apiritif": { - "class": "bzt.modules.apiritif.ApiritifNoseExecutor" - }, - "local": { - "class": "bzt.modules.provisioning.Local" - }, - "monitoring": { - "class": "bzt.modules.monitoring.Monitoring" - }, - "passfail": { - "class": "bzt.modules.passfail.PassFailStatus" - }, - "shellexec": { - "class": "bzt.modules.shellexec.ShellExecutor" - }, - "junit-xml": { - "class": "bzt.modules.reporting.JUnitXMLReporter" - }, - "install-checker": { - "class": "bzt.modules.services.InstallChecker" - }, - "console": { - "class": "bzt.modules.console.ConsoleStatusReporter", - "disable": true - }, - "consolidator": { - "class": "bzt.modules.aggregator.ConsolidatingAggregator", - "percentiles": [ - 0.0, - 50.0, - 90.0, - 95.0, - 99.0, - 99.9, - 100.0 - ] - }, - "final-stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "functional-consolidator": { - "class": "bzt.modules.functional.FunctionalAggregator" - }, - "android-emulator": { - "class": "bzt.modules.services.AndroidEmulatorLoader" - }, - "appium": { - "class": "bzt.modules.services.AppiumLoader" - }, - "virtual-display": { - "class": "bzt.modules.services.VirtualDisplay" - }, - "external-results-loader": { - "class": "bzt.modules.external.ExternalResultsLoader" - }, - "blazemeter": { - "class": "bzt.modules.blazemeter.BlazeMeterUploader" - }, - "cloud": { - "class": "bzt.modules.blazemeter.CloudProvisioning", - "default-location": "us-central1-a" - }, - "proxy2jmx": { - "class": "bzt.modules.proxy2jmx.Proxy2JMX" - }, - "unpacker": { - "class": "bzt.modules.services.Unpacker" - }, - "screenshoter": { - "class": "bzt.modules.blazemeter.ServiceStubScreenshoter" - }, - "capturehar": { - "class": "bzt.modules.blazemeter.ServiceStubCaptureHAR" - }, - "final_stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "server_local_monitoring": { - "class": "metrics_monitoring_taurus.Monitor" - }, - "new_passfail": { - "class": "new_passfail.PassFailStatus" - } - }, - "cli-aliases": { - "cloud": { - "provisioning": "cloud" - }, - "detach": { - "modules": { - "cloud": { - "detach": true - } - } - }, - "func": { - "modules": { - "console": { - "disable": true - } - }, - "settings": { - "aggregator": "functional-consolidator" - } - }, - "gui": { - "modules": { - "jmeter": { - "gui": true - } - } - }, - "install-tools": { - "services": [ - "install-checker" - ] - }, - "local": { - "provisioning": "local" - }, - "locations": { - "modules": { - "cloud": { - "dump-locations": true - } - }, - "provisioning": "cloud" - }, - "report": { - "reporting": [ - "blazemeter" - ] - }, - "public": { - "modules": { - "blazemeter": { - "public-report": true - } - } - }, - "sequential": { - "modules": { - "local": { - "sequential": true - } - } - }, - "lint": { - "cli": { - "linter": { - "lint-and-exit": true - } - } - }, - "nolint": { - "cli": { - "linter": { - "disable": true - } - } - } - }, - "cli": { - "linter": { - "disable": false, - "warn-on-unfamiliar-fields": false, - "ignored-warnings": [], - "lint-and-exit": false - } - }, - "settings": { - "aggregator": "consolidator", - "default-executor": "jmeter", - "env": { - "OBJC_DISABLE_INITIALIZE_FORK_SAFETY": "YES", - "BASEDIR": "tmp/run345/inference_taurus_local_monitoring_criteria", - "TAURUS_ARTIFACTS_DIR": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria" - }, - "artifacts-dir": "tmp/run345/inference_taurus_local_monitoring_criteria" - }, - "provisioning": "local", - "reporting": [ - { - "module": "final-stats" - }, - { - "module": "console" - }, - { - "module": "passfail", - "criteria": [ - { - "subject": "fail", - "condition": ">", - "threshold": "50%", - "logic": "for", - "timeframe": 0, - "label": "s2_fail ", - "stop": true, - "fail": true, - "message": null - }, - { - "subject": "p90", - "condition": ">", - "threshold": "250ms", - "logic": "for", - "timeframe": 0, - "label": "s2_p90 ", - "stop": true, - "fail": true, - "message": null - }, - { - "subject": "avg-rt", - "condition": ">", - "threshold": "1s", - "logic": "for", - "timeframe": 0, - "label": "s2_avg_rt ", - "stop": true, - "fail": true, - "message": null - }, - { - "class": "bzt.modules.monitoring.MonitoringCriteria", - "subject": "ServerLocalClient/sum_workers_memory_rss", - "condition": ">", - "threshold": 132000000, - "timeframe": "1s", - "stop": true, - "fail": true - } - ] - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit.xml" - }, - { - "module": "junit-xml", - "data-source": "sample-labels", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit-1.xml" - }, - { - "module": "final-stats", - "dump-csv": "tmp/run345/inference_taurus_local_monitoring_criteria/final_stats.csv" - } - ], - "services": [ - { - "module": "monitoring", - "local": [ - { - "metrics": [ - "cpu", - "mem", - "bytes-sent", - "bytes-recv", - "disk-read", - "disk-write", - "disk-space", - "engine-loop", - "conn-all" - ] - } - ] - }, - { - "module": "server_local_monitoring", - "ServerLocalClient": [ - { - "interval": "1s", - "metrics": [ - "cpu", - "disk-space", - "mem", - "sum_workers_memory_rss" - ] - } - ] - } - ], - "execution": [ - { - "concurrency": { - "local": 1 - }, - "ramp-up": "5s", - "hold-for": "20s", - "scenario": "Inference", - "executor": "jmeter", - "throughput": {} - } - ], - "scenarios": { - "Inference": { - "script": "/Users/mahesh/git/multi-model-server/tests/performance/tests/register_and_inference.jmx" - } - }, - "included-configs": [], - "version": "1.14.2" -} diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/effective.yml b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/effective.yml deleted file mode 100644 index 53eb8a51e..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/effective.yml +++ /dev/null @@ -1,270 +0,0 @@ ---- -cli: - linter: - disable: false - ignored-warnings: [] - lint-and-exit: false - warn-on-unfamiliar-fields: false -cli-aliases: - cloud: - provisioning: cloud - detach: - modules: - cloud: - detach: true - func: - modules: - console: - disable: true - settings: - aggregator: functional-consolidator - gui: - modules: - jmeter: - gui: true - install-tools: - services: - - install-checker - lint: - cli: - linter: - lint-and-exit: true - local: - provisioning: local - locations: - modules: - cloud: - dump-locations: true - provisioning: cloud - nolint: - cli: - linter: - disable: true - public: - modules: - blazemeter: - public-report: true - report: - reporting: - - blazemeter - sequential: - modules: - local: - sequential: true -execution: -- concurrency: - local: 1 - executor: jmeter - hold-for: 20s - ramp-up: 5s - scenario: Inference - throughput: {} -included-configs: [] -modules: - ab: - class: bzt.modules.ab.ApacheBenchmarkExecutor - android-emulator: - class: bzt.modules.services.AndroidEmulatorLoader - apiritif: - class: bzt.modules.apiritif.ApiritifNoseExecutor - appium: - class: bzt.modules.services.AppiumLoader - blazemeter: - class: bzt.modules.blazemeter.BlazeMeterUploader - capturehar: - class: bzt.modules.blazemeter.ServiceStubCaptureHAR - cloud: - class: bzt.modules.blazemeter.CloudProvisioning - default-location: us-central1-a - console: - class: bzt.modules.console.ConsoleStatusReporter - disable: true - consolidator: - class: bzt.modules.aggregator.ConsolidatingAggregator - percentiles: - - 0.0 - - 50.0 - - 90.0 - - 95.0 - - 99.0 - - 99.9 - - 100.0 - external-results-loader: - class: bzt.modules.external.ExternalResultsLoader - final-stats: - class: bzt.modules.reporting.FinalStatus - final_stats: - class: bzt.modules.reporting.FinalStatus - functional-consolidator: - class: bzt.modules.functional.FunctionalAggregator - gatling: - class: bzt.modules.gatling.GatlingExecutor - properties: - gatling.data.file.bufferSize: 256 - grinder: - class: bzt.modules.grinder.GrinderExecutor - properties: - grinder.useConsole: false - install-checker: - class: bzt.modules.services.InstallChecker - jmeter: - class: bzt.modules.jmeter.JMeterExecutor - path: /Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh - plugins: - - jpgc-casutg - - jpgc-dummy - - jpgc-ffw - - jpgc-fifo - - jpgc-functions - - jpgc-json - - jpgc-perfmon - - jpgc-prmctl - - jpgc-tst - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - jmeter.save.saveservice.autoflush: 'true' - jmeter.save.saveservice.connect_time: 'true' - jmeterengine.force.system.exit: 'true' - management_port: 8081 - port: 8080 - protocol: http - summariser.name: '' - protocol-handlers: - http: bzt.jmx.http.HTTPProtocolHandler - version: 5.2.1 - junit: - class: bzt.modules.java.JUnitTester - junit-xml: - class: bzt.modules.reporting.JUnitXMLReporter - local: - class: bzt.modules.provisioning.Local - locust: - class: bzt.modules.locustio.LocustIOExecutor - mocha: - class: bzt.modules.javascript.MochaTester - molotov: - class: bzt.modules.molotov.MolotovExecutor - monitoring: - class: bzt.modules.monitoring.Monitoring - new_passfail: - class: new_passfail.PassFailStatus - newman: - class: bzt.modules.javascript.NewmanExecutor - nunit: - class: bzt.modules.csharp.NUnitExecutor - passfail: - class: bzt.modules.passfail.PassFailStatus - pbench: - class: bzt.modules.pbench.PBenchExecutor - proxy2jmx: - class: bzt.modules.proxy2jmx.Proxy2JMX - pytest: - class: bzt.modules.pytest.PyTestExecutor - robot: - class: bzt.modules.robot.RobotExecutor - rspec: - class: bzt.modules.ruby.RSpecTester - screenshoter: - class: bzt.modules.blazemeter.ServiceStubScreenshoter - selenium: - class: bzt.modules.selenium.SeleniumExecutor - server_local_monitoring: - class: metrics_monitoring_taurus.Monitor - shellexec: - class: bzt.modules.shellexec.ShellExecutor - siege: - class: bzt.modules.siege.SiegeExecutor - testng: - class: bzt.modules.java.TestNGTester - tsung: - class: bzt.modules.tsung.TsungExecutor - unpacker: - class: bzt.modules.services.Unpacker - virtual-display: - class: bzt.modules.services.VirtualDisplay - wdio: - class: bzt.modules.javascript.WebdriverIOExecutor -provisioning: local -reporting: -- module: final-stats -- module: console -- criteria: - - condition: '>' - fail: true - label: 's2_fail ' - logic: for - message: null - stop: true - subject: fail - threshold: 50% - timeframe: 0 - - condition: '>' - fail: true - label: 's2_p90 ' - logic: for - message: null - stop: true - subject: p90 - threshold: 250ms - timeframe: 0 - - condition: '>' - fail: true - label: 's2_avg_rt ' - logic: for - message: null - stop: true - subject: avg-rt - threshold: 1s - timeframe: 0 - - class: bzt.modules.monitoring.MonitoringCriteria - condition: '>' - fail: true - stop: true - subject: ServerLocalClient/sum_workers_memory_rss - threshold: 132000000 - timeframe: 1s - module: passfail -- module: passfail -- data-source: pass-fail - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit.xml - module: junit-xml -- data-source: sample-labels - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit-1.xml - module: junit-xml -- dump-csv: tmp/run345/inference_taurus_local_monitoring_criteria/final_stats.csv - module: final-stats -scenarios: - Inference: - script: /Users/mahesh/git/multi-model-server/tests/performance/tests/register_and_inference.jmx -services: -- local: - - metrics: - - cpu - - mem - - bytes-sent - - bytes-recv - - disk-read - - disk-write - - disk-space - - engine-loop - - conn-all - module: monitoring -- ServerLocalClient: - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - sum_workers_memory_rss - module: server_local_monitoring -settings: - aggregator: consolidator - artifacts-dir: tmp/run345/inference_taurus_local_monitoring_criteria - default-executor: jmeter - env: - BASEDIR: tmp/run345/inference_taurus_local_monitoring_criteria - OBJC_DISABLE_INITIALIZE_FORK_SAFETY: 'YES' - TAURUS_ARTIFACTS_DIR: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria -version: 1.14.2 - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/error.jtl b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/error.jtl deleted file mode 100644 index d212a8b27..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/error.jtl +++ /dev/null @@ -1,56 +0,0 @@ - - - - - Connection: keep-alive -Content-Length: 111188 -Content-Type: multipart/form-data; boundary=LKGIyFsx9XbUAwKYl8pxndajiaQco4X7Qj6Wr7 -Host: 127.0.0.1:8080 -User-Agent: Apache-HttpClient/4.5.10 (Java/1.8.0_242) - - java.net.SocketException: Socket closed - at java.net.SocketInputStream.socketRead0(Native Method) - at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) - at java.net.SocketInputStream.read(SocketInputStream.java:171) - at java.net.SocketInputStream.read(SocketInputStream.java:141) - at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:137) - at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:153) - at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:280) - at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138) - at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56) - at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259) - at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163) - at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:157) - at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273) - at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125) - at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272) - at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186) - at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) - at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) - at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) - at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) - at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.executeRequest(HTTPHC4Impl.java:850) - at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.sample(HTTPHC4Impl.java:561) - at org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy.sample(HTTPSamplerProxy.java:67) - at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1282) - at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1271) - at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:627) - at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:551) - at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:490) - at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:257) - at java.lang.Thread.run(Thread.java:748) - - - POST - --LKGIyFsx9XbUAwKYl8pxndajiaQco4X7Qj6Wr7 -Content-Disposition: form-data; name="data"; filename="kitten.jpg" -Content-Type: image/jpeg -Content-Transfer-Encoding: binary - -<actual file content, not shown here> ---LKGIyFsx9XbUAwKYl8pxndajiaQco4X7Qj6Wr7-- - - http://127.0.0.1:8080/predictions/squeezenet_v1.1 - - - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/final_stats.csv b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/final_stats.csv deleted file mode 100644 index 4fbd07c9d..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/final_stats.csv +++ /dev/null @@ -1,6 +0,0 @@ -label,avg_ct,avg_lt,avg_rt,bytes,concurrency,fail,stdev_rt,succ,throughput,perc_0.0,perc_50.0,perc_90.0,perc_95.0,perc_99.0,perc_99.9,perc_100.0,rc_200,rc_202,rc_SocketException -Setup squeezenet_v1.1 Model,0.03100,21.29800,21.30100,321,1,0,0.00300,1,1,21.29600,21.29600,21.29600,21.29600,21.29600,21.29600,21.29600,1,, -,0.00151,0.44484,0.44537,35814,1,1,2.94985,50,51,0.00400,0.02800,0.03200,0.03300,21.29600,21.29600,21.29600,49,1,1 -Scale Up squeezenet_v1.1 Model,0.00300,0.00400,0.00400,319,1,0,0.00000,1,1,0.00400,0.00400,0.00400,0.00400,0.00400,0.00400,0.00400,,1, -Inference Request,0.00088,0.02871,0.02921,34851,1,1,0.00361,47,48,0.02100,0.02800,0.03200,0.03300,0.05000,0.05000,0.05000,47,,1 -Scale Down squeezenet_v1.1,0.00100,0.00700,0.00700,323,1,0,0.00000,1,1,0.00700,0.00700,0.00700,0.00700,0.00700,0.00700,0.00700,1,, diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/global_config.yaml b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/global_config.yaml deleted file mode 100644 index 94731f4a3..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/global_config.yaml +++ /dev/null @@ -1,26 +0,0 @@ -modules: - jmeter: - # These are JMeter test case properties. These variables are used in jmx files. - # Change the vaues as per your setup - properties: - hostname : 127.0.0.1 # MMS properties - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg # make sure jpg is available at this path - # if relative path is provided this will be relative to current working directory - -# DO-NOT change properties below unless you know what you are doing. -# They are needed for performance test suite runner script. -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -settings: - env: - BASEDIR : '.' diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml deleted file mode 100644 index c21a2cd18..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml +++ /dev/null @@ -1,43 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 5s - hold-for: 20s - scenario: Inference - -scenarios: - Inference: - script: register_and_inference.jmx - -modules: - server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. - new_passfail: - class: new_passfail.PassFailStatus - - - -services: - - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - sum_workers_memory_rss - -reporting: -- module: passfail - criteria: - - fail of s2_fail >50%, stop as failed - - p90 of s2_p90 >250ms , stop as failed - - avg-rt of s2_avg_rt >1s , stop as failed - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_workers_memory_rss - condition: '>' - threshold: 132000000 - timeframe: 1s - stop : true - fail : true diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/jmeter-bzt.properties b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/jmeter-bzt.properties deleted file mode 100644 index d6649f49c..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/jmeter-bzt.properties +++ /dev/null @@ -1,14 +0,0 @@ -jmeter.save.saveservice.autoflush=true -jmeter.save.saveservice.connect_time=true -jmeterengine.force.system.exit=true -summariser.name= -hostname=127.0.0.1 -port=8080 -management_port=8081 -protocol=http -input_filepath=kitten.jpg -jmeterengine.nongui.port=4445 -jmeterengine.nongui.maxport=4445 -jmeter.save.saveservice.timestamp_format=ms -sampleresult.default.encoding=UTF-8 -user.classpath=/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria:/Users/mahesh/git/multi-model-server/tests/performance/tests diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/jmeter.err b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/jmeter.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/jmeter.out b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/jmeter.out deleted file mode 100644 index e8c1dc1e5..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/jmeter.out +++ /dev/null @@ -1,6 +0,0 @@ -Created the tree successfully using /Users/mahesh/git/multi-model-server/tests/performance/tests/modified_register_and_inference.jmx -Starting standalone test @ Thu Jun 18 22:24:31 IST 2020 (1592499271411) -Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445 -Command: Shutdown received from /127.0.0.1 -Tidying up ... @ Thu Jun 18 22:24:57 IST 2020 (1592499297265) -... end of run diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/kpi.jtl b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/kpi.jtl deleted file mode 100644 index 33fbc95d2..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/kpi.jtl +++ /dev/null @@ -1,52 +0,0 @@ -timeStamp,elapsed,label,responseCode,responseMessage,threadName,success,bytes,grpThreads,allThreads,Latency,Hostname,Connect -1592499271904,21301,Setup squeezenet_v1.1 Model,200,OK,setup squeezenet_v1.1 1-1,true,321,1,1,21298,BRL06570.local,31 -1592499293207,4,Scale Up squeezenet_v1.1 Model,202,Accepted,setup squeezenet_v1.1 1-1,true,319,1,1,4,BRL06570.local,3 -1592499295813,50,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,50,BRL06570.local,7 -1592499295869,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499295900,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499295929,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499295958,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499295987,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499296017,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499296048,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499296077,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499296106,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499296135,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499296164,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499296198,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499296229,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499296259,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499296288,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499296317,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499296350,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499296383,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499296411,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499296438,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499296466,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,31,BRL06570.local,0 -1592499296498,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499296528,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499296557,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499296586,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499296614,33,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,33,BRL06570.local,1 -1592499296648,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,0 -1592499296677,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499296708,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,0 -1592499296736,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499296765,32,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,32,BRL06570.local,1 -1592499296798,30,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,0 -1592499296828,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499296857,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499296885,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499296914,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,0 -1592499296942,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499296971,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499296999,31,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,30,BRL06570.local,1 -1592499297030,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499297057,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499297085,27,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499297112,29,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,29,BRL06570.local,1 -1592499297141,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499297170,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,27,BRL06570.local,1 -1592499297198,28,Inference Request,200,,squeezenet_v1.1 Inference-ThreadStarter 1-1,true,687,1,1,28,BRL06570.local,1 -1592499297226,21,Inference Request,Non HTTP response code: java.net.SocketException,Non HTTP response message: Socket closed,squeezenet_v1.1 Inference-ThreadStarter 1-1,false,2562,1,1,0,BRL06570.local,1 -1592499297257,7,Scale Down squeezenet_v1.1,200,OK,tearDown squeezenet_v1.1 1-1,true,323,1,1,7,BRL06570.local,1 diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/merged.json b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/merged.json deleted file mode 100644 index 52bdd81f5..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/merged.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "execution": [ - { - "concurrency": 1, - "ramp-up": "5s", - "hold-for": "20s", - "scenario": "Inference" - } - ], - "scenarios": { - "Inference": { - "script": "register_and_inference.jmx" - } - }, - "modules": { - "server_local_monitoring": { - "class": "metrics_monitoring_taurus.Monitor" - }, - "new_passfail": { - "class": "new_passfail.PassFailStatus" - }, - "jmeter": { - "properties": { - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - } - } - }, - "services": [ - { - "module": "server_local_monitoring", - "ServerLocalClient": [ - { - "interval": "1s", - "metrics": [ - "cpu", - "disk-space", - "mem", - "sum_workers_memory_rss" - ] - } - ] - } - ], - "reporting": [ - { - "module": "passfail", - "criteria": [ - "fail of s2_fail >50%, stop as failed", - "p90 of s2_p90 >250ms , stop as failed", - "avg-rt of s2_avg_rt >1s , stop as failed", - { - "class": "bzt.modules.monitoring.MonitoringCriteria", - "subject": "ServerLocalClient/sum_workers_memory_rss", - "condition": ">", - "threshold": 132000000, - "timeframe": "1s", - "stop": true, - "fail": true - } - ] - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail" - }, - { - "module": "junit-xml", - "data-source": "sample-labels" - }, - { - "module": "final-stats", - "dump-csv": "${BASEDIR}/final_stats.csv" - } - ], - "settings": { - "env": { - "BASEDIR": "." - } - } -} diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/merged.yml b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/merged.yml deleted file mode 100644 index a3bd76bfc..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/merged.yml +++ /dev/null @@ -1,54 +0,0 @@ ---- -execution: -- concurrency: 1 - hold-for: 20s - ramp-up: 5s - scenario: Inference -modules: - jmeter: - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - management_port: 8081 - port: 8080 - protocol: http - new_passfail: - class: new_passfail.PassFailStatus - server_local_monitoring: - class: metrics_monitoring_taurus.Monitor -reporting: -- criteria: - - fail of s2_fail >50%, stop as failed - - p90 of s2_p90 >250ms , stop as failed - - avg-rt of s2_avg_rt >1s , stop as failed - - class: bzt.modules.monitoring.MonitoringCriteria - condition: '>' - fail: true - stop: true - subject: ServerLocalClient/sum_workers_memory_rss - threshold: 132000000 - timeframe: 1s - module: passfail -- module: passfail -- data-source: pass-fail - module: junit-xml -- data-source: sample-labels - module: junit-xml -- dump-csv: ${BASEDIR}/final_stats.csv - module: final-stats -scenarios: - Inference: - script: register_and_inference.jmx -services: -- ServerLocalClient: - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - sum_workers_memory_rss - module: server_local_monitoring -settings: - env: - BASEDIR: . - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/modified_register_and_inference.jmx b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/modified_register_and_inference.jmx deleted file mode 100644 index 80bf86973..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/modified_register_and_inference.jmx +++ /dev/null @@ -1,176 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - cnn_url - https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - = - The url from where to fetch noop model from - - - scale_up_workers - ${__P(min_workers,1)} - = - The workers to scale No op model to - - - scale_down_workers - 0 - Offload the No-Op Model - = - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_up_workers} - PUT - true - false - true - false - - - - - - - continue15020S - - - - - ${__P(input_filepath)} - data - image/jpeg - - - - - - - - - - - /predictions/${model} - POST - true - false - true - true - - - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_down_workers} - DELETE - true - false - true - false - - - - - - - /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue - - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit-1.xml b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit-1.xml deleted file mode 100644 index 51879decb..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit-1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Non HTTP response message: Socket closed -(status code is Non HTTP response code: java.net.SocketException) -(total errors of this type: 1) - - - - - - diff --git a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit.xml b/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit.xml deleted file mode 100644 index 0c2d5a7b1..000000000 --- a/tests/performance/tmp/run345/inference_taurus_local_monitoring_criteria/xunit.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/tests/performance/tmp/run345/junit.html b/tests/performance/tmp/run345/junit.html deleted file mode 100644 index 152b363b8..000000000 --- a/tests/performance/tmp/run345/junit.html +++ /dev/null @@ -1,962 +0,0 @@ - - - - - - - - - - - -

- - -
- - - - -
- - -
-

inference_server_monitoring

- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nameinference_server_monitoring
hostnameBRL06570.local
timestamp1592498979
time0
tests0
failures0
skipped0
errors0
statusskipped
-
-

- - - - -
- -
-
- - -
-

downscale_workers

- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
namedownscale_workers
tests1
errors0
failures0
skipped0
time0
hostnameBRL06570.local
timestamp1592499057
statussuccess
-
-

- - - - -
- -
-
- scenario_0: ServerLocalClient/total_workers<2 for 1s -
-
-
- - - -
- MESSAGE -
-
- -
- STDOUT - -
- -
- STDERR -
-
-
- Excetued in seconds -
-
-
- -
-
- - -
-

upscale_workers

- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nameupscale_workers
tests1
errors0
failures0
skipped0
time0
hostnameBRL06570.local
timestamp1592499121
statussuccess
-
-

- - - - -
- -
-
- scenario_0: ServerLocalClient/total_workers>4 for 1s -
-
-
- - - -
- MESSAGE -
-
- -
- STDOUT - -
- -
- STDERR -
-
-
- Excetued in seconds -
-
-
- -
-
- - -
-

inference_taurus_local_monitoring

- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nameinference_taurus_local_monitoring
hostnameBRL06570.local
timestamp1592499194
time0
tests0
failures0
skipped0
errors0
statusskipped
-
-

- - - - -
- -
-
- - -
-

inference_taurus_local_monitoring_criteria

- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nameinference_taurus_local_monitoring_criteria
tests4
errors1
failures0
skipped0
time0
hostnameBRL06570.local
timestamp1592499266
statuserror
-
-

- - - - -
- -
-
- scenario_0: fail of s2_fail >50% -
-
-
- - - -
- MESSAGE -
-
- -
- STDOUT - -
- -
- STDERR -
-
-
- Excetued in seconds -
-
-
- -
-
- scenario_0: p90 of s2_p90 >250ms -
-
-
- - - -
- MESSAGE -
-
- -
- STDOUT - -
- -
- STDERR -
-
-
- Excetued in seconds -
-
-
- -
-
- scenario_0: avg-rt of s2_avg_rt >1s -
-
-
- - - -
- MESSAGE -
-
- -
- STDOUT - -
- -
- STDERR -
-
-
- Excetued in seconds -
-
-
- -
-
- scenario_0: ServerLocalClient/sum_workers_memory_rss>132000000 for 1s -
-
-
- - - TYPE: pass/fail criteria triggered

- - -
- MESSAGE -
Failed: ServerLocalClient/sum_workers_memory_rss>132000000 for 1 sec
-
- -
- STDOUT - -
- -
- STDERR -
None
-
-
- Excetued in seconds -
-
-
- -
-
- - -
-

call_jmx

- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
namecall_jmx
hostnameBRL06570.local
timestamp1592499298
time0
tests0
failures0
skipped0
errors0
statusskipped
-
-

- - - - -
- -
-
- - -
-

inference_server_monitoring_criteria

- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nameinference_server_monitoring_criteria
tests4
errors0
failures0
skipped0
time0
hostnameBRL06570.local
timestamp1592499374
statussuccess
-
-

- - - - -
- -
-
- scenario_0: fail of s2_fail >50% -
-
-
- - - -
- MESSAGE -
-
- -
- STDOUT - -
- -
- STDERR -
-
-
- Excetued in seconds -
-
-
- -
-
- scenario_0: p90 of s2_p90 >250ms -
-
-
- - - -
- MESSAGE -
-
- -
- STDOUT - -
- -
- STDERR -
-
-
- Excetued in seconds -
-
-
- -
-
- scenario_0: avg-rt of s2_avg_rt >1s -
-
-
- - - -
- MESSAGE -
-
- -
- STDOUT - -
- -
- STDERR -
-
-
- Excetued in seconds -
-
-
- -
-
- scenario_0: mms-inference-server/sum_workers_file_descriptors>80 for 1s -
-
-
- - - -
- MESSAGE -
-
- -
- STDOUT - -
- -
- STDERR -
-
-
- Excetued in seconds -
-
-
- -
-
- -
-
- -
- - - \ No newline at end of file diff --git a/tests/performance/tmp/run345/junit.xml b/tests/performance/tmp/run345/junit.xml deleted file mode 100644 index 3436f4150..000000000 --- a/tests/performance/tmp/run345/junit.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/tests/performance/tmp/run345/upscale_workers/effective.json b/tests/performance/tmp/run345/upscale_workers/effective.json deleted file mode 100644 index d9842c064..000000000 --- a/tests/performance/tmp/run345/upscale_workers/effective.json +++ /dev/null @@ -1,366 +0,0 @@ -{ - "modules": { - "ab": { - "class": "bzt.modules.ab.ApacheBenchmarkExecutor" - }, - "gatling": { - "class": "bzt.modules.gatling.GatlingExecutor", - "properties": { - "gatling.data.file.bufferSize": 256 - } - }, - "grinder": { - "class": "bzt.modules.grinder.GrinderExecutor", - "properties": { - "grinder.useConsole": false - } - }, - "jmeter": { - "class": "bzt.modules.jmeter.JMeterExecutor", - "protocol-handlers": { - "http": "bzt.jmx.http.HTTPProtocolHandler" - }, - "plugins": [ - "jpgc-casutg", - "jpgc-dummy", - "jpgc-ffw", - "jpgc-fifo", - "jpgc-functions", - "jpgc-json", - "jpgc-perfmon", - "jpgc-prmctl", - "jpgc-tst" - ], - "properties": { - "jmeter.save.saveservice.autoflush": "true", - "jmeter.save.saveservice.connect_time": "true", - "jmeterengine.force.system.exit": "true", - "summariser.name": "", - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - }, - "version": "5.2.1", - "path": "/Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh" - }, - "locust": { - "class": "bzt.modules.locustio.LocustIOExecutor" - }, - "molotov": { - "class": "bzt.modules.molotov.MolotovExecutor" - }, - "pbench": { - "class": "bzt.modules.pbench.PBenchExecutor" - }, - "siege": { - "class": "bzt.modules.siege.SiegeExecutor" - }, - "tsung": { - "class": "bzt.modules.tsung.TsungExecutor" - }, - "selenium": { - "class": "bzt.modules.selenium.SeleniumExecutor" - }, - "junit": { - "class": "bzt.modules.java.JUnitTester" - }, - "testng": { - "class": "bzt.modules.java.TestNGTester" - }, - "rspec": { - "class": "bzt.modules.ruby.RSpecTester" - }, - "mocha": { - "class": "bzt.modules.javascript.MochaTester" - }, - "nunit": { - "class": "bzt.modules.csharp.NUnitExecutor" - }, - "pytest": { - "class": "bzt.modules.pytest.PyTestExecutor" - }, - "wdio": { - "class": "bzt.modules.javascript.WebdriverIOExecutor" - }, - "robot": { - "class": "bzt.modules.robot.RobotExecutor" - }, - "newman": { - "class": "bzt.modules.javascript.NewmanExecutor" - }, - "apiritif": { - "class": "bzt.modules.apiritif.ApiritifNoseExecutor" - }, - "local": { - "class": "bzt.modules.provisioning.Local" - }, - "monitoring": { - "class": "bzt.modules.monitoring.Monitoring" - }, - "passfail": { - "class": "bzt.modules.passfail.PassFailStatus" - }, - "shellexec": { - "class": "bzt.modules.shellexec.ShellExecutor" - }, - "junit-xml": { - "class": "bzt.modules.reporting.JUnitXMLReporter" - }, - "install-checker": { - "class": "bzt.modules.services.InstallChecker" - }, - "console": { - "class": "bzt.modules.console.ConsoleStatusReporter", - "disable": true - }, - "consolidator": { - "class": "bzt.modules.aggregator.ConsolidatingAggregator", - "percentiles": [ - 0.0, - 50.0, - 90.0, - 95.0, - 99.0, - 99.9, - 100.0 - ] - }, - "final-stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "functional-consolidator": { - "class": "bzt.modules.functional.FunctionalAggregator" - }, - "android-emulator": { - "class": "bzt.modules.services.AndroidEmulatorLoader" - }, - "appium": { - "class": "bzt.modules.services.AppiumLoader" - }, - "virtual-display": { - "class": "bzt.modules.services.VirtualDisplay" - }, - "external-results-loader": { - "class": "bzt.modules.external.ExternalResultsLoader" - }, - "blazemeter": { - "class": "bzt.modules.blazemeter.BlazeMeterUploader" - }, - "cloud": { - "class": "bzt.modules.blazemeter.CloudProvisioning", - "default-location": "us-central1-a" - }, - "proxy2jmx": { - "class": "bzt.modules.proxy2jmx.Proxy2JMX" - }, - "unpacker": { - "class": "bzt.modules.services.Unpacker" - }, - "screenshoter": { - "class": "bzt.modules.blazemeter.ServiceStubScreenshoter" - }, - "capturehar": { - "class": "bzt.modules.blazemeter.ServiceStubCaptureHAR" - }, - "final_stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "server_local_monitoring": { - "class": "metrics_monitoring_taurus.Monitor" - }, - "new_passfail": { - "class": "new_passfail.PassFailStatus" - } - }, - "cli-aliases": { - "cloud": { - "provisioning": "cloud" - }, - "detach": { - "modules": { - "cloud": { - "detach": true - } - } - }, - "func": { - "modules": { - "console": { - "disable": true - } - }, - "settings": { - "aggregator": "functional-consolidator" - } - }, - "gui": { - "modules": { - "jmeter": { - "gui": true - } - } - }, - "install-tools": { - "services": [ - "install-checker" - ] - }, - "local": { - "provisioning": "local" - }, - "locations": { - "modules": { - "cloud": { - "dump-locations": true - } - }, - "provisioning": "cloud" - }, - "report": { - "reporting": [ - "blazemeter" - ] - }, - "public": { - "modules": { - "blazemeter": { - "public-report": true - } - } - }, - "sequential": { - "modules": { - "local": { - "sequential": true - } - } - }, - "lint": { - "cli": { - "linter": { - "lint-and-exit": true - } - } - }, - "nolint": { - "cli": { - "linter": { - "disable": true - } - } - } - }, - "cli": { - "linter": { - "disable": false, - "warn-on-unfamiliar-fields": false, - "ignored-warnings": [], - "lint-and-exit": false - } - }, - "settings": { - "aggregator": "consolidator", - "default-executor": "jmeter", - "env": { - "OBJC_DISABLE_INITIALIZE_FORK_SAFETY": "YES", - "BASEDIR": "tmp/run345/upscale_workers", - "TAURUS_ARTIFACTS_DIR": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/upscale_workers" - }, - "artifacts-dir": "tmp/run345/upscale_workers" - }, - "provisioning": "local", - "reporting": [ - { - "module": "final-stats" - }, - { - "module": "console" - }, - { - "module": "passfail", - "criteria": [ - { - "class": "bzt.modules.monitoring.MonitoringCriteria", - "subject": "ServerLocalClient/total_workers", - "condition": ">", - "threshold": 4, - "timeframe": "1s", - "stop": true, - "fail": true - } - ] - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/upscale_workers/xunit.xml" - }, - { - "module": "junit-xml", - "data-source": "sample-labels", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/upscale_workers/xunit-1.xml" - }, - { - "module": "final-stats", - "dump-csv": "tmp/run345/upscale_workers/final_stats.csv" - } - ], - "services": [ - { - "module": "monitoring", - "local": [ - { - "metrics": [ - "cpu", - "mem", - "bytes-sent", - "bytes-recv", - "disk-read", - "disk-write", - "disk-space", - "engine-loop", - "conn-all" - ] - } - ] - }, - { - "module": "server_local_monitoring", - "ServerLocalClient": [ - { - "interval": "1s", - "metrics": [ - "cpu", - "disk-space", - "mem", - "total_workers" - ] - } - ] - } - ], - "execution": [ - { - "concurrency": { - "local": 3 - }, - "ramp-up": "5s", - "iterations": 20, - "scenario": "Upscale", - "executor": "jmeter", - "throughput": {} - } - ], - "scenarios": { - "Upscale": { - "script": "/Users/mahesh/git/multi-model-server/tests/performance/tests/upscale_workers.jmx" - } - }, - "included-configs": [], - "version": "1.14.2" -} diff --git a/tests/performance/tmp/run345/upscale_workers/effective.yml b/tests/performance/tmp/run345/upscale_workers/effective.yml deleted file mode 100644 index bbae3996f..000000000 --- a/tests/performance/tmp/run345/upscale_workers/effective.yml +++ /dev/null @@ -1,243 +0,0 @@ ---- -cli: - linter: - disable: false - ignored-warnings: [] - lint-and-exit: false - warn-on-unfamiliar-fields: false -cli-aliases: - cloud: - provisioning: cloud - detach: - modules: - cloud: - detach: true - func: - modules: - console: - disable: true - settings: - aggregator: functional-consolidator - gui: - modules: - jmeter: - gui: true - install-tools: - services: - - install-checker - lint: - cli: - linter: - lint-and-exit: true - local: - provisioning: local - locations: - modules: - cloud: - dump-locations: true - provisioning: cloud - nolint: - cli: - linter: - disable: true - public: - modules: - blazemeter: - public-report: true - report: - reporting: - - blazemeter - sequential: - modules: - local: - sequential: true -execution: -- concurrency: - local: 3 - executor: jmeter - iterations: 20 - ramp-up: 5s - scenario: Upscale - throughput: {} -included-configs: [] -modules: - ab: - class: bzt.modules.ab.ApacheBenchmarkExecutor - android-emulator: - class: bzt.modules.services.AndroidEmulatorLoader - apiritif: - class: bzt.modules.apiritif.ApiritifNoseExecutor - appium: - class: bzt.modules.services.AppiumLoader - blazemeter: - class: bzt.modules.blazemeter.BlazeMeterUploader - capturehar: - class: bzt.modules.blazemeter.ServiceStubCaptureHAR - cloud: - class: bzt.modules.blazemeter.CloudProvisioning - default-location: us-central1-a - console: - class: bzt.modules.console.ConsoleStatusReporter - disable: true - consolidator: - class: bzt.modules.aggregator.ConsolidatingAggregator - percentiles: - - 0.0 - - 50.0 - - 90.0 - - 95.0 - - 99.0 - - 99.9 - - 100.0 - external-results-loader: - class: bzt.modules.external.ExternalResultsLoader - final-stats: - class: bzt.modules.reporting.FinalStatus - final_stats: - class: bzt.modules.reporting.FinalStatus - functional-consolidator: - class: bzt.modules.functional.FunctionalAggregator - gatling: - class: bzt.modules.gatling.GatlingExecutor - properties: - gatling.data.file.bufferSize: 256 - grinder: - class: bzt.modules.grinder.GrinderExecutor - properties: - grinder.useConsole: false - install-checker: - class: bzt.modules.services.InstallChecker - jmeter: - class: bzt.modules.jmeter.JMeterExecutor - path: /Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh - plugins: - - jpgc-casutg - - jpgc-dummy - - jpgc-ffw - - jpgc-fifo - - jpgc-functions - - jpgc-json - - jpgc-perfmon - - jpgc-prmctl - - jpgc-tst - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - jmeter.save.saveservice.autoflush: 'true' - jmeter.save.saveservice.connect_time: 'true' - jmeterengine.force.system.exit: 'true' - management_port: 8081 - port: 8080 - protocol: http - summariser.name: '' - protocol-handlers: - http: bzt.jmx.http.HTTPProtocolHandler - version: 5.2.1 - junit: - class: bzt.modules.java.JUnitTester - junit-xml: - class: bzt.modules.reporting.JUnitXMLReporter - local: - class: bzt.modules.provisioning.Local - locust: - class: bzt.modules.locustio.LocustIOExecutor - mocha: - class: bzt.modules.javascript.MochaTester - molotov: - class: bzt.modules.molotov.MolotovExecutor - monitoring: - class: bzt.modules.monitoring.Monitoring - new_passfail: - class: new_passfail.PassFailStatus - newman: - class: bzt.modules.javascript.NewmanExecutor - nunit: - class: bzt.modules.csharp.NUnitExecutor - passfail: - class: bzt.modules.passfail.PassFailStatus - pbench: - class: bzt.modules.pbench.PBenchExecutor - proxy2jmx: - class: bzt.modules.proxy2jmx.Proxy2JMX - pytest: - class: bzt.modules.pytest.PyTestExecutor - robot: - class: bzt.modules.robot.RobotExecutor - rspec: - class: bzt.modules.ruby.RSpecTester - screenshoter: - class: bzt.modules.blazemeter.ServiceStubScreenshoter - selenium: - class: bzt.modules.selenium.SeleniumExecutor - server_local_monitoring: - class: metrics_monitoring_taurus.Monitor - shellexec: - class: bzt.modules.shellexec.ShellExecutor - siege: - class: bzt.modules.siege.SiegeExecutor - testng: - class: bzt.modules.java.TestNGTester - tsung: - class: bzt.modules.tsung.TsungExecutor - unpacker: - class: bzt.modules.services.Unpacker - virtual-display: - class: bzt.modules.services.VirtualDisplay - wdio: - class: bzt.modules.javascript.WebdriverIOExecutor -provisioning: local -reporting: -- module: final-stats -- module: console -- criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - condition: '>' - fail: true - stop: true - subject: ServerLocalClient/total_workers - threshold: 4 - timeframe: 1s - module: passfail -- module: passfail -- data-source: pass-fail - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/upscale_workers/xunit.xml - module: junit-xml -- data-source: sample-labels - filename: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/upscale_workers/xunit-1.xml - module: junit-xml -- dump-csv: tmp/run345/upscale_workers/final_stats.csv - module: final-stats -scenarios: - Upscale: - script: /Users/mahesh/git/multi-model-server/tests/performance/tests/upscale_workers.jmx -services: -- local: - - metrics: - - cpu - - mem - - bytes-sent - - bytes-recv - - disk-read - - disk-write - - disk-space - - engine-loop - - conn-all - module: monitoring -- ServerLocalClient: - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - total_workers - module: server_local_monitoring -settings: - aggregator: consolidator - artifacts-dir: tmp/run345/upscale_workers - default-executor: jmeter - env: - BASEDIR: tmp/run345/upscale_workers - OBJC_DISABLE_INITIALIZE_FORK_SAFETY: 'YES' - TAURUS_ARTIFACTS_DIR: /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/upscale_workers -version: 1.14.2 - diff --git a/tests/performance/tmp/run345/upscale_workers/error.jtl b/tests/performance/tmp/run345/upscale_workers/error.jtl deleted file mode 100644 index 32bb536a8..000000000 --- a/tests/performance/tmp/run345/upscale_workers/error.jtl +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/tmp/run345/upscale_workers/final_stats.csv b/tests/performance/tmp/run345/upscale_workers/final_stats.csv deleted file mode 100644 index 44ecfb4b2..000000000 --- a/tests/performance/tmp/run345/upscale_workers/final_stats.csv +++ /dev/null @@ -1,5 +0,0 @@ -label,avg_ct,avg_lt,avg_rt,bytes,concurrency,fail,stdev_rt,succ,throughput,perc_0.0,perc_50.0,perc_90.0,perc_95.0,perc_99.0,perc_99.9,perc_100.0,rc_200 -Setup squeezenet_v1.1 Model,0.03800,61.94600,61.94800,321,1,0,0.01200,1,1,61.92000,61.92000,61.92000,61.92000,61.92000,61.92000,61.92000,1 -,0.00069,1.03539,1.03545,18584,1,0,7.80150,62,62,0.00000,0.00100,0.02900,0.03200,61.92000,61.92000,61.92000,62 -Scale Up squeezenet_v1.1 Model,0.00007,0.03668,0.03672,17940,1,0,0.25260,60,60,0.00000,0.00100,0.01500,0.03000,1.97600,1.97600,1.97600,60 -Scale Down squeezenet_v1.1,0.00100,0.04700,0.04700,323,1,0,0.00000,1,1,0.04700,0.04700,0.04700,0.04700,0.04700,0.04700,0.04700,1 diff --git a/tests/performance/tmp/run345/upscale_workers/global_config.yaml b/tests/performance/tmp/run345/upscale_workers/global_config.yaml deleted file mode 100644 index 94731f4a3..000000000 --- a/tests/performance/tmp/run345/upscale_workers/global_config.yaml +++ /dev/null @@ -1,26 +0,0 @@ -modules: - jmeter: - # These are JMeter test case properties. These variables are used in jmx files. - # Change the vaues as per your setup - properties: - hostname : 127.0.0.1 # MMS properties - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg # make sure jpg is available at this path - # if relative path is provided this will be relative to current working directory - -# DO-NOT change properties below unless you know what you are doing. -# They are needed for performance test suite runner script. -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -settings: - env: - BASEDIR : '.' diff --git a/tests/performance/tmp/run345/upscale_workers/jmeter-bzt.properties b/tests/performance/tmp/run345/upscale_workers/jmeter-bzt.properties deleted file mode 100644 index 983b2ec11..000000000 --- a/tests/performance/tmp/run345/upscale_workers/jmeter-bzt.properties +++ /dev/null @@ -1,14 +0,0 @@ -jmeter.save.saveservice.autoflush=true -jmeter.save.saveservice.connect_time=true -jmeterengine.force.system.exit=true -summariser.name= -hostname=127.0.0.1 -port=8080 -management_port=8081 -protocol=http -input_filepath=kitten.jpg -jmeterengine.nongui.port=4445 -jmeterengine.nongui.maxport=4445 -jmeter.save.saveservice.timestamp_format=ms -sampleresult.default.encoding=UTF-8 -user.classpath=/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/upscale_workers:/Users/mahesh/git/multi-model-server/tests/performance/tests diff --git a/tests/performance/tmp/run345/upscale_workers/jmeter.err b/tests/performance/tmp/run345/upscale_workers/jmeter.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/performance/tmp/run345/upscale_workers/jmeter.out b/tests/performance/tmp/run345/upscale_workers/jmeter.out deleted file mode 100644 index 78824f690..000000000 --- a/tests/performance/tmp/run345/upscale_workers/jmeter.out +++ /dev/null @@ -1,5 +0,0 @@ -Created the tree successfully using /Users/mahesh/git/multi-model-server/tests/performance/tests/modified_upscale_workers.jmx -Starting standalone test @ Thu Jun 18 22:22:06 IST 2020 (1592499126765) -Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445 -Tidying up ... @ Thu Jun 18 22:23:12 IST 2020 (1592499192801) -... end of run diff --git a/tests/performance/tmp/run345/upscale_workers/kpi.jtl b/tests/performance/tmp/run345/upscale_workers/kpi.jtl deleted file mode 100644 index beb686e2e..000000000 --- a/tests/performance/tmp/run345/upscale_workers/kpi.jtl +++ /dev/null @@ -1,63 +0,0 @@ -timeStamp,elapsed,label,responseCode,responseMessage,threadName,success,bytes,grpThreads,allThreads,Latency,Hostname,Connect -1592499127312,61948,Setup squeezenet_v1.1 Model,200,OK,setup squeezenet_v1.1 1-1,true,321,1,1,61946,BRL06570.local,38 -1592499191015,2,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,2,BRL06570.local,1 -1592499191018,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191020,29,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,29,BRL06570.local,0 -1592499191050,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191052,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191054,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191056,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191058,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191060,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191062,2,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,2,BRL06570.local,0 -1592499191065,3,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,3,BRL06570.local,0 -1592499191069,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191071,29,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,29,BRL06570.local,0 -1592499191101,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191103,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191105,3,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,3,BRL06570.local,0 -1592499191109,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191111,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,1,BRL06570.local,0 -1592499191113,2,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,2,BRL06570.local,0 -1592499191115,2,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-2,true,299,2,2,2,BRL06570.local,0 -1592499189344,1976,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1976,BRL06570.local,1 -1592499191320,30,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,30,BRL06570.local,0 -1592499191350,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,1 -1592499191352,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191353,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191354,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191356,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191357,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191358,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191359,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191361,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191362,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191363,2,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191365,2,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191367,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191369,30,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,30,BRL06570.local,0 -1592499191400,0,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,0,BRL06570.local,0 -1592499191401,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191402,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499191403,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-1,true,299,1,1,1,BRL06570.local,0 -1592499192681,2,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,2,BRL06570.local,1 -1592499192683,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192685,15,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,15,BRL06570.local,0 -1592499192701,0,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,0,BRL06570.local,0 -1592499192702,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192703,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192704,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192705,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192706,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192707,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192708,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192709,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192710,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192712,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192714,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192715,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192716,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192717,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192718,32,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,32,BRL06570.local,0 -1592499192750,1,Scale Up squeezenet_v1.1 Model,200,OK,squeezenet_v1.1 Inference 1-3,true,299,1,1,1,BRL06570.local,0 -1592499192754,47,Scale Down squeezenet_v1.1,200,OK,tearDown squeezenet_v1.1 1-1,true,323,1,1,47,BRL06570.local,1 diff --git a/tests/performance/tmp/run345/upscale_workers/merged.json b/tests/performance/tmp/run345/upscale_workers/merged.json deleted file mode 100644 index d5480a978..000000000 --- a/tests/performance/tmp/run345/upscale_workers/merged.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "execution": [ - { - "concurrency": 3, - "ramp-up": "5s", - "iterations": 20, - "scenario": "Upscale" - } - ], - "scenarios": { - "Upscale": { - "script": "upscale_workers.jmx" - } - }, - "modules": { - "server_local_monitoring": { - "class": "metrics_monitoring_taurus.Monitor" - }, - "new_passfail": { - "class": "new_passfail.PassFailStatus" - }, - "jmeter": { - "properties": { - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - } - } - }, - "services": [ - { - "module": "server_local_monitoring", - "ServerLocalClient": [ - { - "interval": "1s", - "metrics": [ - "cpu", - "disk-space", - "mem", - "total_workers" - ] - } - ] - } - ], - "reporting": [ - { - "module": "passfail", - "criteria": [ - { - "class": "bzt.modules.monitoring.MonitoringCriteria", - "subject": "ServerLocalClient/total_workers", - "condition": ">", - "threshold": 4, - "timeframe": "1s", - "stop": true, - "fail": true - } - ] - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail" - }, - { - "module": "junit-xml", - "data-source": "sample-labels" - }, - { - "module": "final-stats", - "dump-csv": "${BASEDIR}/final_stats.csv" - } - ], - "settings": { - "env": { - "BASEDIR": "." - } - } -} diff --git a/tests/performance/tmp/run345/upscale_workers/merged.yml b/tests/performance/tmp/run345/upscale_workers/merged.yml deleted file mode 100644 index b00461b41..000000000 --- a/tests/performance/tmp/run345/upscale_workers/merged.yml +++ /dev/null @@ -1,51 +0,0 @@ ---- -execution: -- concurrency: 3 - iterations: 20 - ramp-up: 5s - scenario: Upscale -modules: - jmeter: - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - management_port: 8081 - port: 8080 - protocol: http - new_passfail: - class: new_passfail.PassFailStatus - server_local_monitoring: - class: metrics_monitoring_taurus.Monitor -reporting: -- criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - condition: '>' - fail: true - stop: true - subject: ServerLocalClient/total_workers - threshold: 4 - timeframe: 1s - module: passfail -- module: passfail -- data-source: pass-fail - module: junit-xml -- data-source: sample-labels - module: junit-xml -- dump-csv: ${BASEDIR}/final_stats.csv - module: final-stats -scenarios: - Upscale: - script: upscale_workers.jmx -services: -- ServerLocalClient: - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - total_workers - module: server_local_monitoring -settings: - env: - BASEDIR: . - diff --git a/tests/performance/tmp/run345/upscale_workers/modified_upscale_workers.jmx b/tests/performance/tmp/run345/upscale_workers/modified_upscale_workers.jmx deleted file mode 100644 index 87b5ea335..000000000 --- a/tests/performance/tmp/run345/upscale_workers/modified_upscale_workers.jmx +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - cnn_url - https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - = - The url from where to fetch noop model from - - - scale_up_workers - ${__P(min_workers,4)} - = - The workers to scale No op model to - - - scale_down_workers - 0 - Offload the No-Op Model - = - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - continuefalse2035false5 - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_up_workers}&synchronous=true - PUT - true - false - true - false - - - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_down_workers} - DELETE - true - false - true - false - - - - - - - /Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/upscale_workers/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/Users/mahesh/git/multi-model-server/tests/performance/tmp/run345/upscale_workers/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue - - diff --git a/tests/performance/tmp/run345/upscale_workers/upscale_workers.yaml b/tests/performance/tmp/run345/upscale_workers/upscale_workers.yaml deleted file mode 100644 index 862c939ed..000000000 --- a/tests/performance/tmp/run345/upscale_workers/upscale_workers.yaml +++ /dev/null @@ -1,40 +0,0 @@ ---- -execution: -- concurrency: 3 - ramp-up: 5s - iterations: 20 - scenario: Upscale - -scenarios: - Upscale: - script: upscale_workers.jmx - -modules: - server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. - new_passfail: - class: new_passfail.PassFailStatus - - - -services: - - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - total_workers - -reporting: -- module: passfail - criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers - condition: '>' - threshold: 4 - timeframe: 1s - stop : true - fail : true \ No newline at end of file diff --git a/tests/performance/tmp/run345/upscale_workers/xunit-1.xml b/tests/performance/tmp/run345/upscale_workers/xunit-1.xml deleted file mode 100644 index ff9ca478f..000000000 --- a/tests/performance/tmp/run345/upscale_workers/xunit-1.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/tests/performance/tmp/run345/upscale_workers/xunit.xml b/tests/performance/tmp/run345/upscale_workers/xunit.xml deleted file mode 100644 index 768e22ae6..000000000 --- a/tests/performance/tmp/run345/upscale_workers/xunit.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - From 1d744ac1060cd5a9c0779e09aaaa2197a757d282 Mon Sep 17 00:00:00 2001 From: Mahesh Ambule <15611578+maheshambule@users.noreply.github.com> Date: Fri, 19 Jun 2020 12:07:05 +0530 Subject: [PATCH 109/329] config.ini path changes --- tests/performance/agents/configuration.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/performance/agents/configuration.py b/tests/performance/agents/configuration.py index e5ebeb998..fc5d75864 100644 --- a/tests/performance/agents/configuration.py +++ b/tests/performance/agents/configuration.py @@ -15,9 +15,11 @@ """ # pylint: disable=redefined-builtin import configparser +import pathlib config = configparser.ConfigParser() -config.read('./config.ini') +path = pathlib.Path(__file__).parent.absolute() +config.read('{}/config.ini'.format(path)) def get(section, key, default=''): try: From 7116c6ff1eb8016638623e93bebb56c5863fe846 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 12:23:57 +0530 Subject: [PATCH 110/329] test on file descriptors, update concurency --- .../performance/tests/downscale_workers.yaml | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/tests/performance/tests/downscale_workers.yaml b/tests/performance/tests/downscale_workers.yaml index 2cc6ce5a3..d2aa7f853 100644 --- a/tests/performance/tests/downscale_workers.yaml +++ b/tests/performance/tests/downscale_workers.yaml @@ -1,8 +1,9 @@ --- execution: -- concurrency: 1 - ramp-up: 0s - iterations: 1 +- concurrency: 10 + ramp-up: 1s + hold-for: 30s +# iterations: 200 scenario: Downscale scenarios: @@ -20,6 +21,7 @@ services: - interval: 1s metrics: - total_workers + - sum_workers_file_descriptors modules: server_local_monitoring: @@ -30,10 +32,31 @@ modules: reporting: - module: passfail criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 4 + timeframe: 1s + stop : true + fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '<' threshold: 2 timeframe: 1s stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '>' + threshold: 38 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '<' + threshold: 21 + timeframe: 1s + stop : true fail : true \ No newline at end of file From bda046f0593facb9cb7eaa1015eeaf1ce0c3ac15 Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 12:29:42 +0530 Subject: [PATCH 111/329] refactor run performance suite, add orphan process metric Signed-off-by: Dhruva Ray --- tests/performance/agents/metrics/__init__.py | 7 +- tests/performance/agents/utils/process.py | 4 +- tests/performance/requirements.txt | 1 + tests/performance/run_performance_suite.py | 238 +++++++++---------- 4 files changed, 125 insertions(+), 125 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index 2c6c46c8a..d0a428d0d 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -78,11 +78,13 @@ class ProcessType(Enum): for op in list(operators): AVAILABLE_METRICS.append('{}_{}_{}'.format(op, PNAME, metric)) +__CHILDREN = set() def get_metrics(server_process, child_processes): """ Get Server processes specific metrics """ result = {} + __CHILDREN.update(child_processes) def update_metric(metric_name, proc_type, stats): stats = stats if stats else [0] @@ -119,8 +121,9 @@ def update_metric(metric_name, proc_type, stats): # Total processes - result['total_processes'] = len([server_process] + child_processes) - result['total_workers'] = len(child_processes) - 1 if len(child_processes) else 0 + result['total_processes'] = len(child_processes) + 1 + result['total_workers'] = max(len(child_processes) - 1, 0) + result['orphans'] = len(list(filter(lambda p : p['ppid'] == 1, worker_stats))) ### SYSTEM METRICS ### result['system_disk_used'] = psutil.disk_usage('/').used diff --git a/tests/performance/agents/utils/process.py b/tests/performance/agents/utils/process.py index 9032d46ca..e241e46f6 100644 --- a/tests/performance/agents/utils/process.py +++ b/tests/performance/agents/utils/process.py @@ -49,10 +49,10 @@ def get_process_pid_from_file(file_path): def get_child_processes(process): """Get all running child processes recursively""" - child_processes = [] + child_processes = set() for p in process.children(recursive=True): if p.status() == 'running': - child_processes.append(p) + child_processes.add(p) return child_processes diff --git a/tests/performance/requirements.txt b/tests/performance/requirements.txt index 2b216ad94..09806abc2 100644 --- a/tests/performance/requirements.txt +++ b/tests/performance/requirements.txt @@ -3,3 +3,4 @@ junitparser==1.4.1 git+https://github.com/maheshambule/vjunit.git#egg=vjunit tqdm==4.40.0 pathlib +click \ No newline at end of file diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 3c7787703..ee71be66d 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -15,7 +15,6 @@ """ # pylint: disable=redefined-builtin -import argparse import glob import logging import os @@ -24,6 +23,7 @@ import subprocess import sys import time +import click from subprocess import PIPE, STDOUT import requests @@ -32,9 +32,12 @@ from tqdm import tqdm logger = logging.getLogger(__name__) -code = 0 -metrics_monitoring_server = "agents/metrics_monitoring_server.py" +logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + +PATH = pathlib.Path(__file__).parent.absolute() +GLOBAL_CONFIG_PATH = "{}/tests/common/global_config.yaml".format(PATH) +code = 0 class Timer(object): def __init__(self, description): @@ -50,6 +53,87 @@ def __exit__(self, type, value, traceback): def diff(self): return int(time.time()) - self.start +class Monitoring(object): + def __init__(self, path, use = True): + self.path = "{}/{}".format(path, "agents/metrics_monitoring_server.py") + self.use = use + + def __enter__(self): + with open(GLOBAL_CONFIG_PATH) as conf_file: + global_config = yaml.safe_load(conf_file) + server_props = global_config["modules"]["jmeter"]["properties"] + server_ping_url = "{}://{}:{}/ping".format(server_props["protocol"], server_props["hostname"], + server_props["port"]) + try: + requests.get(server_ping_url) + except requests.exceptions.ConnectionError: + raise Exception("Server is not running. Pinged url {}. Exiting...".format(server_ping_url)) + + if self.use: + start_monitoring_server = "python {} --start".format(self.path) + run_process(start_monitoring_server, wait=False) + time.sleep(2) + return self + + def __exit__(self, type, value, traceback): + if self.use: + stop_monitoring_server = "python {} --stop".format(self.path) + run_process(stop_monitoring_server) + +class Suite(object): + def __init__(self, name, artifacts_dir, junit_xml, timer): + self.ts = TestSuite(name) + self.name = name + self.junit_xml = junit_xml + self.timer = timer + self.artifacts_dir = artifacts_dir + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + xunit_file = "{}/xunit.xml".format(self.artifacts_dir) + tests, failures, skipped, errors = 0, 0, 0, 0 + err_txt = "" + if os.path.exists(xunit_file): + xml = JUnitXml.fromfile(xunit_file) + for i, suite in enumerate(xml): # tqqdm + for case in suite: + name = "scenario_{}: {}".format(i, case.name) + result = case.result + if isinstance(result, Error): + errors += 1 + err_txt = self.err + elif isinstance(result, Failure): + failures += 1 + err_txt = self.err + elif isinstance(result, Skipped): + skipped += 1 + else: + tests += 1 + + tc = TestCase(name) + tc.result = result + self.ts.add_testcase(tc) + else: + tc = TestCase(self.name) + if code: + tc.result = Error("Suite run failed", "Error") + else: + tc.result = Skipped() + self.ts.add_testcase(tc) + + + self.ts.hostname = socket.gethostname() + self.ts.timestamp = self.timer.start + self.ts.time = self.timer.diff() + self.ts.tests = tests + self.ts.failures = failures + self.ts.skipped = skipped + self.ts.errors = errors + self.ts.update_statistics() + self.junit_xml.add_testsuite(self.ts) + def run_process(cmd, wait=True): print("running command : {}".format(cmd)) @@ -66,8 +150,6 @@ def run_process(cmd, wait=True): print(line) return p.returncode, '\n'.join(lines) - - else: p = subprocess.Popen(cmd, shell=True) return p.returncode, '' @@ -75,8 +157,7 @@ def run_process(cmd, wait=True): def get_test_yamls(dir_path=None, pattern="*.yaml"): if not dir_path: - path = pathlib.Path(__file__).parent.absolute() - dir_path = str(path) + "/tests" + dir_path = str(PATH) + "/tests" path_pattern = "{}/{}".format(dir_path, pattern) return glob.glob(path_pattern) @@ -93,123 +174,38 @@ def get_options(artifacts_dir, jmeter_path=None): return options_str - -def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monitoring_server): +@click.command() +@click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True), required=True) +@click.option('-t', '--test-dir', help='Directory containing tests.', type=click.Path(exists=True), default=None) +@click.option('-p', '--pattern', help='Test case file name pattern. Example --> *.yaml', default="*.yaml") +@click.option('-j', '--jmeter-path', help='JMeter executable path.') +@click.option('--monit/--no-monit', help='Start Monitoring server', default=True) +def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit): if os.path.exists(artifacts_dir): - raise Exception("Artifacts dir '{}' already exists. Provide different one.".format(artifacts_dir)) - - path = pathlib.Path(__file__).parent.absolute() - - global_config_file = "{}/tests/common/global_config.yaml".format(path) - with open(global_config_file) as conf_file: - global_config = yaml.safe_load(conf_file) - server_props = global_config["modules"]["jmeter"]["properties"] - server_ping_url = "{}://{}:{}/ping".format(server_props["protocol"], server_props["hostname"], server_props["port"]) - try: - requests.get(server_ping_url) - except requests.exceptions.ConnectionError: - raise Exception("Server is not running. Pinged url {}. Exiting..".format(server_ping_url)) - - if monitoring_server: - start_monitoring_server = "python {}/{} --start".format(path, metrics_monitoring_server) - code, output = run_process(start_monitoring_server, wait=False) - time.sleep(2) - - # TODO - Add check if server started - - junit_xml = JUnitXml() - pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(path)) - - test_yamls = get_test_yamls(test_dir, pattern) - for test_file in tqdm(test_yamls, desc="Test Suites"): - suite_name = os.path.basename(test_file).rsplit('.', 1)[0] - with Timer("Test suite {} execution time".format(suite_name)) as t: - suit_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) - options_str = get_options(suit_artifacts_dir, jmeter_path) - code, err = run_process("{} bzt {} {} {}".format(pre_command, options_str, - test_file, global_config_file)) - suite_time = t.diff() - suite_start = t.start - - # Assumes default file name - xunit_file = "{}/xunit.xml".format(suit_artifacts_dir) - tests, failures, skipped, errors = 0, 0, 0, 0 - err_txt = "" - ts = TestSuite(suite_name) - if os.path.exists(xunit_file): - xml = JUnitXml.fromfile(xunit_file) - for i, suite in enumerate(xml): #tqqdm - for case in suite: - name = "scenario_{}: {}".format(i, case.name) - result = case.result - if isinstance(result, Error): - errors += 1 - err_txt = err - elif isinstance(result, Failure): - failures += 1 - err_txt = err - elif isinstance(result, Skipped): - skipped += 1 - else: - tests +=1 - - tc = TestCase(name) - tc.result = result - # TODO Fix html report with system_err - # tc.system_err = err_txt[:-4] - ts.add_testcase(tc) - else: - tc = TestCase(suite_name) - if code: - tc.result = Error("Suite run failed", "Error") - # tc.system_err = err[:-4] - else: - tc.result = Skipped() - # tc.system_out = err[:-4] - ts.add_testcase(tc) - - ts.hostname = socket.gethostname() - ts.timestamp = suite_start - ts.time = suite_time - ts.tests = tests - ts.failures = failures - ts.skipped = skipped - ts.errors = errors - ts.update_statistics() - junit_xml.add_testsuite(ts) - - junit_xml.update_statistics() - junit_xml_path = '{}/junit.xml'.format(artifacts_dir) - junit_html_path = '{}/junit.html'.format(artifacts_dir) - junit_xml.write(junit_xml_path) - run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) - - if monitoring_server: - stop_monitoring_server = "python {}/{} --stop".format(path, metrics_monitoring_server) - run_process(stop_monitoring_server) + msg = "Artifacts dir '{}' already exists... Please provide a new directory.".format(artifacts_dir) + raise Exception(msg) + + with Monitoring(PATH, monit): + junit_xml = JUnitXml() + pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(PATH)) + test_yamls = get_test_yamls(test_dir, pattern) + for test_file in tqdm(test_yamls, desc="Test Suites"): + suite_name = os.path.basename(test_file).rsplit('.', 1)[0] + with Timer("Test suite {} execution time".format(suite_name)) as t: + suite_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) + options_str = get_options(suite_artifacts_dir, jmeter_path) + with Suite(suite_name, suite_artifacts_dir, junit_xml, t) as s: + s.code, s.err = run_process("{} bzt {} {} {}".format(pre_command, options_str, + test_file, GLOBAL_CONFIG_PATH)) + + junit_xml.update_statistics() + junit_xml_path = '{}/junit.xml'.format(artifacts_dir) + junit_html_path = '{}/junit.html'.format(artifacts_dir) + junit_xml.write(junit_xml_path) + run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: sys.exit(3) - if __name__ == "__main__": - logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) - parser = argparse.ArgumentParser(prog='run_performance_suite.py', description='Performance Test Suite Runner') - parser.add_argument('-a', '--artifacts-dir', nargs=1, type=str, dest='artifacts', required=True, - help='A artifacts directory') - - parser.add_argument('-d', '--test-dir', nargs=1, type=str, dest='test_dir', default=[None], - help='A test dir') - - parser.add_argument('-p', '--pattern', nargs=1, type=str, dest='pattern', default=["*.yaml"], - help='Test case file name pattern. example *.yaml') - - parser.add_argument('-j', '--jmeter-path', nargs=1, type=str, dest='jmeter_path', default=[None], - help='JMeter executable bin path') - - parser.add_argument('-m', '--monitoring-server', nargs=1, type=bool, dest='monitoring_server', default=[True], - help='Whether to start monitoring server') - - args = parser.parse_args() - run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0], args.jmeter_path[0], - args.monitoring_server[0]) + run_test_suite() \ No newline at end of file From e76eb645dcf4ba3f0474b08b702b903c50edeffd Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 12:40:47 +0530 Subject: [PATCH 112/329] Updated readme with new metrics Signed-off-by: Dhruva Ray --- tests/performance/README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 78a59df30..4d720a6df 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -28,11 +28,11 @@ Refer the [link](https://gettaurus.org/docs/Installation/) for more details on i ### B. Running the test suite 1. Run MMS server -2. Make sure parameters set in the [global_config.yaml](tests/common/global_config.yaml) are correct. +2. Make sure parameters set in the [tests/common/global_config.yaml](tests/common/global_config.yaml) are correct. 3. Run the test suite runner script 4. Check the console logs, $artifacts-dir$/junit.html report and other artifacts. - **steps are provided below** + **Steps are provided below** ```bash export MMS_HOME= cd $MMS_HOME/tests/performance @@ -41,9 +41,9 @@ Refer the [link](https://gettaurus.org/docs/Installation/) for more details on i # multi-model-server --start # check variables - #vi tests/common/global_config.yaml + # vi tests/common/global_config.yaml # jpeg download command for quick reference. Set input_filepath in global_config.yaml - #curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg + # curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg python -m run_performance_suite --artifacts-dir='' --pattern='*criteria*.yaml' ``` @@ -67,7 +67,7 @@ To add test case follow steps below. You can specify the test scenarios, in the scenario section of the yaml. To get you started quickly, we have provided a sample JMeter script and a Taurus yaml file [here](tests/register_and_inference.jmx) and [here](tests/call_jmx.yaml) . -Here is how the sample call_jmx.yaml looks like. Note variables used by jmx script are specified in [global_config.yaml](tests/common/global_config.yaml) file. +Here is how the sample call_jmx.yaml looks like. Note variables used by jmx script are specified in [tests/common/global_config.yaml](tests/common/global_config.yaml) file. ```yaml execution: @@ -246,7 +246,13 @@ bzt inference_taurus_local_monitoring_criteria.yaml tests/common/global_config.y | Syntax | Examples | | ------ | -------- | - | {aggreagate}\_all\_{metricname} | total_processes, sum_all_memory_percent, avg_all_iowait_time, min_all_io_write_bytes, max_all_threads | + | {aggregate}\_all\_{metricname} | sum_all_memory_percent, avg_all_iowait_time, min_all_io_write_bytes, max_all_threads | + + - Miscellaneous + * total_processes - Total number of processes spawned for frontend & workers + * total_workers - Total number of workers spawned + * orphans - Total number of orphan processes + ## Work in Progress 1. Add more metrics for cpu and gpu both. Add documentation around those. From 3c4c60a86d48cdc956010d8df76b675c7fa459e5 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 13:23:18 +0530 Subject: [PATCH 113/329] rename files --- tests/performance/tests/common/setup.jmx | 100 ------------------ tests/performance/tests/common/teardown.jmx | 75 ------------- ...ale_workers.jmx => scale_down_workers.jmx} | 0 ...e_workers.yaml => scale_down_workers.yaml} | 19 ++-- ...scale_workers.jmx => scale_up_workers.jmx} | 0 ...ale_workers.yaml => scale_up_workers.yaml} | 6 +- 6 files changed, 16 insertions(+), 184 deletions(-) delete mode 100644 tests/performance/tests/common/setup.jmx delete mode 100644 tests/performance/tests/common/teardown.jmx rename tests/performance/tests/{downscale_workers.jmx => scale_down_workers.jmx} (100%) rename tests/performance/tests/{downscale_workers.yaml => scale_down_workers.yaml} (70%) rename tests/performance/tests/{upscale_workers.jmx => scale_up_workers.jmx} (100%) rename tests/performance/tests/{upscale_workers.yaml => scale_up_workers.yaml} (92%) diff --git a/tests/performance/tests/common/setup.jmx b/tests/performance/tests/common/setup.jmx deleted file mode 100644 index fcddf3495..000000000 --- a/tests/performance/tests/common/setup.jmx +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - cnn_url - https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - = - The url from where to fetch noop model from - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=4 - PUT - true - false - true - false - - - - - - - - - diff --git a/tests/performance/tests/common/teardown.jmx b/tests/performance/tests/common/teardown.jmx deleted file mode 100644 index b769d3aae..000000000 --- a/tests/performance/tests/common/teardown.jmx +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model} - DELETE - true - false - true - false - - - - - - - - - diff --git a/tests/performance/tests/downscale_workers.jmx b/tests/performance/tests/scale_down_workers.jmx similarity index 100% rename from tests/performance/tests/downscale_workers.jmx rename to tests/performance/tests/scale_down_workers.jmx diff --git a/tests/performance/tests/downscale_workers.yaml b/tests/performance/tests/scale_down_workers.yaml similarity index 70% rename from tests/performance/tests/downscale_workers.yaml rename to tests/performance/tests/scale_down_workers.yaml index d2aa7f853..ca78dac5b 100644 --- a/tests/performance/tests/downscale_workers.yaml +++ b/tests/performance/tests/scale_down_workers.yaml @@ -3,19 +3,19 @@ execution: - concurrency: 10 ramp-up: 1s hold-for: 30s -# iterations: 200 - scenario: Downscale + scenario: scaledown scenarios: - Downscale: - script: downscale_workers.jmx + scaledown: + script: scale_down_workers.jmx services: - module: shellexec prepare: - - bzt tests/common/setup.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/ + - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true post-process: - - bzt tests/common/teardown.jmx tests/common/global_config.yaml -o settings.artifacts-dir=/tmp/mms_perf/ + - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 - module: server_local_monitoring ServerLocalClient: - interval: 1s @@ -59,4 +59,11 @@ reporting: threshold: 21 timeframe: 1s stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '<' + threshold: 1 + timeframe: 1s + stop : true fail : true \ No newline at end of file diff --git a/tests/performance/tests/upscale_workers.jmx b/tests/performance/tests/scale_up_workers.jmx similarity index 100% rename from tests/performance/tests/upscale_workers.jmx rename to tests/performance/tests/scale_up_workers.jmx diff --git a/tests/performance/tests/upscale_workers.yaml b/tests/performance/tests/scale_up_workers.yaml similarity index 92% rename from tests/performance/tests/upscale_workers.yaml rename to tests/performance/tests/scale_up_workers.yaml index 862c939ed..dfe47f8c6 100644 --- a/tests/performance/tests/upscale_workers.yaml +++ b/tests/performance/tests/scale_up_workers.yaml @@ -3,11 +3,11 @@ execution: - concurrency: 3 ramp-up: 5s iterations: 20 - scenario: Upscale + scenario: scaleup scenarios: - Upscale: - script: upscale_workers.jmx + scaleup: + script: scale_up_workers.jmx modules: server_local_monitoring: From 57bcc4e3cc1e3b14d6c71e2a5d9ea492e1dfc647 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 13:35:06 +0530 Subject: [PATCH 114/329] Refactor scale up\down scenario --- .../performance/tests/scale_down_workers.jmx | 6 +- .../performance/tests/scale_down_workers.yaml | 27 +++---- tests/performance/tests/scale_up_workers.jmx | 72 +------------------ tests/performance/tests/scale_up_workers.yaml | 51 +++++++++---- 4 files changed, 55 insertions(+), 101 deletions(-) diff --git a/tests/performance/tests/scale_down_workers.jmx b/tests/performance/tests/scale_down_workers.jmx index 609cc4832..0cd569261 100644 --- a/tests/performance/tests/scale_down_workers.jmx +++ b/tests/performance/tests/scale_down_workers.jmx @@ -42,7 +42,7 @@ - + continue false @@ -56,7 +56,7 @@ true - + @@ -64,7 +64,7 @@ ${__P(management_port,8444)} - /models/${model}?min_worker=${scale_down_workers} + /models/${model}?min_worker=${scale_down_workers}&synchronous=true PUT true false diff --git a/tests/performance/tests/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers.yaml index ca78dac5b..0e51cb28f 100644 --- a/tests/performance/tests/scale_down_workers.yaml +++ b/tests/performance/tests/scale_down_workers.yaml @@ -9,25 +9,26 @@ scenarios: scaledown: script: scale_down_workers.jmx +modules: + server_local_monitoring: + class : metrics_monitoring_taurus.Monitor # monitoring class. + new_passfail: + class: new_passfail.PassFailStatus + services: - module: shellexec prepare: - - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true + - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true post-process: - - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 - module: server_local_monitoring ServerLocalClient: - - interval: 1s - metrics: - - total_workers - - sum_workers_file_descriptors - -modules: - server_local_monitoring: - class : metrics_monitoring_taurus.Monitor # monitoring class. - new_passfail: - class: new_passfail.PassFailStatus + - interval: 1s + metrics: + - total_workers + - sum_workers_file_descriptors + - orphans reporting: - module: passfail diff --git a/tests/performance/tests/scale_up_workers.jmx b/tests/performance/tests/scale_up_workers.jmx index ba2c883ad..551380101 100644 --- a/tests/performance/tests/scale_up_workers.jmx +++ b/tests/performance/tests/scale_up_workers.jmx @@ -54,41 +54,7 @@ - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - + continue false @@ -102,7 +68,7 @@ true - + @@ -122,40 +88,6 @@ - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=${scale_down_workers} - DELETE - true - false - true - false - - - - - - diff --git a/tests/performance/tests/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers.yaml index dfe47f8c6..d3f7a8eff 100644 --- a/tests/performance/tests/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers.yaml @@ -1,8 +1,8 @@ --- execution: -- concurrency: 3 - ramp-up: 5s - iterations: 20 +- concurrency: 10 + ramp-up: 1s + hold-for: 30s scenario: scaleup scenarios: @@ -11,22 +11,22 @@ scenarios: modules: server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. + class : metrics_monitoring_taurus.Monitor new_passfail: class: new_passfail.PassFailStatus - - services: - - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - - interval: 1s - metrics: - - cpu - - disk-space - - mem - - total_workers + - module: shellexec + prepare: + - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + post-process: + - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + metrics: + - total_workers + - sum_workers_file_descriptors reporting: - module: passfail @@ -37,4 +37,25 @@ reporting: threshold: 4 timeframe: 1s stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '>' + threshold: 38 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '<' + threshold: 21 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '<' + threshold: 1 + timeframe: 1s + stop : true fail : true \ No newline at end of file From b56b41ca9c0fcee6a8cfaabbe0715d120fddeff0 Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 13:38:47 +0530 Subject: [PATCH 115/329] dump monitored metrics on console Signed-off-by: Dhruva Ray --- tests/performance/agents/metrics/__init__.py | 1 - tests/performance/agents/metrics_collector.py | 6 ++++++ tests/performance/agents/metrics_monitoring_taurus.py | 8 ++++++++ tests/performance/requirements.txt | 3 ++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index d0a428d0d..5e7113d37 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -16,7 +16,6 @@ import psutil - class ProcessType(Enum): """ Type of MMS processes to compute metrics on """ FRONTEND = 1 diff --git a/tests/performance/agents/metrics_collector.py b/tests/performance/agents/metrics_collector.py index 262021e6b..1b3d86b03 100755 --- a/tests/performance/agents/metrics_collector.py +++ b/tests/performance/agents/metrics_collector.py @@ -24,6 +24,7 @@ import gevent import psutil +from tabulate import tabulate logger = logging.getLogger(__name__) @@ -100,9 +101,14 @@ def monitor_processes(server_process, metrics, interval, socket): while True: message = [] collected_metrics = get_metrics(server_process, get_child_processes(server_process)) + table = {} for metric in metrics: message.append(str(collected_metrics.get(metric, 0))) + if collected_metrics.get(metric) is not None: + table[metric] = [collected_metrics.get(metric, 0)] + message = "\t".join(message)+"\n" + logger.info("\n{}".format(tabulate(table, headers=table.keys(), tablefmt="pretty"))) if socket: try: diff --git a/tests/performance/agents/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_taurus.py index 5f1ea429b..10746d41b 100644 --- a/tests/performance/agents/metrics_monitoring_taurus.py +++ b/tests/performance/agents/metrics_monitoring_taurus.py @@ -27,6 +27,7 @@ from metrics import get_metrics, AVAILABLE_METRICS as AVAILABLE_SERVER_METRICS from utils.process import get_process_pid_from_file, get_server_processes, \ get_child_processes, get_server_pidfile +from tabulate import tabulate PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 @@ -86,4 +87,11 @@ def _calc_resource_stats(self, interval): server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) server_process = get_server_processes(server_pid) result.update(get_metrics(server_process, get_child_processes(server_process))) + + table = {} + for key in self.metrics: + if result.get(key) is not None: + table[key] = [result[key]] + self.log.info("\n{0}".format(tabulate(table, headers=table.keys(), tablefmt="pretty"))) + return result diff --git a/tests/performance/requirements.txt b/tests/performance/requirements.txt index 09806abc2..d6b689c90 100644 --- a/tests/performance/requirements.txt +++ b/tests/performance/requirements.txt @@ -3,4 +3,5 @@ junitparser==1.4.1 git+https://github.com/maheshambule/vjunit.git#egg=vjunit tqdm==4.40.0 pathlib -click \ No newline at end of file +click +tabulate \ No newline at end of file From ac5dffa3775542bd1c3a51d7c78f90bd630872d6 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 14:12:13 +0530 Subject: [PATCH 116/329] Refactor scale up \ down scenario --- tests/performance/tests/scale_down_workers.jmx | 3 ++- tests/performance/tests/scale_up_workers.jmx | 17 +++-------------- tests/performance/tests/scale_up_workers.yaml | 12 ++++++------ 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/tests/performance/tests/scale_down_workers.jmx b/tests/performance/tests/scale_down_workers.jmx index 0cd569261..1abcd9eae 100644 --- a/tests/performance/tests/scale_down_workers.jmx +++ b/tests/performance/tests/scale_down_workers.jmx @@ -17,13 +17,14 @@ scale_down_workers 2 - Offload the No-Op Model + Number of workers to scale down to = model squeezenet_v1.1 = + Model name diff --git a/tests/performance/tests/scale_up_workers.jmx b/tests/performance/tests/scale_up_workers.jmx index 551380101..0a5ac2905 100644 --- a/tests/performance/tests/scale_up_workers.jmx +++ b/tests/performance/tests/scale_up_workers.jmx @@ -14,28 +14,17 @@ - - cnn_url - https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - = - The url from where to fetch noop model from - scale_up_workers - ${__P(min_workers,4)} - = - The workers to scale No op model to - - - scale_down_workers - 0 - Offload the No-Op Model + 4 = + Number of workers to scale up to model squeezenet_v1.1 = + Model name diff --git a/tests/performance/tests/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers.yaml index d3f7a8eff..5f633b583 100644 --- a/tests/performance/tests/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers.yaml @@ -40,16 +40,16 @@ reporting: fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors - condition: '>' - threshold: 38 - timeframe: 1s + condition: '<' + threshold: 4 + timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors - condition: '<' - threshold: 21 - timeframe: 1s + condition: '>' + threshold: 38 + timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria From b11b81ae1c63cb6f1cfa9b7fd1542b4c999ff68a Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 14:19:03 +0530 Subject: [PATCH 117/329] changes --- tests/performance/run_performance_suite.py | 62 ++++++++++++---------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index fc229862f..ad46eea27 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -24,7 +24,7 @@ import subprocess import yaml import requests -import configuration +from agents import configuration import csv import pandas as pd import boto3 @@ -40,9 +40,12 @@ logger = logging.getLogger(__name__) code = 0 metrics_monitoring_server = "agents/metrics_monitoring_server.py" +base_file_path = pathlib.Path(__file__).parent.absolute() +run_artifacts_path = "{}/run_artifacts/".format(base_file_path) S3_BUCKET = configuration.get('suite', 's3_bucket') + class Timer(object): def __init__(self, description): self.description = description @@ -180,8 +183,7 @@ def get_metrics_diff(dir1): for rep_section in test_yaml1['reporting']: if rep_section.get('module', None) == 'passfail' : for criterion in rep_section.get('criteria', []): - if isinstance(criterion, dict) and\ - 'monitoring' in criterion.get('class', ''): + if isinstance(criterion, dict) and 'monitoring' in criterion.get('class', ''): subject = criterion["subject"] metric = subject.rsplit('/',1) metric = metric[1] if len(metric) == 1 else metric[0] @@ -194,8 +196,6 @@ def get_metrics_diff(dir1): return metrics1, diff_percents1 metrics1, diff_percents1 = get_metrics_diff(dir1) - metrics2, diff_percents2 = get_metrics_diff(dir2) - metrics_file1 = glob.glob("{}/{}/SAlogs_*".format(dir1, sub_dir1)) metrics_file2 = glob.glob("{}/{}/SAlogs_*".format(dir2, sub_dir1)) @@ -208,21 +208,21 @@ def get_metrics_diff(dir1): rows.append([sub_dir1, "log_file", "NA", "NA", "NA", "pass"]) continue - metrics1 = pd.read_csv(metrics_file1[0]) - metrics2 = pd.read_csv(metrics_file2[0]) + metrics_from_file1 = pd.read_csv(metrics_file1[0]) + metrics_from_file2 = pd.read_csv(metrics_file2[0]) - for col in metrics1.columns[1:]: + for col, diff_percent in zip(metrics1.columns, diff_percents1): for agg_func in aggregates: name = "{}_{}".format(agg_func, str(col)) try: - val1 = getattr(metrics1[str(col)], agg_func)() + val1 = getattr(metrics_from_file1[str(col)], agg_func)() except TypeError: val1 = "NULL" print(col) - if str(col) in metrics2: + if str(col) in metrics_from_file2: try: - val2 = getattr(metrics2[str(col)], agg_func)() + val2 = getattr(metrics_from_file2[str(col)], agg_func)() except TypeError: val2 = "NULL" else: @@ -269,13 +269,23 @@ def get_metrics_diff(dir1): def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, - monitoring_server, env_name, diff_percent): + monitoring_server, env_name, diff_percent, compare_local): + + commit_id = subprocess.check_output('git rev-parse --short HEAD'.split()).decode("utf-8")[:-1] + artifacts_folder_name = "{}_{}_{}".format(env_name, commit_id, int(time.time())) + + store_local = False + if artifacts_dir is None: + artifacts_dir = "{}/{}".format(artifacts_folder_name, run_artifacts_path) + logger.info("Using artifacts dir as {}".format(artifacts_dir)) + store_local = True + if os.path.exists(artifacts_dir): raise Exception("Artifacts dir '{}' already exists. Provide different one.".format(artifacts_dir)) - path = pathlib.Path(__file__).parent.absolute() - global_config_file = "{}/tests/common/global_config.yaml".format(path) + + global_config_file = "{}/tests/common/global_config.yaml".format(base_file_path) with open(global_config_file) as conf_file: global_config = yaml.safe_load(conf_file) server_props = global_config["modules"]["jmeter"]["properties"] @@ -286,14 +296,14 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, raise Exception("Server is not running. Pinged url {}. Exiting..".format(server_ping_url)) if monitoring_server: - start_monitoring_server = "python {}/{} --start".format(path, metrics_monitoring_server) + start_monitoring_server = "python3 {}/{} --start".format(base_file_path, metrics_monitoring_server) code, output = run_process(start_monitoring_server, wait=False) time.sleep(2) # TODO - Add check if server started junit_xml = JUnitXml() - pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(path)) + pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(base_file_path)) test_yamls = get_test_yamls(test_dir, pattern) out_report_rows = [] @@ -364,7 +374,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if monitoring_server: - stop_monitoring_server = "python {}/{} --stop".format(path, metrics_monitoring_server) + stop_monitoring_server = "python3 {}/{} --stop".format(base_file_path, metrics_monitoring_server) run_process(stop_monitoring_server) with open('final_report.csv', 'w', newline='') as csvfile: @@ -372,22 +382,16 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, quotechar='|', quoting=csv.QUOTE_MINIMAL) csv_writer.writerows(out_report_rows) - path = pathlib.Path(artifacts_dir) - commit_id = subprocess.check_output('git rev-parse --short HEAD'.split()).decode("utf-8")[:-1] - new_name = "{}_{}_{}".format(env_name, commit_id, suite_start) - - # new_name_path = "{}/{}/".format(path.parent, new_name) - # os.rename(artifacts_dir, new_name_path) - - # latest_dir = get_latest_dir("{}/comp_data".format(artifacts_dir) , env_name) - # result = compare_artifacts(artifacts_dir, latest_dir, artifacts_dir, diff_percent=diff_percent) + if compare_local: + compare_dir = get_latest_dir(run_artifacts_path, env_name) + else: + compare_dir = download_s3_files(env_name, "{}/comp_data".format(artifacts_dir)) - compare_dir = download_s3_files(env_name, "{}/comp_data".format(artifacts_dir)) compare_result = True if compare_dir: compare_result = compare_artifacts(artifacts_dir, compare_dir, artifacts_dir, diff_percent=diff_percent) - run_process("aws s3 cp {} s3://{}/{} --recursive".format(artifacts_dir, S3_BUCKET, new_name)) + run_process("aws s3 cp {} s3://{}/{} --recursive".format(artifacts_dir, S3_BUCKET, artifacts_folder_name)) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: sys.exit(3) @@ -395,7 +399,9 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, if not compare_result: sys.exit(4) + if __name__ == "__main__": + exit() logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) parser = argparse.ArgumentParser(prog='run_performance_suite.py', description='Performance Test Suite Runner') parser.add_argument('-a', '--artifacts-dir', nargs=1, type=str, dest='artifacts', required=True, From 1fff38b7c5e47dcecf722e7a9678572279bc9809 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 14:25:52 +0530 Subject: [PATCH 118/329] refactor scale up \ down scenario --- tests/performance/tests/scale_down_workers.yaml | 2 +- tests/performance/tests/scale_up_workers.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/performance/tests/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers.yaml index 0e51cb28f..da6ab1954 100644 --- a/tests/performance/tests/scale_down_workers.yaml +++ b/tests/performance/tests/scale_down_workers.yaml @@ -11,7 +11,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor # monitoring class. + class : metrics_monitoring_taurus.Monitor new_passfail: class: new_passfail.PassFailStatus diff --git a/tests/performance/tests/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers.yaml index 5f633b583..c7e3d651d 100644 --- a/tests/performance/tests/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers.yaml @@ -27,6 +27,7 @@ services: metrics: - total_workers - sum_workers_file_descriptors + - orphans reporting: - module: passfail From 05bed01254857f30d4cc56e503e84de93cf42503 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 14:53:45 +0530 Subject: [PATCH 119/329] added orphans to AVAILABLE_METRICS --- tests/performance/agents/metrics/__init__.py | 10 +++++++--- tests/performance/tests/scale_down_workers.yaml | 4 ++-- tests/performance/tests/scale_up_workers.yaml | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index 5e7113d37..a802f099e 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -51,8 +51,6 @@ class ProcessType(Enum): } system_metrics = { - 'total_processes': None, - 'total_workers': None, 'system_disk_used': None, 'system_memory_percent': None, 'system_read_count': None, @@ -61,7 +59,13 @@ class ProcessType(Enum): 'system_write_bytes': None, } -AVAILABLE_METRICS = list(system_metrics) +misc_metrics = { + 'total_processes': None, + 'total_workers': None, + 'orphans': None +} + +AVAILABLE_METRICS = list(system_metrics) + list(misc_metrics) for metric in list(process_metrics): for ptype in list(ProcessType): diff --git a/tests/performance/tests/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers.yaml index da6ab1954..e243c741b 100644 --- a/tests/performance/tests/scale_down_workers.yaml +++ b/tests/performance/tests/scale_down_workers.yaml @@ -63,8 +63,8 @@ reporting: fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/orphans - condition: '<' - threshold: 1 + condition: '>' + threshold: 0 timeframe: 1s stop : true fail : true \ No newline at end of file diff --git a/tests/performance/tests/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers.yaml index c7e3d651d..58db36d29 100644 --- a/tests/performance/tests/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers.yaml @@ -55,8 +55,8 @@ reporting: fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/orphans - condition: '<' - threshold: 1 + condition: '>' + threshold: 0 timeframe: 1s stop : true fail : true \ No newline at end of file From 81c46183b5b0e8958107f28698b2e0ad73084d14 Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 15:11:10 +0530 Subject: [PATCH 120/329] add tests table Signed-off-by: Dhruva Ray --- tests/performance/TESTS.md | 27 +++++++++++++++++++ tests/performance/agents/metrics_collector.py | 9 ++++--- .../agents/metrics_monitoring_taurus.py | 2 ++ 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 tests/performance/TESTS.md diff --git a/tests/performance/TESTS.md b/tests/performance/TESTS.md new file mode 100644 index 000000000..fe8045c25 --- /dev/null +++ b/tests/performance/TESTS.md @@ -0,0 +1,27 @@ +|CODE|Test Types |Comments | +|----|------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| +|A |Operation time is deterministic for given inputs and env configuration |Model Inference Time = (Wait time for worker to be free) + (Time taken by worker to infer) | +|B |Minimum drift in user sla and system metrics over time |Current architecture doesnt support recycling of workers. Need to check whether params specified in sheet 2 remain within acceptable bounds| +|C |Demonstrate performance isolation across registered models |User uploaded handlers cannot cause denial of service (dos) for other model type inference | +|D |Operations should scale linearly on node |Time to scale N workers == ( Time to scale 1 worker ) * N | +|E |Demonstrate expected service concurrency commensurate with environment configuration|Setup environment in a way to minimize false positives/negatives | +|F |Demonstrate preservation of performance characteristics |Do not spawn additional workers, accept model registrations if they hamper system SLAs. Is this implemented currently? | +|G |Demonstrate cleanup releases system resources |Unregistering model should free up commensurate resources held by workers | +|H |Demonstrate that cleanup/termination of operations should be graceful |Scale down should wait for current inference operation to succeed. Is this current behavior? | +|I |Demonstrate that operations rollback in case request cannot be satisifed |Ongoing inference should complete before scaledown operation is allowed to start | +|J |Demonstrate that operations are idempotent |Multiple simultaneous scale operations with the same parameter value should result in the same system state | + +|API|CODE |YAML | +|---|------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| +|Register Model|A,B,F,J | | +|Inference|A,B,C | | +|Batch Inference|A,B,C | | +|Custom Model Handlers|C | | +|Scale Workers - UP/DOWN|D,G,I,F,J | | +|Unregister Models|D,G,I,J | | +|List Models|A,B,E | | +|Health Check|A,B,E | | +|API Description|A,B,E | | +|Model Describe|A,B,E | | +|List Models|A,B,E | | + diff --git a/tests/performance/agents/metrics_collector.py b/tests/performance/agents/metrics_collector.py index 1b3d86b03..845c606d9 100755 --- a/tests/performance/agents/metrics_collector.py +++ b/tests/performance/agents/metrics_collector.py @@ -101,14 +101,15 @@ def monitor_processes(server_process, metrics, interval, socket): while True: message = [] collected_metrics = get_metrics(server_process, get_child_processes(server_process)) - table = {} + #table = {} for metric in metrics: message.append(str(collected_metrics.get(metric, 0))) - if collected_metrics.get(metric) is not None: - table[metric] = [collected_metrics.get(metric, 0)] + #if collected_metrics.get(metric) is not None: + # table[metric] = [collected_metrics.get(metric, 0)] + message = "\t".join(message)+"\n" - logger.info("\n{}".format(tabulate(table, headers=table.keys(), tablefmt="pretty"))) + #logger.info("\n{}".format(tabulate(table, headers=table.keys(), tablefmt="pretty"))) if socket: try: diff --git a/tests/performance/agents/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_taurus.py index 10746d41b..12d426ebe 100644 --- a/tests/performance/agents/metrics_monitoring_taurus.py +++ b/tests/performance/agents/metrics_monitoring_taurus.py @@ -88,10 +88,12 @@ def _calc_resource_stats(self, interval): server_process = get_server_processes(server_pid) result.update(get_metrics(server_process, get_child_processes(server_process))) + ''' table = {} for key in self.metrics: if result.get(key) is not None: table[key] = [result[key]] self.log.info("\n{0}".format(tabulate(table, headers=table.keys(), tablefmt="pretty"))) + ''' return result From a7235df822b64dc1cbec816e2d8a8ba9ada530f3 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 15:28:23 +0530 Subject: [PATCH 121/329] compare changes --- tests/performance/run_performance_suite.py | 24 ++++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index ad46eea27..ace5f6e20 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -165,8 +165,8 @@ def compare_artifacts(dir1, dir2, out_dir, diff_percent=None): return False over_all_pass = True - sub_dirs_1 = list([x[0].rsplit('/',1)[1] for x in os.walk(dir1) if x[0] != dir1]) - sub_dirs_2 = list([x[0].rsplit('/',1)[1] for x in os.walk(dir2) if x[0] != dir2]) + sub_dirs_1 = list([x for x in os.listdir(dir1) if os.path.isdir(dir1+"/"+x) and x not in [dir1, 'comp_data']]) + sub_dirs_2 = list([x for x in os.listdir(dir2) if os.path.isdir(dir2+"/"+x) and x not in [dir2, 'comp_data']]) aggregates = ["mean", "max", "min"] header = ["test_suite", "metric", "run1", "run2", "percentage_diff", "result"] @@ -180,13 +180,13 @@ def get_metrics_diff(dir1): test_yaml1 = "{}/{}/{}.yaml".format(dir1, sub_dir1, sub_dir1) with open(test_yaml1) as test_yaml1: test_yaml1 = yaml.safe_load(test_yaml1) - for rep_section in test_yaml1['reporting']: + for rep_section in test_yaml1.get('reporting',[]): if rep_section.get('module', None) == 'passfail' : for criterion in rep_section.get('criteria', []): if isinstance(criterion, dict) and 'monitoring' in criterion.get('class', ''): subject = criterion["subject"] metric = subject.rsplit('/',1) - metric = metric[1] if len(metric) == 1 else metric[0] + metric = metric[1] if len(metric) == 2 else metric[0] diff_percent = criterion.get("diff_percent", None) if diff_percent: @@ -211,7 +211,7 @@ def get_metrics_diff(dir1): metrics_from_file1 = pd.read_csv(metrics_file1[0]) metrics_from_file2 = pd.read_csv(metrics_file2[0]) - for col, diff_percent in zip(metrics1.columns, diff_percents1): + for col, diff_percent in zip(metrics1, diff_percents1): for agg_func in aggregates: name = "{}_{}".format(agg_func, str(col)) try: @@ -219,6 +219,8 @@ def get_metrics_diff(dir1): except TypeError: val1 = "NULL" print(col) + except Exception as e: + print(e) if str(col) in metrics_from_file2: try: @@ -247,9 +249,11 @@ def get_metrics_diff(dir1): pass_fail = "pass" if diff < diff_percent else "fail" else: # special case of 0 pass_fail = "pass" + diff = 0 except Exception as e: logger.info("error while calculating the diff {}".format(str(e))) + diff = "NA" pass_fail = "fail" if over_all_pass: @@ -276,7 +280,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, store_local = False if artifacts_dir is None: - artifacts_dir = "{}/{}".format(artifacts_folder_name, run_artifacts_path) + artifacts_dir = "{}/{}".format(run_artifacts_path, artifacts_folder_name) logger.info("Using artifacts dir as {}".format(artifacts_dir)) store_local = True @@ -401,10 +405,9 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, if __name__ == "__main__": - exit() logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) parser = argparse.ArgumentParser(prog='run_performance_suite.py', description='Performance Test Suite Runner') - parser.add_argument('-a', '--artifacts-dir', nargs=1, type=str, dest='artifacts', required=True, + parser.add_argument('-a', '--artifacts-dir', nargs=1, type=str, dest='artifacts', required=False, default=[None], help='A artifacts directory') parser.add_argument('-e', '--env-name', nargs=1, type=bool, dest='env_name', default=[socket.gethostname()], @@ -422,9 +425,12 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, parser.add_argument('-m', '--monitoring-server', nargs=1, type=bool, dest='monitoring_server', default=[True], help='Whether to start monitoring server') + parser.add_argument('-l', '--compare-local', nargs=1, type=bool, dest='compare_local', default=[False], + help='Whether to do a comparison with local files otherwise s3 files are used') + parser.add_argument('-c', '--diff-percent', nargs=1, type=float, dest='diff_percent', default=[None], help='Acceptable percentage difference between metrics from previous runs') args = parser.parse_args() run_test_suite(args.artifacts[0], args.test_dir[0], args.pattern[0], args.jmeter_path[0], - args.monitoring_server[0], args.env_name[0], args.diff_percent[0]) + args.monitoring_server[0], args.env_name[0], args.diff_percent[0], args.compare_local[0]) From 3b58425c6929b7b1fe014ac9795ced926bced0e3 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 16:09:17 +0530 Subject: [PATCH 122/329] final changes --- tests/performance/requirements.txt | 2 ++ tests/performance/run_performance_suite.py | 7 +++---- .../tests/inference_server_monitoring_criteria.yaml | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/performance/requirements.txt b/tests/performance/requirements.txt index 2b216ad94..962054ac9 100644 --- a/tests/performance/requirements.txt +++ b/tests/performance/requirements.txt @@ -3,3 +3,5 @@ junitparser==1.4.1 git+https://github.com/maheshambule/vjunit.git#egg=vjunit tqdm==4.40.0 pathlib +boto +aws \ No newline at end of file diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index ace5f6e20..258b32269 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -169,7 +169,7 @@ def compare_artifacts(dir1, dir2, out_dir, diff_percent=None): sub_dirs_2 = list([x for x in os.listdir(dir2) if os.path.isdir(dir2+"/"+x) and x not in [dir2, 'comp_data']]) aggregates = ["mean", "max", "min"] - header = ["test_suite", "metric", "run1", "run2", "percentage_diff", "result"] + header = ["run_name1", "run_name2", "test_suite", "metric", "run1", "run2", "percentage_diff", "result"] rows = [header] for sub_dir1 in sub_dirs_1: if sub_dir1 in sub_dirs_2: @@ -287,8 +287,6 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, if os.path.exists(artifacts_dir): raise Exception("Artifacts dir '{}' already exists. Provide different one.".format(artifacts_dir)) - - global_config_file = "{}/tests/common/global_config.yaml".format(base_file_path) with open(global_config_file) as conf_file: global_config = yaml.safe_load(conf_file) @@ -395,7 +393,8 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, if compare_dir: compare_result = compare_artifacts(artifacts_dir, compare_dir, artifacts_dir, diff_percent=diff_percent) - run_process("aws s3 cp {} s3://{}/{} --recursive".format(artifacts_dir, S3_BUCKET, artifacts_folder_name)) + if not store_local: + run_process("aws s3 cp {} s3://{}/{} --recursive".format(artifacts_dir, S3_BUCKET, artifacts_folder_name)) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: sys.exit(3) diff --git a/tests/performance/tests/inference_server_monitoring_criteria.yaml b/tests/performance/tests/inference_server_monitoring_criteria.yaml index 46ea8fbeb..e0f1b4451 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_server_monitoring_criteria.yaml @@ -34,7 +34,6 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: mms-inference-server/sum_workers_file_descriptors condition: '>' - threshold: 80 timeframe: 1s fail: true stop: true From 659ea83fb8b76c4cbf087e6814a90417c7c44ecc Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 16:09:49 +0530 Subject: [PATCH 123/329] final changes --- tests/performance/agents/configuration.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/performance/agents/configuration.py b/tests/performance/agents/configuration.py index e5ebeb998..fc5d75864 100644 --- a/tests/performance/agents/configuration.py +++ b/tests/performance/agents/configuration.py @@ -15,9 +15,11 @@ """ # pylint: disable=redefined-builtin import configparser +import pathlib config = configparser.ConfigParser() -config.read('./config.ini') +path = pathlib.Path(__file__).parent.absolute() +config.read('{}/config.ini'.format(path)) def get(section, key, default=''): try: From 6c3f9f25e82ff339838b2f01f5aeae424aaf7c19 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 16:28:32 +0530 Subject: [PATCH 124/329] single \ multiple worker inference, remove new_passfail --- .../tests/inference_multiple_worker.jmx | 85 +++++++++++++++++++ .../tests/inference_multiple_worker.yaml | 69 +++++++++++++++ .../tests/inference_single_worker.jmx | 85 +++++++++++++++++++ .../tests/inference_single_worker.yaml | 69 +++++++++++++++ ...ence_taurus_local_monitoring_criteria.yaml | 4 - .../performance/tests/scale_down_workers.yaml | 2 - tests/performance/tests/scale_up_workers.yaml | 2 - 7 files changed, 308 insertions(+), 8 deletions(-) create mode 100644 tests/performance/tests/inference_multiple_worker.jmx create mode 100644 tests/performance/tests/inference_multiple_worker.yaml create mode 100644 tests/performance/tests/inference_single_worker.jmx create mode 100644 tests/performance/tests/inference_single_worker.yaml diff --git a/tests/performance/tests/inference_multiple_worker.jmx b/tests/performance/tests/inference_multiple_worker.jmx new file mode 100644 index 000000000..04bfce2e2 --- /dev/null +++ b/tests/performance/tests/inference_multiple_worker.jmx @@ -0,0 +1,85 @@ + + + + + + false + true + false + + + + + + + + + + model + squeezenet_v1.1 + = + Model Name + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + + diff --git a/tests/performance/tests/inference_multiple_worker.yaml b/tests/performance/tests/inference_multiple_worker.yaml new file mode 100644 index 000000000..84e1d8543 --- /dev/null +++ b/tests/performance/tests/inference_multiple_worker.yaml @@ -0,0 +1,69 @@ +--- +execution: +- concurrency: 10 + ramp-up: 1s + hold-for: 1m + iterations: 100 + scenario: inference + +scenarios: + inference: + script: inference_multiple_worker.jmx + +modules: + server_local_monitoring: + class : metrics_monitoring_taurus.Monitor + +services: + - module: shellexec + prepare: + - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true + post-process: + - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + metrics: + - total_workers + - sum_workers_file_descriptors + - orphans + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 4 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '<' + threshold: 4 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '>' + threshold: 38 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '<' + threshold: 38 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '>' + threshold: 0 + timeframe: 1s + stop : true + fail : true \ No newline at end of file diff --git a/tests/performance/tests/inference_single_worker.jmx b/tests/performance/tests/inference_single_worker.jmx new file mode 100644 index 000000000..04bfce2e2 --- /dev/null +++ b/tests/performance/tests/inference_single_worker.jmx @@ -0,0 +1,85 @@ + + + + + + false + true + false + + + + + + + + + + model + squeezenet_v1.1 + = + Model Name + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + + diff --git a/tests/performance/tests/inference_single_worker.yaml b/tests/performance/tests/inference_single_worker.yaml new file mode 100644 index 000000000..e6f27bb33 --- /dev/null +++ b/tests/performance/tests/inference_single_worker.yaml @@ -0,0 +1,69 @@ +--- +execution: +- concurrency: 10 + ramp-up: 1s + hold-for: 1m + iterations: 100 + scenario: inference + +scenarios: + inference: + script: inference_single_worker.jmx + +modules: + server_local_monitoring: + class : metrics_monitoring_taurus.Monitor + +services: + - module: shellexec + prepare: + - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true + post-process: + - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + metrics: + - total_workers + - sum_workers_file_descriptors + - orphans + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 1 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '<' + threshold: 1 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '>' + threshold: 11 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '<' + threshold: 11 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '>' + threshold: 0 + timeframe: 1s + stop : true + fail : true \ No newline at end of file diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml index c21a2cd18..a00ef2820 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml @@ -13,10 +13,6 @@ modules: server_local_monitoring: # metrics_monitoring_taurus and dependencies should be in python path class : metrics_monitoring_taurus.Monitor # monitoring class. - new_passfail: - class: new_passfail.PassFailStatus - - services: - module: server_local_monitoring # should be added in modules section diff --git a/tests/performance/tests/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers.yaml index e243c741b..beeb3ec88 100644 --- a/tests/performance/tests/scale_down_workers.yaml +++ b/tests/performance/tests/scale_down_workers.yaml @@ -12,8 +12,6 @@ scenarios: modules: server_local_monitoring: class : metrics_monitoring_taurus.Monitor - new_passfail: - class: new_passfail.PassFailStatus services: - module: shellexec diff --git a/tests/performance/tests/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers.yaml index 58db36d29..19d643320 100644 --- a/tests/performance/tests/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers.yaml @@ -12,8 +12,6 @@ scenarios: modules: server_local_monitoring: class : metrics_monitoring_taurus.Monitor - new_passfail: - class: new_passfail.PassFailStatus services: - module: shellexec From aca096331b3235e397d156244d3788ed7e01a2df Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 17:40:29 +0530 Subject: [PATCH 125/329] remove reclaimed pids Signed-off-by: Dhruva Ray --- tests/performance/agents/metrics/__init__.py | 42 +++++++++++--------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index a802f099e..282006e8c 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -81,13 +81,33 @@ class ProcessType(Enum): for op in list(operators): AVAILABLE_METRICS.append('{}_{}_{}'.format(op, PNAME, metric)) -__CHILDREN = set() +children = set() def get_metrics(server_process, child_processes): """ Get Server processes specific metrics """ result = {} - __CHILDREN.update(child_processes) + children.update(child_processes) + + # as_dict() gets all stats in one shot + processes_stats = [] + reclaimed_pids = [] + processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) + for process in children: + if len(list(filter(lambda p: p == server_process.pid, process.parents()))) == 0: + reclaimed_pids.append(process) + else: + processes_stats.append({'type': ProcessType.WORKER, 'stats' : process.as_dict()}) + + for p in reclaimed_pids: + children.remove(p) + + ### PROCESS METRICS ### + worker_stats = list(map(lambda x: x['stats'], \ + filter(lambda x: x['type'] == ProcessType.WORKER, processes_stats))) + server_stats = list(map(lambda x: x['stats'], \ + filter(lambda x: x['type'] == ProcessType.FRONTEND, processes_stats))) + all_stats = list(map(lambda x: x['stats'], processes_stats)) def update_metric(metric_name, proc_type, stats): stats = stats if stats else [0] @@ -104,28 +124,14 @@ def update_metric(metric_name, proc_type, stats): for op_name in operators: result['{}_{}_{}'.format(op_name, proc_name, metric_name)] = operators[op_name](stats) - # as_dict() gets all stats in one shot - processes_stats = [] - processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) - for process in child_processes: - processes_stats.append({'type': ProcessType.WORKER, 'stats' : process.as_dict()}) - - ### PROCESS METRICS ### - worker_stats = list(map(lambda x: x['stats'], \ - filter(lambda x: x['type'] == ProcessType.WORKER, processes_stats))) - server_stats = list(map(lambda x: x['stats'], \ - filter(lambda x: x['type'] == ProcessType.FRONTEND, processes_stats))) - all_stats = list(map(lambda x: x['stats'], processes_stats)) - for k in process_metrics: update_metric(k, ProcessType.WORKER, list(map(process_metrics[k], worker_stats))) update_metric(k, ProcessType.ALL, list(map(process_metrics[k], all_stats))) update_metric(k, ProcessType.FRONTEND, list(map(process_metrics[k], server_stats))) - # Total processes - result['total_processes'] = len(child_processes) + 1 - result['total_workers'] = max(len(child_processes) - 1, 0) + result['total_processes'] = len(worker_stats) + 1 + result['total_workers'] = max(len(worker_stats) - 1, 0) result['orphans'] = len(list(filter(lambda p : p['ppid'] == 1, worker_stats))) ### SYSTEM METRICS ### From 5643e8df9bf96b863965b813f3b12205e2f95b8f Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 18:07:35 +0530 Subject: [PATCH 126/329] updated file name in script --- .circleci/scripts/linux_test_perf_regression.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 7fb424ebd..460bcef57 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -24,7 +24,7 @@ pip install bzt # Execute performance test suite and store exit code curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg -./run_perfomance_suite.py --artifacts-dir=$ARTIFACTS_DIR --jmeter-path=$JMETER_PATH +./run_performance_suite.py --artifacts-dir=$ARTIFACTS_DIR --jmeter-path=$JMETER_PATH EXIT_CODE=$? # Stop server From 7629ad3f55cafba54c7fd94317eb4523c244f656 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 18:22:05 +0530 Subject: [PATCH 127/329] register unregister scenario --- .../performance/tests/register_unregister.jmx | 107 ++++++++++++++++++ .../tests/register_unregister.yaml | 48 ++++++++ 2 files changed, 155 insertions(+) create mode 100644 tests/performance/tests/register_unregister.jmx create mode 100644 tests/performance/tests/register_unregister.yaml diff --git a/tests/performance/tests/register_unregister.jmx b/tests/performance/tests/register_unregister.jmx new file mode 100644 index 000000000..d08cf1dc1 --- /dev/null +++ b/tests/performance/tests/register_unregister.jmx @@ -0,0 +1,107 @@ + + + + + + false + true + false + + + + + + + + + + model + squeezenet_v1.1 + = + Model name + + + model_url + https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + URL to model store on s3 + = + + + initial_workers + 1 + Number of initial workers to create + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${model_url}&initial_workers=${initial_workers}&synchronous=true + POST + true + false + true + false + + + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model} + DELETE + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/register_unregister.yaml b/tests/performance/tests/register_unregister.yaml new file mode 100644 index 000000000..3080cf875 --- /dev/null +++ b/tests/performance/tests/register_unregister.yaml @@ -0,0 +1,48 @@ +--- +execution: +- concurrency: 1 + ramp-up: 1s + iterations: 5 + scenario: scaledown + +scenarios: + scaledown: + script: register_unregister.jmx + +modules: + server_local_monitoring: + class : metrics_monitoring_taurus.Monitor + +services: + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + metrics: + - total_workers + - sum_workers_file_descriptors + - orphans + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 1 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '>' + threshold: 11 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '>' + threshold: 0 + timeframe: 1s + stop : true + fail : true \ No newline at end of file From 7bcacb3fd37f767462d8ca27a176eafb81a1c476 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 18:35:54 +0530 Subject: [PATCH 128/329] Updated TESTS md --- tests/performance/TESTS.md | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/tests/performance/TESTS.md b/tests/performance/TESTS.md index fe8045c25..851776d6f 100644 --- a/tests/performance/TESTS.md +++ b/tests/performance/TESTS.md @@ -11,17 +11,16 @@ |I |Demonstrate that operations rollback in case request cannot be satisifed |Ongoing inference should complete before scaledown operation is allowed to start | |J |Demonstrate that operations are idempotent |Multiple simultaneous scale operations with the same parameter value should result in the same system state | -|API|CODE |YAML | -|---|------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| -|Register Model|A,B,F,J | | -|Inference|A,B,C | | -|Batch Inference|A,B,C | | -|Custom Model Handlers|C | | -|Scale Workers - UP/DOWN|D,G,I,F,J | | -|Unregister Models|D,G,I,J | | -|List Models|A,B,E | | -|Health Check|A,B,E | | -|API Description|A,B,E | | -|Model Describe|A,B,E | | -|List Models|A,B,E | | - +|API|CODE|YAML| +|---|----|---| +|Register Model|A,B,F,J|[register_unregister.yaml](tests/register_unregister.yaml)| +|Inference|A,B,C|[inference_single_worker.yaml](tests/inference_single_worker.yaml), [inference_multiple_worker.yaml](tests/inference_multiple_worker.yaml)| +|Batch Inference|A,B,C|| +|Custom Model Handlers|C|| +|Scale Workers - UP/DOWN|D,G,I,F,J|[scale_up_workers.yaml](tests/scale_up_workers.yaml), [scale_down_workers.yaml](tests/scale_down_workers.yaml)| +|Unregister Models|D,G,I,J|[register_unregister.yaml](tests/register_unregister.yaml)| +|List Models|A,B,E|| +|Health Check|A,B,E|| +|API Description|A,B,E|| +|Model Describe|A,B,E|| +|List Models|A,B,E|| From 69ddfbcac011fbc2e6604305a8bc1f61ab3856d6 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 18:54:20 +0530 Subject: [PATCH 129/329] model description scenario --- tests/performance/TESTS.md | 3 +- tests/performance/tests/model_description.jmx | 76 +++++++++++++++++++ .../performance/tests/model_description.yaml | 68 +++++++++++++++++ 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 tests/performance/tests/model_description.jmx create mode 100644 tests/performance/tests/model_description.yaml diff --git a/tests/performance/TESTS.md b/tests/performance/TESTS.md index 851776d6f..2be0b66db 100644 --- a/tests/performance/TESTS.md +++ b/tests/performance/TESTS.md @@ -19,8 +19,7 @@ |Custom Model Handlers|C|| |Scale Workers - UP/DOWN|D,G,I,F,J|[scale_up_workers.yaml](tests/scale_up_workers.yaml), [scale_down_workers.yaml](tests/scale_down_workers.yaml)| |Unregister Models|D,G,I,J|[register_unregister.yaml](tests/register_unregister.yaml)| -|List Models|A,B,E|| |Health Check|A,B,E|| |API Description|A,B,E|| -|Model Describe|A,B,E|| +|Model Describe|A,B,E|[model_description.yaml](tests/model_description.yaml)| |List Models|A,B,E|| diff --git a/tests/performance/tests/model_description.jmx b/tests/performance/tests/model_description.jmx new file mode 100644 index 000000000..64524c383 --- /dev/null +++ b/tests/performance/tests/model_description.jmx @@ -0,0 +1,76 @@ + + + + + + false + true + false + + + + + + + + + + model + squeezenet_v1.1 + = + Model name + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model} + GET + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/model_description.yaml b/tests/performance/tests/model_description.yaml new file mode 100644 index 000000000..ff26a7268 --- /dev/null +++ b/tests/performance/tests/model_description.yaml @@ -0,0 +1,68 @@ +--- +execution: +- concurrency: 10 + ramp-up: 1s + hold-for: 30s + scenario: model_description + +scenarios: + model_description: + script: model_description.jmx + +modules: + server_local_monitoring: + class : metrics_monitoring_taurus.Monitor + +services: + - module: shellexec + prepare: + - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true + post-process: + - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + metrics: + - total_workers + - sum_workers_file_descriptors + - orphans + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 1 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '<' + threshold: 1 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '>' + threshold: 11 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '<' + threshold: 11 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '>' + threshold: 0 + timeframe: 1s + stop : true + fail : true \ No newline at end of file From e0776df50677fad499dcbe4adecb62c936ce3f94 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 18:58:08 +0530 Subject: [PATCH 130/329] compare runs --- tests/performance/run_performance_suite.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 9dce2abb4..3e7ede37a 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -92,6 +92,7 @@ def __exit__(self, type, value, traceback): stop_monitoring_server = "python {} --stop".format(self.path) run_process(stop_monitoring_server) + class Suite(object): def __init__(self, name, artifacts_dir, junit_xml, timer): self.ts = TestSuite(name) @@ -349,14 +350,14 @@ def get_options(artifacts_dir, jmeter_path=None): @click.command() -@click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True), required=True) +@click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True)) @click.option('-t', '--test-dir', help='Directory containing tests.', type=click.Path(exists=True), default=None) @click.option('-p', '--pattern', help='Test case file name pattern. Example --> *.yaml', default="*.yaml") @click.option('-j', '--jmeter-path', help='JMeter executable path.') @click.option('--monit/--no-monit', help='Start Monitoring server', default=True) @click.option('--env-name', help='Unique machine id on which MMS server is running', default=socket.gethostname()) @click.option('--compare-local/--no-compare-local', help='Compare with previous run with files stored' - ' in run_artifacts directory', default=False) + ' in run_artifacts directory', default=True) def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_name, compare_local): commit_id = subprocess.check_output('git rev-parse --short HEAD'.split()).decode("utf-8")[:-1] artifacts_folder_name = "{}_{}_{}".format(env_name, commit_id, int(time.time())) From 6b535b3f80d06fc7b3ce7c10776a35b596fd144f Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 19:05:36 +0530 Subject: [PATCH 131/329] remove files --- .../2020-06-18_22-01-33.340630/effective.json | 326 ------------------ .../2020-06-18_22-01-33.340630/effective.yml | 217 ------------ .../2020-06-18_22-01-33.340630/error.jtl | 4 - .../global_config.yaml | 26 -- .../jmeter-bzt.properties | 14 - .../2020-06-18_22-01-33.340630/jmeter.err | 0 .../2020-06-18_22-01-33.340630/jmeter.out | 5 - .../jmx_pwucwwli.json | 10 - .../2020-06-18_22-01-33.340630/kpi.jtl | 3 - .../2020-06-18_22-01-33.340630/merged.json | 43 --- .../2020-06-18_22-01-33.340630/merged.yml | 25 -- .../modified_setup.jmx | 100 ------ .../2020-06-18_22-01-33.340630/xunit-1.xml | 7 - .../2020-06-18_22-01-33.340630/xunit.xml | 4 - .../2020-06-18_22-03-04.522973/effective.json | 326 ------------------ .../2020-06-18_22-03-04.522973/effective.yml | 217 ------------ .../2020-06-18_22-03-04.522973/error.jtl | 4 - .../global_config.yaml | 26 -- .../jmeter-bzt.properties | 14 - .../2020-06-18_22-03-04.522973/jmeter.err | 0 .../2020-06-18_22-03-04.522973/jmeter.out | 5 - .../jmx_gf97bh11.json | 10 - .../2020-06-18_22-03-04.522973/kpi.jtl | 2 - .../2020-06-18_22-03-04.522973/merged.json | 43 --- .../2020-06-18_22-03-04.522973/merged.yml | 25 -- .../modified_teardown.jmx | 75 ---- .../2020-06-18_22-03-04.522973/xunit-1.xml | 6 - .../2020-06-18_22-03-04.522973/xunit.xml | 4 - tests/performance/nohup.out | 316 ----------------- 29 files changed, 1857 deletions(-) delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/effective.json delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/effective.yml delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/error.jtl delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/global_config.yaml delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/jmeter-bzt.properties delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/jmeter.err delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/jmeter.out delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/jmx_pwucwwli.json delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/kpi.jtl delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/merged.json delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/merged.yml delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/modified_setup.jmx delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/xunit-1.xml delete mode 100644 tests/performance/2020-06-18_22-01-33.340630/xunit.xml delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/effective.json delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/effective.yml delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/error.jtl delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/global_config.yaml delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/jmeter-bzt.properties delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/jmeter.err delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/jmeter.out delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/jmx_gf97bh11.json delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/kpi.jtl delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/merged.json delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/merged.yml delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/modified_teardown.jmx delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/xunit-1.xml delete mode 100644 tests/performance/2020-06-18_22-03-04.522973/xunit.xml delete mode 100644 tests/performance/nohup.out diff --git a/tests/performance/2020-06-18_22-01-33.340630/effective.json b/tests/performance/2020-06-18_22-01-33.340630/effective.json deleted file mode 100644 index 98cc47c46..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/effective.json +++ /dev/null @@ -1,326 +0,0 @@ -{ - "modules": { - "ab": { - "class": "bzt.modules.ab.ApacheBenchmarkExecutor" - }, - "gatling": { - "class": "bzt.modules.gatling.GatlingExecutor", - "properties": { - "gatling.data.file.bufferSize": 256 - } - }, - "grinder": { - "class": "bzt.modules.grinder.GrinderExecutor", - "properties": { - "grinder.useConsole": false - } - }, - "jmeter": { - "class": "bzt.modules.jmeter.JMeterExecutor", - "protocol-handlers": { - "http": "bzt.jmx.http.HTTPProtocolHandler" - }, - "plugins": [ - "jpgc-casutg", - "jpgc-dummy", - "jpgc-ffw", - "jpgc-fifo", - "jpgc-functions", - "jpgc-json", - "jpgc-perfmon", - "jpgc-prmctl", - "jpgc-tst" - ], - "properties": { - "jmeter.save.saveservice.autoflush": "true", - "jmeter.save.saveservice.connect_time": "true", - "jmeterengine.force.system.exit": "true", - "summariser.name": "", - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - }, - "version": "5.2.1", - "path": "/Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh" - }, - "locust": { - "class": "bzt.modules.locustio.LocustIOExecutor" - }, - "molotov": { - "class": "bzt.modules.molotov.MolotovExecutor" - }, - "pbench": { - "class": "bzt.modules.pbench.PBenchExecutor" - }, - "siege": { - "class": "bzt.modules.siege.SiegeExecutor" - }, - "tsung": { - "class": "bzt.modules.tsung.TsungExecutor" - }, - "selenium": { - "class": "bzt.modules.selenium.SeleniumExecutor" - }, - "junit": { - "class": "bzt.modules.java.JUnitTester" - }, - "testng": { - "class": "bzt.modules.java.TestNGTester" - }, - "rspec": { - "class": "bzt.modules.ruby.RSpecTester" - }, - "mocha": { - "class": "bzt.modules.javascript.MochaTester" - }, - "nunit": { - "class": "bzt.modules.csharp.NUnitExecutor" - }, - "pytest": { - "class": "bzt.modules.pytest.PyTestExecutor" - }, - "wdio": { - "class": "bzt.modules.javascript.WebdriverIOExecutor" - }, - "robot": { - "class": "bzt.modules.robot.RobotExecutor" - }, - "newman": { - "class": "bzt.modules.javascript.NewmanExecutor" - }, - "apiritif": { - "class": "bzt.modules.apiritif.ApiritifNoseExecutor" - }, - "local": { - "class": "bzt.modules.provisioning.Local" - }, - "monitoring": { - "class": "bzt.modules.monitoring.Monitoring" - }, - "passfail": { - "class": "bzt.modules.passfail.PassFailStatus" - }, - "shellexec": { - "class": "bzt.modules.shellexec.ShellExecutor" - }, - "junit-xml": { - "class": "bzt.modules.reporting.JUnitXMLReporter" - }, - "install-checker": { - "class": "bzt.modules.services.InstallChecker" - }, - "console": { - "class": "bzt.modules.console.ConsoleStatusReporter" - }, - "consolidator": { - "class": "bzt.modules.aggregator.ConsolidatingAggregator", - "percentiles": [ - 0.0, - 50.0, - 90.0, - 95.0, - 99.0, - 99.9, - 100.0 - ] - }, - "final-stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "functional-consolidator": { - "class": "bzt.modules.functional.FunctionalAggregator" - }, - "android-emulator": { - "class": "bzt.modules.services.AndroidEmulatorLoader" - }, - "appium": { - "class": "bzt.modules.services.AppiumLoader" - }, - "virtual-display": { - "class": "bzt.modules.services.VirtualDisplay" - }, - "external-results-loader": { - "class": "bzt.modules.external.ExternalResultsLoader" - }, - "blazemeter": { - "class": "bzt.modules.blazemeter.BlazeMeterUploader" - }, - "cloud": { - "class": "bzt.modules.blazemeter.CloudProvisioning", - "default-location": "us-central1-a" - }, - "proxy2jmx": { - "class": "bzt.modules.proxy2jmx.Proxy2JMX" - }, - "unpacker": { - "class": "bzt.modules.services.Unpacker" - }, - "screenshoter": { - "class": "bzt.modules.blazemeter.ServiceStubScreenshoter" - }, - "capturehar": { - "class": "bzt.modules.blazemeter.ServiceStubCaptureHAR" - }, - "final_stats": { - "class": "bzt.modules.reporting.FinalStatus" - } - }, - "cli-aliases": { - "cloud": { - "provisioning": "cloud" - }, - "detach": { - "modules": { - "cloud": { - "detach": true - } - } - }, - "func": { - "modules": { - "console": { - "disable": true - } - }, - "settings": { - "aggregator": "functional-consolidator" - } - }, - "gui": { - "modules": { - "jmeter": { - "gui": true - } - } - }, - "install-tools": { - "services": [ - "install-checker" - ] - }, - "local": { - "provisioning": "local" - }, - "locations": { - "modules": { - "cloud": { - "dump-locations": true - } - }, - "provisioning": "cloud" - }, - "report": { - "reporting": [ - "blazemeter" - ] - }, - "public": { - "modules": { - "blazemeter": { - "public-report": true - } - } - }, - "sequential": { - "modules": { - "local": { - "sequential": true - } - } - }, - "lint": { - "cli": { - "linter": { - "lint-and-exit": true - } - } - }, - "nolint": { - "cli": { - "linter": { - "disable": true - } - } - } - }, - "cli": { - "linter": { - "disable": false, - "warn-on-unfamiliar-fields": false, - "ignored-warnings": [], - "lint-and-exit": false - } - }, - "settings": { - "aggregator": "consolidator", - "default-executor": "jmeter", - "env": { - "OBJC_DISABLE_INITIALIZE_FORK_SAFETY": "YES", - "BASEDIR": ".", - "TAURUS_ARTIFACTS_DIR": "/Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-01-33.340630" - } - }, - "provisioning": "local", - "reporting": [ - { - "module": "final-stats" - }, - { - "module": "console" - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-01-33.340630/xunit.xml" - }, - { - "module": "junit-xml", - "data-source": "sample-labels", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-01-33.340630/xunit-1.xml" - }, - { - "module": "final-stats", - "dump-csv": "./final_stats.csv" - } - ], - "services": [ - { - "module": "monitoring", - "local": [ - { - "metrics": [ - "cpu", - "mem", - "bytes-sent", - "bytes-recv", - "disk-read", - "disk-write", - "disk-space", - "engine-loop", - "conn-all" - ] - } - ] - } - ], - "execution": [ - { - "executor": "jmeter", - "scenario": "setup.jmx", - "throughput": {}, - "concurrency": {} - } - ], - "included-configs": [], - "version": "1.14.2", - "scenarios": { - "setup.jmx": { - "script": "/Users/mahesh/git/multi-model-server/tests/performance/tests/common/setup.jmx" - } - } -} diff --git a/tests/performance/2020-06-18_22-01-33.340630/effective.yml b/tests/performance/2020-06-18_22-01-33.340630/effective.yml deleted file mode 100644 index ed4c94a62..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/effective.yml +++ /dev/null @@ -1,217 +0,0 @@ ---- -cli: - linter: - disable: false - ignored-warnings: [] - lint-and-exit: false - warn-on-unfamiliar-fields: false -cli-aliases: - cloud: - provisioning: cloud - detach: - modules: - cloud: - detach: true - func: - modules: - console: - disable: true - settings: - aggregator: functional-consolidator - gui: - modules: - jmeter: - gui: true - install-tools: - services: - - install-checker - lint: - cli: - linter: - lint-and-exit: true - local: - provisioning: local - locations: - modules: - cloud: - dump-locations: true - provisioning: cloud - nolint: - cli: - linter: - disable: true - public: - modules: - blazemeter: - public-report: true - report: - reporting: - - blazemeter - sequential: - modules: - local: - sequential: true -execution: -- concurrency: {} - executor: jmeter - scenario: setup.jmx - throughput: {} -included-configs: [] -modules: - ab: - class: bzt.modules.ab.ApacheBenchmarkExecutor - android-emulator: - class: bzt.modules.services.AndroidEmulatorLoader - apiritif: - class: bzt.modules.apiritif.ApiritifNoseExecutor - appium: - class: bzt.modules.services.AppiumLoader - blazemeter: - class: bzt.modules.blazemeter.BlazeMeterUploader - capturehar: - class: bzt.modules.blazemeter.ServiceStubCaptureHAR - cloud: - class: bzt.modules.blazemeter.CloudProvisioning - default-location: us-central1-a - console: - class: bzt.modules.console.ConsoleStatusReporter - consolidator: - class: bzt.modules.aggregator.ConsolidatingAggregator - percentiles: - - 0.0 - - 50.0 - - 90.0 - - 95.0 - - 99.0 - - 99.9 - - 100.0 - external-results-loader: - class: bzt.modules.external.ExternalResultsLoader - final-stats: - class: bzt.modules.reporting.FinalStatus - final_stats: - class: bzt.modules.reporting.FinalStatus - functional-consolidator: - class: bzt.modules.functional.FunctionalAggregator - gatling: - class: bzt.modules.gatling.GatlingExecutor - properties: - gatling.data.file.bufferSize: 256 - grinder: - class: bzt.modules.grinder.GrinderExecutor - properties: - grinder.useConsole: false - install-checker: - class: bzt.modules.services.InstallChecker - jmeter: - class: bzt.modules.jmeter.JMeterExecutor - path: /Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh - plugins: - - jpgc-casutg - - jpgc-dummy - - jpgc-ffw - - jpgc-fifo - - jpgc-functions - - jpgc-json - - jpgc-perfmon - - jpgc-prmctl - - jpgc-tst - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - jmeter.save.saveservice.autoflush: 'true' - jmeter.save.saveservice.connect_time: 'true' - jmeterengine.force.system.exit: 'true' - management_port: 8081 - port: 8080 - protocol: http - summariser.name: '' - protocol-handlers: - http: bzt.jmx.http.HTTPProtocolHandler - version: 5.2.1 - junit: - class: bzt.modules.java.JUnitTester - junit-xml: - class: bzt.modules.reporting.JUnitXMLReporter - local: - class: bzt.modules.provisioning.Local - locust: - class: bzt.modules.locustio.LocustIOExecutor - mocha: - class: bzt.modules.javascript.MochaTester - molotov: - class: bzt.modules.molotov.MolotovExecutor - monitoring: - class: bzt.modules.monitoring.Monitoring - newman: - class: bzt.modules.javascript.NewmanExecutor - nunit: - class: bzt.modules.csharp.NUnitExecutor - passfail: - class: bzt.modules.passfail.PassFailStatus - pbench: - class: bzt.modules.pbench.PBenchExecutor - proxy2jmx: - class: bzt.modules.proxy2jmx.Proxy2JMX - pytest: - class: bzt.modules.pytest.PyTestExecutor - robot: - class: bzt.modules.robot.RobotExecutor - rspec: - class: bzt.modules.ruby.RSpecTester - screenshoter: - class: bzt.modules.blazemeter.ServiceStubScreenshoter - selenium: - class: bzt.modules.selenium.SeleniumExecutor - shellexec: - class: bzt.modules.shellexec.ShellExecutor - siege: - class: bzt.modules.siege.SiegeExecutor - testng: - class: bzt.modules.java.TestNGTester - tsung: - class: bzt.modules.tsung.TsungExecutor - unpacker: - class: bzt.modules.services.Unpacker - virtual-display: - class: bzt.modules.services.VirtualDisplay - wdio: - class: bzt.modules.javascript.WebdriverIOExecutor -provisioning: local -reporting: -- module: final-stats -- module: console -- module: passfail -- data-source: pass-fail - filename: /Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-01-33.340630/xunit.xml - module: junit-xml -- data-source: sample-labels - filename: /Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-01-33.340630/xunit-1.xml - module: junit-xml -- dump-csv: ./final_stats.csv - module: final-stats -scenarios: - setup.jmx: - script: /Users/mahesh/git/multi-model-server/tests/performance/tests/common/setup.jmx -services: -- local: - - metrics: - - cpu - - mem - - bytes-sent - - bytes-recv - - disk-read - - disk-write - - disk-space - - engine-loop - - conn-all - module: monitoring -settings: - aggregator: consolidator - default-executor: jmeter - env: - BASEDIR: . - OBJC_DISABLE_INITIALIZE_FORK_SAFETY: 'YES' - TAURUS_ARTIFACTS_DIR: /Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-01-33.340630 -version: 1.14.2 - diff --git a/tests/performance/2020-06-18_22-01-33.340630/error.jtl b/tests/performance/2020-06-18_22-01-33.340630/error.jtl deleted file mode 100644 index 32bb536a8..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/error.jtl +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/2020-06-18_22-01-33.340630/global_config.yaml b/tests/performance/2020-06-18_22-01-33.340630/global_config.yaml deleted file mode 100644 index 94731f4a3..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/global_config.yaml +++ /dev/null @@ -1,26 +0,0 @@ -modules: - jmeter: - # These are JMeter test case properties. These variables are used in jmx files. - # Change the vaues as per your setup - properties: - hostname : 127.0.0.1 # MMS properties - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg # make sure jpg is available at this path - # if relative path is provided this will be relative to current working directory - -# DO-NOT change properties below unless you know what you are doing. -# They are needed for performance test suite runner script. -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -settings: - env: - BASEDIR : '.' diff --git a/tests/performance/2020-06-18_22-01-33.340630/jmeter-bzt.properties b/tests/performance/2020-06-18_22-01-33.340630/jmeter-bzt.properties deleted file mode 100644 index d493f9de0..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/jmeter-bzt.properties +++ /dev/null @@ -1,14 +0,0 @@ -jmeter.save.saveservice.autoflush=true -jmeter.save.saveservice.connect_time=true -jmeterengine.force.system.exit=true -summariser.name= -hostname=127.0.0.1 -port=8080 -management_port=8081 -protocol=http -input_filepath=kitten.jpg -jmeterengine.nongui.port=4445 -jmeterengine.nongui.maxport=4445 -jmeter.save.saveservice.timestamp_format=ms -sampleresult.default.encoding=UTF-8 -user.classpath=/Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-01-33.340630:/Users/mahesh/git/multi-model-server/tests/performance/tests/common diff --git a/tests/performance/2020-06-18_22-01-33.340630/jmeter.err b/tests/performance/2020-06-18_22-01-33.340630/jmeter.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/performance/2020-06-18_22-01-33.340630/jmeter.out b/tests/performance/2020-06-18_22-01-33.340630/jmeter.out deleted file mode 100644 index 5141043b2..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/jmeter.out +++ /dev/null @@ -1,5 +0,0 @@ -Created the tree successfully using /Users/mahesh/git/multi-model-server/tests/performance/tests/common/modified_setup.jmx -Starting standalone test @ Thu Jun 18 22:01:37 IST 2020 (1592497897801) -Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445 -Tidying up ... @ Thu Jun 18 22:02:56 IST 2020 (1592497976438) -... end of run diff --git a/tests/performance/2020-06-18_22-01-33.340630/jmx_pwucwwli.json b/tests/performance/2020-06-18_22-01-33.340630/jmx_pwucwwli.json deleted file mode 100644 index 98feddad3..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/jmx_pwucwwli.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "execution": [ - { - "executor": "jmeter", - "scenario": { - "script": "tests/common/setup.jmx" - } - } - ] -} diff --git a/tests/performance/2020-06-18_22-01-33.340630/kpi.jtl b/tests/performance/2020-06-18_22-01-33.340630/kpi.jtl deleted file mode 100644 index f8ee627a4..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/kpi.jtl +++ /dev/null @@ -1,3 +0,0 @@ -timeStamp,elapsed,label,responseCode,responseMessage,threadName,success,bytes,grpThreads,allThreads,Latency,Hostname,Connect -1592497898337,78092,Register squeezenet_v1.1 Model,200,OK,Register and Scale Up squeezenet_v1.1 Workers 1-1,true,321,1,1,78090,BRL06570.local,33 -1592497976431,4,Scale Up squeezenet_v1.1 Model,202,Accepted,Register and Scale Up squeezenet_v1.1 Workers 1-1,true,319,1,1,4,BRL06570.local,2 diff --git a/tests/performance/2020-06-18_22-01-33.340630/merged.json b/tests/performance/2020-06-18_22-01-33.340630/merged.json deleted file mode 100644 index 344ee74eb..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/merged.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "modules": { - "jmeter": { - "properties": { - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - } - } - }, - "reporting": [ - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail" - }, - { - "module": "junit-xml", - "data-source": "sample-labels" - }, - { - "module": "final-stats", - "dump-csv": "${BASEDIR}/final_stats.csv" - } - ], - "settings": { - "env": { - "BASEDIR": "." - } - }, - "execution": [ - { - "executor": "jmeter", - "scenario": { - "script": "tests/common/setup.jmx" - } - } - ] -} diff --git a/tests/performance/2020-06-18_22-01-33.340630/merged.yml b/tests/performance/2020-06-18_22-01-33.340630/merged.yml deleted file mode 100644 index bbb90f6a4..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/merged.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -execution: -- executor: jmeter - scenario: - script: tests/common/setup.jmx -modules: - jmeter: - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - management_port: 8081 - port: 8080 - protocol: http -reporting: -- module: passfail -- data-source: pass-fail - module: junit-xml -- data-source: sample-labels - module: junit-xml -- dump-csv: ${BASEDIR}/final_stats.csv - module: final-stats -settings: - env: - BASEDIR: . - diff --git a/tests/performance/2020-06-18_22-01-33.340630/modified_setup.jmx b/tests/performance/2020-06-18_22-01-33.340630/modified_setup.jmx deleted file mode 100644 index d885d8f43..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/modified_setup.jmx +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - cnn_url - https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - = - The url from where to fetch noop model from - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models?url=${cnn_url} - POST - true - false - true - false - - - - - - - - - - - ${__P(management_port,8444)} - - - /models/${model}?min_worker=4 - PUT - true - false - true - false - - - - - - - /Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-01-33.340630/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-01-33.340630/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue - - diff --git a/tests/performance/2020-06-18_22-01-33.340630/xunit-1.xml b/tests/performance/2020-06-18_22-01-33.340630/xunit-1.xml deleted file mode 100644 index b89dfa75c..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/xunit-1.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/tests/performance/2020-06-18_22-01-33.340630/xunit.xml b/tests/performance/2020-06-18_22-01-33.340630/xunit.xml deleted file mode 100644 index 8bdd58dbc..000000000 --- a/tests/performance/2020-06-18_22-01-33.340630/xunit.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/2020-06-18_22-03-04.522973/effective.json b/tests/performance/2020-06-18_22-03-04.522973/effective.json deleted file mode 100644 index 514b9efbd..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/effective.json +++ /dev/null @@ -1,326 +0,0 @@ -{ - "modules": { - "ab": { - "class": "bzt.modules.ab.ApacheBenchmarkExecutor" - }, - "gatling": { - "class": "bzt.modules.gatling.GatlingExecutor", - "properties": { - "gatling.data.file.bufferSize": 256 - } - }, - "grinder": { - "class": "bzt.modules.grinder.GrinderExecutor", - "properties": { - "grinder.useConsole": false - } - }, - "jmeter": { - "class": "bzt.modules.jmeter.JMeterExecutor", - "protocol-handlers": { - "http": "bzt.jmx.http.HTTPProtocolHandler" - }, - "plugins": [ - "jpgc-casutg", - "jpgc-dummy", - "jpgc-ffw", - "jpgc-fifo", - "jpgc-functions", - "jpgc-json", - "jpgc-perfmon", - "jpgc-prmctl", - "jpgc-tst" - ], - "properties": { - "jmeter.save.saveservice.autoflush": "true", - "jmeter.save.saveservice.connect_time": "true", - "jmeterengine.force.system.exit": "true", - "summariser.name": "", - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - }, - "version": "5.2.1", - "path": "/Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh" - }, - "locust": { - "class": "bzt.modules.locustio.LocustIOExecutor" - }, - "molotov": { - "class": "bzt.modules.molotov.MolotovExecutor" - }, - "pbench": { - "class": "bzt.modules.pbench.PBenchExecutor" - }, - "siege": { - "class": "bzt.modules.siege.SiegeExecutor" - }, - "tsung": { - "class": "bzt.modules.tsung.TsungExecutor" - }, - "selenium": { - "class": "bzt.modules.selenium.SeleniumExecutor" - }, - "junit": { - "class": "bzt.modules.java.JUnitTester" - }, - "testng": { - "class": "bzt.modules.java.TestNGTester" - }, - "rspec": { - "class": "bzt.modules.ruby.RSpecTester" - }, - "mocha": { - "class": "bzt.modules.javascript.MochaTester" - }, - "nunit": { - "class": "bzt.modules.csharp.NUnitExecutor" - }, - "pytest": { - "class": "bzt.modules.pytest.PyTestExecutor" - }, - "wdio": { - "class": "bzt.modules.javascript.WebdriverIOExecutor" - }, - "robot": { - "class": "bzt.modules.robot.RobotExecutor" - }, - "newman": { - "class": "bzt.modules.javascript.NewmanExecutor" - }, - "apiritif": { - "class": "bzt.modules.apiritif.ApiritifNoseExecutor" - }, - "local": { - "class": "bzt.modules.provisioning.Local" - }, - "monitoring": { - "class": "bzt.modules.monitoring.Monitoring" - }, - "passfail": { - "class": "bzt.modules.passfail.PassFailStatus" - }, - "shellexec": { - "class": "bzt.modules.shellexec.ShellExecutor" - }, - "junit-xml": { - "class": "bzt.modules.reporting.JUnitXMLReporter" - }, - "install-checker": { - "class": "bzt.modules.services.InstallChecker" - }, - "console": { - "class": "bzt.modules.console.ConsoleStatusReporter" - }, - "consolidator": { - "class": "bzt.modules.aggregator.ConsolidatingAggregator", - "percentiles": [ - 0.0, - 50.0, - 90.0, - 95.0, - 99.0, - 99.9, - 100.0 - ] - }, - "final-stats": { - "class": "bzt.modules.reporting.FinalStatus" - }, - "functional-consolidator": { - "class": "bzt.modules.functional.FunctionalAggregator" - }, - "android-emulator": { - "class": "bzt.modules.services.AndroidEmulatorLoader" - }, - "appium": { - "class": "bzt.modules.services.AppiumLoader" - }, - "virtual-display": { - "class": "bzt.modules.services.VirtualDisplay" - }, - "external-results-loader": { - "class": "bzt.modules.external.ExternalResultsLoader" - }, - "blazemeter": { - "class": "bzt.modules.blazemeter.BlazeMeterUploader" - }, - "cloud": { - "class": "bzt.modules.blazemeter.CloudProvisioning", - "default-location": "us-central1-a" - }, - "proxy2jmx": { - "class": "bzt.modules.proxy2jmx.Proxy2JMX" - }, - "unpacker": { - "class": "bzt.modules.services.Unpacker" - }, - "screenshoter": { - "class": "bzt.modules.blazemeter.ServiceStubScreenshoter" - }, - "capturehar": { - "class": "bzt.modules.blazemeter.ServiceStubCaptureHAR" - }, - "final_stats": { - "class": "bzt.modules.reporting.FinalStatus" - } - }, - "cli-aliases": { - "cloud": { - "provisioning": "cloud" - }, - "detach": { - "modules": { - "cloud": { - "detach": true - } - } - }, - "func": { - "modules": { - "console": { - "disable": true - } - }, - "settings": { - "aggregator": "functional-consolidator" - } - }, - "gui": { - "modules": { - "jmeter": { - "gui": true - } - } - }, - "install-tools": { - "services": [ - "install-checker" - ] - }, - "local": { - "provisioning": "local" - }, - "locations": { - "modules": { - "cloud": { - "dump-locations": true - } - }, - "provisioning": "cloud" - }, - "report": { - "reporting": [ - "blazemeter" - ] - }, - "public": { - "modules": { - "blazemeter": { - "public-report": true - } - } - }, - "sequential": { - "modules": { - "local": { - "sequential": true - } - } - }, - "lint": { - "cli": { - "linter": { - "lint-and-exit": true - } - } - }, - "nolint": { - "cli": { - "linter": { - "disable": true - } - } - } - }, - "cli": { - "linter": { - "disable": false, - "warn-on-unfamiliar-fields": false, - "ignored-warnings": [], - "lint-and-exit": false - } - }, - "settings": { - "aggregator": "consolidator", - "default-executor": "jmeter", - "env": { - "OBJC_DISABLE_INITIALIZE_FORK_SAFETY": "YES", - "BASEDIR": ".", - "TAURUS_ARTIFACTS_DIR": "/Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-03-04.522973" - } - }, - "provisioning": "local", - "reporting": [ - { - "module": "final-stats" - }, - { - "module": "console" - }, - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-03-04.522973/xunit.xml" - }, - { - "module": "junit-xml", - "data-source": "sample-labels", - "filename": "/Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-03-04.522973/xunit-1.xml" - }, - { - "module": "final-stats", - "dump-csv": "./final_stats.csv" - } - ], - "services": [ - { - "module": "monitoring", - "local": [ - { - "metrics": [ - "cpu", - "mem", - "bytes-sent", - "bytes-recv", - "disk-read", - "disk-write", - "disk-space", - "engine-loop", - "conn-all" - ] - } - ] - } - ], - "execution": [ - { - "executor": "jmeter", - "scenario": "teardown.jmx", - "throughput": {}, - "concurrency": {} - } - ], - "included-configs": [], - "version": "1.14.2", - "scenarios": { - "teardown.jmx": { - "script": "/Users/mahesh/git/multi-model-server/tests/performance/tests/common/teardown.jmx" - } - } -} diff --git a/tests/performance/2020-06-18_22-03-04.522973/effective.yml b/tests/performance/2020-06-18_22-03-04.522973/effective.yml deleted file mode 100644 index acfa0c7f8..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/effective.yml +++ /dev/null @@ -1,217 +0,0 @@ ---- -cli: - linter: - disable: false - ignored-warnings: [] - lint-and-exit: false - warn-on-unfamiliar-fields: false -cli-aliases: - cloud: - provisioning: cloud - detach: - modules: - cloud: - detach: true - func: - modules: - console: - disable: true - settings: - aggregator: functional-consolidator - gui: - modules: - jmeter: - gui: true - install-tools: - services: - - install-checker - lint: - cli: - linter: - lint-and-exit: true - local: - provisioning: local - locations: - modules: - cloud: - dump-locations: true - provisioning: cloud - nolint: - cli: - linter: - disable: true - public: - modules: - blazemeter: - public-report: true - report: - reporting: - - blazemeter - sequential: - modules: - local: - sequential: true -execution: -- concurrency: {} - executor: jmeter - scenario: teardown.jmx - throughput: {} -included-configs: [] -modules: - ab: - class: bzt.modules.ab.ApacheBenchmarkExecutor - android-emulator: - class: bzt.modules.services.AndroidEmulatorLoader - apiritif: - class: bzt.modules.apiritif.ApiritifNoseExecutor - appium: - class: bzt.modules.services.AppiumLoader - blazemeter: - class: bzt.modules.blazemeter.BlazeMeterUploader - capturehar: - class: bzt.modules.blazemeter.ServiceStubCaptureHAR - cloud: - class: bzt.modules.blazemeter.CloudProvisioning - default-location: us-central1-a - console: - class: bzt.modules.console.ConsoleStatusReporter - consolidator: - class: bzt.modules.aggregator.ConsolidatingAggregator - percentiles: - - 0.0 - - 50.0 - - 90.0 - - 95.0 - - 99.0 - - 99.9 - - 100.0 - external-results-loader: - class: bzt.modules.external.ExternalResultsLoader - final-stats: - class: bzt.modules.reporting.FinalStatus - final_stats: - class: bzt.modules.reporting.FinalStatus - functional-consolidator: - class: bzt.modules.functional.FunctionalAggregator - gatling: - class: bzt.modules.gatling.GatlingExecutor - properties: - gatling.data.file.bufferSize: 256 - grinder: - class: bzt.modules.grinder.GrinderExecutor - properties: - grinder.useConsole: false - install-checker: - class: bzt.modules.services.InstallChecker - jmeter: - class: bzt.modules.jmeter.JMeterExecutor - path: /Users/mahesh/.bzt/jmeter-taurus/5.2.1/bin/jmeter.sh - plugins: - - jpgc-casutg - - jpgc-dummy - - jpgc-ffw - - jpgc-fifo - - jpgc-functions - - jpgc-json - - jpgc-perfmon - - jpgc-prmctl - - jpgc-tst - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - jmeter.save.saveservice.autoflush: 'true' - jmeter.save.saveservice.connect_time: 'true' - jmeterengine.force.system.exit: 'true' - management_port: 8081 - port: 8080 - protocol: http - summariser.name: '' - protocol-handlers: - http: bzt.jmx.http.HTTPProtocolHandler - version: 5.2.1 - junit: - class: bzt.modules.java.JUnitTester - junit-xml: - class: bzt.modules.reporting.JUnitXMLReporter - local: - class: bzt.modules.provisioning.Local - locust: - class: bzt.modules.locustio.LocustIOExecutor - mocha: - class: bzt.modules.javascript.MochaTester - molotov: - class: bzt.modules.molotov.MolotovExecutor - monitoring: - class: bzt.modules.monitoring.Monitoring - newman: - class: bzt.modules.javascript.NewmanExecutor - nunit: - class: bzt.modules.csharp.NUnitExecutor - passfail: - class: bzt.modules.passfail.PassFailStatus - pbench: - class: bzt.modules.pbench.PBenchExecutor - proxy2jmx: - class: bzt.modules.proxy2jmx.Proxy2JMX - pytest: - class: bzt.modules.pytest.PyTestExecutor - robot: - class: bzt.modules.robot.RobotExecutor - rspec: - class: bzt.modules.ruby.RSpecTester - screenshoter: - class: bzt.modules.blazemeter.ServiceStubScreenshoter - selenium: - class: bzt.modules.selenium.SeleniumExecutor - shellexec: - class: bzt.modules.shellexec.ShellExecutor - siege: - class: bzt.modules.siege.SiegeExecutor - testng: - class: bzt.modules.java.TestNGTester - tsung: - class: bzt.modules.tsung.TsungExecutor - unpacker: - class: bzt.modules.services.Unpacker - virtual-display: - class: bzt.modules.services.VirtualDisplay - wdio: - class: bzt.modules.javascript.WebdriverIOExecutor -provisioning: local -reporting: -- module: final-stats -- module: console -- module: passfail -- data-source: pass-fail - filename: /Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-03-04.522973/xunit.xml - module: junit-xml -- data-source: sample-labels - filename: /Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-03-04.522973/xunit-1.xml - module: junit-xml -- dump-csv: ./final_stats.csv - module: final-stats -scenarios: - teardown.jmx: - script: /Users/mahesh/git/multi-model-server/tests/performance/tests/common/teardown.jmx -services: -- local: - - metrics: - - cpu - - mem - - bytes-sent - - bytes-recv - - disk-read - - disk-write - - disk-space - - engine-loop - - conn-all - module: monitoring -settings: - aggregator: consolidator - default-executor: jmeter - env: - BASEDIR: . - OBJC_DISABLE_INITIALIZE_FORK_SAFETY: 'YES' - TAURUS_ARTIFACTS_DIR: /Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-03-04.522973 -version: 1.14.2 - diff --git a/tests/performance/2020-06-18_22-03-04.522973/error.jtl b/tests/performance/2020-06-18_22-03-04.522973/error.jtl deleted file mode 100644 index 32bb536a8..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/error.jtl +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/2020-06-18_22-03-04.522973/global_config.yaml b/tests/performance/2020-06-18_22-03-04.522973/global_config.yaml deleted file mode 100644 index 94731f4a3..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/global_config.yaml +++ /dev/null @@ -1,26 +0,0 @@ -modules: - jmeter: - # These are JMeter test case properties. These variables are used in jmx files. - # Change the vaues as per your setup - properties: - hostname : 127.0.0.1 # MMS properties - port : 8080 - management_port : 8081 - protocol : http - input_filepath : kitten.jpg # make sure jpg is available at this path - # if relative path is provided this will be relative to current working directory - -# DO-NOT change properties below unless you know what you are doing. -# They are needed for performance test suite runner script. -reporting: -- module: passfail # this is to enable passfail module -- module: junit-xml - data-source: pass-fail -- module: junit-xml - data-source: sample-labels -- module: final-stats - dump-csv : ${BASEDIR}/final_stats.csv - -settings: - env: - BASEDIR : '.' diff --git a/tests/performance/2020-06-18_22-03-04.522973/jmeter-bzt.properties b/tests/performance/2020-06-18_22-03-04.522973/jmeter-bzt.properties deleted file mode 100644 index 437969fb2..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/jmeter-bzt.properties +++ /dev/null @@ -1,14 +0,0 @@ -jmeter.save.saveservice.autoflush=true -jmeter.save.saveservice.connect_time=true -jmeterengine.force.system.exit=true -summariser.name= -hostname=127.0.0.1 -port=8080 -management_port=8081 -protocol=http -input_filepath=kitten.jpg -jmeterengine.nongui.port=4445 -jmeterengine.nongui.maxport=4445 -jmeter.save.saveservice.timestamp_format=ms -sampleresult.default.encoding=UTF-8 -user.classpath=/Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-03-04.522973:/Users/mahesh/git/multi-model-server/tests/performance/tests/common diff --git a/tests/performance/2020-06-18_22-03-04.522973/jmeter.err b/tests/performance/2020-06-18_22-03-04.522973/jmeter.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/performance/2020-06-18_22-03-04.522973/jmeter.out b/tests/performance/2020-06-18_22-03-04.522973/jmeter.out deleted file mode 100644 index 6de1db02c..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/jmeter.out +++ /dev/null @@ -1,5 +0,0 @@ -Created the tree successfully using /Users/mahesh/git/multi-model-server/tests/performance/tests/common/modified_teardown.jmx -Starting standalone test @ Thu Jun 18 22:03:09 IST 2020 (1592497989081) -Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445 -Tidying up ... @ Thu Jun 18 22:03:09 IST 2020 (1592497989719) -... end of run diff --git a/tests/performance/2020-06-18_22-03-04.522973/jmx_gf97bh11.json b/tests/performance/2020-06-18_22-03-04.522973/jmx_gf97bh11.json deleted file mode 100644 index b69c2fdb4..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/jmx_gf97bh11.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "execution": [ - { - "executor": "jmeter", - "scenario": { - "script": "tests/common/teardown.jmx" - } - } - ] -} diff --git a/tests/performance/2020-06-18_22-03-04.522973/kpi.jtl b/tests/performance/2020-06-18_22-03-04.522973/kpi.jtl deleted file mode 100644 index 9332ed92c..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/kpi.jtl +++ /dev/null @@ -1,2 +0,0 @@ -timeStamp,elapsed,label,responseCode,responseMessage,threadName,success,bytes,grpThreads,allThreads,Latency,Hostname,Connect -1592497989661,54,Unregister squeezenet_v1.1,200,OK,Tear Down 1-1,true,323,1,1,52,BRL06570.local,32 diff --git a/tests/performance/2020-06-18_22-03-04.522973/merged.json b/tests/performance/2020-06-18_22-03-04.522973/merged.json deleted file mode 100644 index bd20e0877..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/merged.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "modules": { - "jmeter": { - "properties": { - "hostname": "127.0.0.1", - "port": 8080, - "management_port": 8081, - "protocol": "http", - "input_filepath": "kitten.jpg" - } - } - }, - "reporting": [ - { - "module": "passfail" - }, - { - "module": "junit-xml", - "data-source": "pass-fail" - }, - { - "module": "junit-xml", - "data-source": "sample-labels" - }, - { - "module": "final-stats", - "dump-csv": "${BASEDIR}/final_stats.csv" - } - ], - "settings": { - "env": { - "BASEDIR": "." - } - }, - "execution": [ - { - "executor": "jmeter", - "scenario": { - "script": "tests/common/teardown.jmx" - } - } - ] -} diff --git a/tests/performance/2020-06-18_22-03-04.522973/merged.yml b/tests/performance/2020-06-18_22-03-04.522973/merged.yml deleted file mode 100644 index c22e8839c..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/merged.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -execution: -- executor: jmeter - scenario: - script: tests/common/teardown.jmx -modules: - jmeter: - properties: - hostname: 127.0.0.1 - input_filepath: kitten.jpg - management_port: 8081 - port: 8080 - protocol: http -reporting: -- module: passfail -- data-source: pass-fail - module: junit-xml -- data-source: sample-labels - module: junit-xml -- dump-csv: ${BASEDIR}/final_stats.csv - module: final-stats -settings: - env: - BASEDIR: . - diff --git a/tests/performance/2020-06-18_22-03-04.522973/modified_teardown.jmx b/tests/performance/2020-06-18_22-03-04.522973/modified_teardown.jmx deleted file mode 100644 index 50003be38..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/modified_teardown.jmx +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - model - squeezenet_v1.1 - = - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - /models/${model} - DELETE - true - false - true - false - - - - - - - /Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-03-04.522973/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/Users/mahesh/git/multi-model-server/tests/performance/2020-06-18_22-03-04.522973/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue - - diff --git a/tests/performance/2020-06-18_22-03-04.522973/xunit-1.xml b/tests/performance/2020-06-18_22-03-04.522973/xunit-1.xml deleted file mode 100644 index 9b7c3d2cc..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/xunit-1.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/tests/performance/2020-06-18_22-03-04.522973/xunit.xml b/tests/performance/2020-06-18_22-03-04.522973/xunit.xml deleted file mode 100644 index 8bdd58dbc..000000000 --- a/tests/performance/2020-06-18_22-03-04.522973/xunit.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/tests/performance/nohup.out b/tests/performance/nohup.out deleted file mode 100644 index 66a207ba4..000000000 --- a/tests/performance/nohup.out +++ /dev/null @@ -1,316 +0,0 @@ -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I am running -I \ No newline at end of file From 07780b9eb0501aa0146aede09919e82feddb1545 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 19:06:49 +0530 Subject: [PATCH 132/329] change in ini --- tests/performance/agents/config.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/performance/agents/config.ini b/tests/performance/agents/config.ini index 5b391b46e..79ab92e68 100644 --- a/tests/performance/agents/config.ini +++ b/tests/performance/agents/config.ini @@ -6,5 +6,4 @@ HOST = PORT = 9009 [suite] -diff_percent = 35 s3_bucket = regression-reports-123 \ No newline at end of file From bee14d1a7ee7f3c9be5984601a37f8885e683fba Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 19:10:43 +0530 Subject: [PATCH 133/329] path name cahnge --- tests/performance/run_performance_suite.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 3e7ede37a..7979450c3 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -40,13 +40,14 @@ logger = logging.getLogger(__name__) logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) -PATH = pathlib.Path(__file__).parent.absolute() -GLOBAL_CONFIG_PATH = "{}/tests/common/global_config.yaml".format(PATH) + + code = 0 metrics_monitoring_server = "agents/metrics_monitoring_server.py" base_file_path = pathlib.Path(__file__).parent.absolute() run_artifacts_path = "{}/run_artifacts/".format(base_file_path) +GLOBAL_CONFIG_PATH = "{}/tests/common/global_config.yaml".format(base_file_path) S3_BUCKET = configuration.get('suite', 's3_bucket') @@ -331,7 +332,7 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): def get_test_yamls(dir_path=None, pattern="*.yaml"): if not dir_path: - dir_path = str(PATH) + "/tests" + dir_path = str(base_file_path) + "/tests" path_pattern = "{}/{}".format(dir_path, pattern) return glob.glob(path_pattern) @@ -369,9 +370,9 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam artifacts_dir = "{}/{}".format(artifacts_dir, artifacts_folder_name) logger.info("Artifacts will be stored in directory {}".format(artifacts_dir)) - with Monitoring(PATH, monit): + with Monitoring(base_file_path, monit): junit_xml = JUnitXml() - pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(PATH)) + pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(base_file_path)) test_yamls = get_test_yamls(test_dir, pattern) for test_file in tqdm(test_yamls, desc="Test Suites"): suite_name = os.path.basename(test_file).rsplit('.', 1)[0] From 7ac1cfcf754eae5bdac85b29e262b7cfc8895a86 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 19:17:47 +0530 Subject: [PATCH 134/329] list models scenario --- tests/performance/TESTS.md | 2 +- tests/performance/tests/list_models.jmx | 76 ++++++++++++++++++++++++ tests/performance/tests/list_models.yaml | 55 +++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 tests/performance/tests/list_models.jmx create mode 100644 tests/performance/tests/list_models.yaml diff --git a/tests/performance/TESTS.md b/tests/performance/TESTS.md index 2be0b66db..b2d57c5a6 100644 --- a/tests/performance/TESTS.md +++ b/tests/performance/TESTS.md @@ -22,4 +22,4 @@ |Health Check|A,B,E|| |API Description|A,B,E|| |Model Describe|A,B,E|[model_description.yaml](tests/model_description.yaml)| -|List Models|A,B,E|| +|List Models|A,B,E|[list_models.yaml](tests/list_models.yaml)| diff --git a/tests/performance/tests/list_models.jmx b/tests/performance/tests/list_models.jmx new file mode 100644 index 000000000..cc0f9bca8 --- /dev/null +++ b/tests/performance/tests/list_models.jmx @@ -0,0 +1,76 @@ + + + + + + false + true + false + + + + + + + + + + model + squeezenet_v1.1 + = + Model name + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models + GET + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/list_models.yaml b/tests/performance/tests/list_models.yaml new file mode 100644 index 000000000..d43b169b7 --- /dev/null +++ b/tests/performance/tests/list_models.yaml @@ -0,0 +1,55 @@ +--- +execution: +- concurrency: 10 + ramp-up: 1s + hold-for: 30s + scenario: list_models + +scenarios: + list_models: + script: list_models.jmx + +modules: + server_local_monitoring: + class : metrics_monitoring_taurus.Monitor + +services: + - module: shellexec + prepare: + - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/shufflenet.mar + post-process: + - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - curl -s -X DELETE http://localhost:8081/models/shufflenet + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + metrics: + - total_workers + - sum_workers_file_descriptors + - orphans + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 1 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '>' + threshold: 8 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '>' + threshold: 0 + timeframe: 1s + stop : true + fail : true \ No newline at end of file From 825f82561d28f75a999f3402117e946b0a040ba9 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 19:27:53 +0530 Subject: [PATCH 135/329] requirement.txt changes --- tests/performance/requirements.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/performance/requirements.txt b/tests/performance/requirements.txt index 400bf734b..aa2511a41 100644 --- a/tests/performance/requirements.txt +++ b/tests/performance/requirements.txt @@ -2,8 +2,9 @@ gevent==20.5.2 junitparser==1.4.1 git+https://github.com/maheshambule/vjunit.git#egg=vjunit tqdm==4.40.0 -pathlib -boto -aws -click -tabulate \ No newline at end of file +pathlib==1.0.1 +boto==2.49.0 +awscli==1.18.80 +click==7.1.2 +tabulate==0.8.7 +pandas==1.0.3 \ No newline at end of file From 04b3661b7700856b78677844bd731a5d7f0ef66c Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 19:33:11 +0530 Subject: [PATCH 136/329] boto3 req.txt --- tests/performance/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/requirements.txt b/tests/performance/requirements.txt index aa2511a41..3893d6b09 100644 --- a/tests/performance/requirements.txt +++ b/tests/performance/requirements.txt @@ -3,7 +3,7 @@ junitparser==1.4.1 git+https://github.com/maheshambule/vjunit.git#egg=vjunit tqdm==4.40.0 pathlib==1.0.1 -boto==2.49.0 +boto3==1.14.3 awscli==1.18.80 click==7.1.2 tabulate==0.8.7 From e07c43820aaed4ed72aabd3ebaa7c3d8dbb0e709 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 19:35:58 +0530 Subject: [PATCH 137/329] api description and health check scenario --- tests/performance/TESTS.md | 16 ++-- tests/performance/tests/api_description.jmx | 88 ++++++++++++++++++++ tests/performance/tests/api_description.yaml | 41 +++++++++ tests/performance/tests/health_check.jmx | 51 ++++++++++++ tests/performance/tests/health_check.yaml | 41 +++++++++ 5 files changed, 229 insertions(+), 8 deletions(-) create mode 100644 tests/performance/tests/api_description.jmx create mode 100644 tests/performance/tests/api_description.yaml create mode 100644 tests/performance/tests/health_check.jmx create mode 100644 tests/performance/tests/health_check.yaml diff --git a/tests/performance/TESTS.md b/tests/performance/TESTS.md index b2d57c5a6..6a4413333 100644 --- a/tests/performance/TESTS.md +++ b/tests/performance/TESTS.md @@ -13,13 +13,13 @@ |API|CODE|YAML| |---|----|---| -|Register Model|A,B,F,J|[register_unregister.yaml](tests/register_unregister.yaml)| -|Inference|A,B,C|[inference_single_worker.yaml](tests/inference_single_worker.yaml), [inference_multiple_worker.yaml](tests/inference_multiple_worker.yaml)| +|Register Model|A,B,F,J|[WIP] [register_unregister.yaml](tests/register_unregister.yaml)| +|Inference|A,B,C|[WIP] [inference_single_worker.yaml](tests/inference_single_worker.yaml), [inference_multiple_worker.yaml](tests/inference_multiple_worker.yaml)| |Batch Inference|A,B,C|| |Custom Model Handlers|C|| -|Scale Workers - UP/DOWN|D,G,I,F,J|[scale_up_workers.yaml](tests/scale_up_workers.yaml), [scale_down_workers.yaml](tests/scale_down_workers.yaml)| -|Unregister Models|D,G,I,J|[register_unregister.yaml](tests/register_unregister.yaml)| -|Health Check|A,B,E|| -|API Description|A,B,E|| -|Model Describe|A,B,E|[model_description.yaml](tests/model_description.yaml)| -|List Models|A,B,E|[list_models.yaml](tests/list_models.yaml)| +|Scale Workers - UP/DOWN|D,G,I,F,J|[WIP] [scale_up_workers.yaml](tests/scale_up_workers.yaml), [scale_down_workers.yaml](tests/scale_down_workers.yaml)| +|Unregister Models|D,G,I,J|[WIP] [register_unregister.yaml](tests/register_unregister.yaml)| +|Health Check|A,B,E|[WIP] [health_check.yaml](tests/health_check.yaml)| +|API Description|A,B,E|[WIP] [api_description.yaml](tests/api_description.yaml)| +|Model Describe|A,B,E|[WIP] [model_description.yaml](tests/model_description.yaml)| +|List Models|A,B,E|[WIP] [list_models.yaml](tests/list_models.yaml)| diff --git a/tests/performance/tests/api_description.jmx b/tests/performance/tests/api_description.jmx new file mode 100644 index 000000000..abdea339a --- /dev/null +++ b/tests/performance/tests/api_description.jmx @@ -0,0 +1,88 @@ + + + + + + false + true + false + + + + + + + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + + OPTIONS + true + false + true + false + + + + + + + + + + + ${__P(port,8443)} + + + + OPTIONS + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/api_description.yaml b/tests/performance/tests/api_description.yaml new file mode 100644 index 000000000..ff1ad5b71 --- /dev/null +++ b/tests/performance/tests/api_description.yaml @@ -0,0 +1,41 @@ +--- +execution: +- concurrency: 10 + ramp-up: 1s + hold-for: 30s + scenario: api_description + +scenarios: + api_description: + script: api_description.jmx + +modules: + server_local_monitoring: + class : metrics_monitoring_taurus.Monitor + +services: + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + metrics: + - total_workers + - sum_workers_file_descriptors + - orphans + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 0 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '>' + threshold: 0 + timeframe: 1s + stop : true + fail : true \ No newline at end of file diff --git a/tests/performance/tests/health_check.jmx b/tests/performance/tests/health_check.jmx new file mode 100644 index 000000000..6e8ef7325 --- /dev/null +++ b/tests/performance/tests/health_check.jmx @@ -0,0 +1,51 @@ + + + + + + false + true + false + + + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + /ping + GET + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/health_check.yaml b/tests/performance/tests/health_check.yaml new file mode 100644 index 000000000..306dfdd1b --- /dev/null +++ b/tests/performance/tests/health_check.yaml @@ -0,0 +1,41 @@ +--- +execution: +- concurrency: 10 + ramp-up: 1s + hold-for: 30s + scenario: health_check + +scenarios: + health_check: + script: health_check.jmx + +modules: + server_local_monitoring: + class : metrics_monitoring_taurus.Monitor + +services: + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + metrics: + - total_workers + - sum_workers_file_descriptors + - orphans + +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 0 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '>' + threshold: 0 + timeframe: 1s + stop : true + fail : true \ No newline at end of file From 5ca9e96a97c3cfaebca06fdad365680d4253946f Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 19:44:37 +0530 Subject: [PATCH 138/329] Updated test plan names --- tests/performance/tests/api_description.jmx | 2 +- tests/performance/tests/health_check.jmx | 2 +- tests/performance/tests/inference_multiple_worker.jmx | 2 +- tests/performance/tests/inference_single_worker.jmx | 2 +- tests/performance/tests/list_models.jmx | 2 +- tests/performance/tests/model_description.jmx | 2 +- tests/performance/tests/register_and_inference.jmx | 2 +- tests/performance/tests/register_unregister.jmx | 2 +- tests/performance/tests/scale_down_workers.jmx | 2 +- tests/performance/tests/scale_up_workers.jmx | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/performance/tests/api_description.jmx b/tests/performance/tests/api_description.jmx index abdea339a..e2b2dd867 100644 --- a/tests/performance/tests/api_description.jmx +++ b/tests/performance/tests/api_description.jmx @@ -1,7 +1,7 @@ - + false true diff --git a/tests/performance/tests/health_check.jmx b/tests/performance/tests/health_check.jmx index 6e8ef7325..ada3f72e1 100644 --- a/tests/performance/tests/health_check.jmx +++ b/tests/performance/tests/health_check.jmx @@ -1,7 +1,7 @@ - + false true diff --git a/tests/performance/tests/inference_multiple_worker.jmx b/tests/performance/tests/inference_multiple_worker.jmx index 04bfce2e2..867b30162 100644 --- a/tests/performance/tests/inference_multiple_worker.jmx +++ b/tests/performance/tests/inference_multiple_worker.jmx @@ -1,7 +1,7 @@ - + false true diff --git a/tests/performance/tests/inference_single_worker.jmx b/tests/performance/tests/inference_single_worker.jmx index 04bfce2e2..8dcfe0702 100644 --- a/tests/performance/tests/inference_single_worker.jmx +++ b/tests/performance/tests/inference_single_worker.jmx @@ -1,7 +1,7 @@ - + false true diff --git a/tests/performance/tests/list_models.jmx b/tests/performance/tests/list_models.jmx index cc0f9bca8..2030b122c 100644 --- a/tests/performance/tests/list_models.jmx +++ b/tests/performance/tests/list_models.jmx @@ -1,7 +1,7 @@ - + false true diff --git a/tests/performance/tests/model_description.jmx b/tests/performance/tests/model_description.jmx index 64524c383..f6d33c99c 100644 --- a/tests/performance/tests/model_description.jmx +++ b/tests/performance/tests/model_description.jmx @@ -1,7 +1,7 @@ - + false true diff --git a/tests/performance/tests/register_and_inference.jmx b/tests/performance/tests/register_and_inference.jmx index 265b5efb8..0d60d304c 100644 --- a/tests/performance/tests/register_and_inference.jmx +++ b/tests/performance/tests/register_and_inference.jmx @@ -1,7 +1,7 @@ - + false true diff --git a/tests/performance/tests/register_unregister.jmx b/tests/performance/tests/register_unregister.jmx index d08cf1dc1..5031f750d 100644 --- a/tests/performance/tests/register_unregister.jmx +++ b/tests/performance/tests/register_unregister.jmx @@ -1,7 +1,7 @@ - + false true diff --git a/tests/performance/tests/scale_down_workers.jmx b/tests/performance/tests/scale_down_workers.jmx index 1abcd9eae..cca760b5f 100644 --- a/tests/performance/tests/scale_down_workers.jmx +++ b/tests/performance/tests/scale_down_workers.jmx @@ -1,7 +1,7 @@ - + false true diff --git a/tests/performance/tests/scale_up_workers.jmx b/tests/performance/tests/scale_up_workers.jmx index 0a5ac2905..e8afa3f10 100644 --- a/tests/performance/tests/scale_up_workers.jmx +++ b/tests/performance/tests/scale_up_workers.jmx @@ -1,7 +1,7 @@ - + false true From 9282f285a8201b61b8f699c8ddc56537bb4eb23d Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 20:15:24 +0530 Subject: [PATCH 139/329] added missing threshold --- .../performance/tests/inference_server_monitoring_criteria.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/performance/tests/inference_server_monitoring_criteria.yaml b/tests/performance/tests/inference_server_monitoring_criteria.yaml index e0f1b4451..46ea8fbeb 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_server_monitoring_criteria.yaml @@ -34,6 +34,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: mms-inference-server/sum_workers_file_descriptors condition: '>' + threshold: 80 timeframe: 1s fail: true stop: true From 9c25f8b62e03d9954ae9091c2de6d7607a0a93bd Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 20:20:41 +0530 Subject: [PATCH 140/329] polished README.md Signed-off-by: Dhruva Ray --- tests/performance/README.md | 77 ++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 3497b0019..9897960cc 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -25,8 +25,7 @@ Refer the [link](https://gettaurus.org/docs/Installation/) for more details on i export MMS_HOME= pip install -r $MMS_HOME/tests/performance/requirements.txt ``` -3. Git is installed. -Make sure that you have git installed and you are in MMS git repository while running the test suite. +3. Make sure that `git` is installed and the test suites are run from the MMS working directory. This is used to compare performance regresssions across runs and the run artifacts are stored in a folder which have the commit SHA. ### B. Running the test suite 1. Run MMS server @@ -50,25 +49,8 @@ Make sure that you have git installed and you are in MMS git repository while ru python -m run_performance_suite --artifacts-dir='' --pattern='*criteria*.yaml' ``` -### C. Understanding comparison between different runs: -At the end, the test suite runner script compares the monitoring metric with values from previous run which was executed on same environment. -Note you should have a one test suite run on same environment in order to do the comparison. -The previous run results from S3 bucket or a local directory can be used. See compare-local option below. The comparison happens for the -monitoring metrics criteria for which 'diff_percent' properties is specified. See pass/fail criteria [section](#3-add-passfail-criteria) -Below are different options used by run_performance_suite script for coparison. -1. **artifacts-dir**: -This is an optional parameter. The default is './run_artifacts' directory. -A sub directory with '{env_name}_{git_commit_id}_{timestamp}' gets created in the artifacts dir. -2. **env-name**: -This is an optional parameter. The default is current hostname. This should be unique it is used while doing comparison between runs. -Comparison should happen between the runs of same environment. -3. **compare-local/no-compare-local**: -This is an optional parameter. The default is compare-local. If compare-local is on previous run results from './run_artifacts' will be used used otherwise results from -S3 bucket will be used. Note that other users will also be uploading the results to same S3 bucket. - - -### D. Understanding the test suite artifacts and reports -1. The $artifacts-dir$/junit.html contains the summary report of the test run. Note that each test yaml is treated as a +### C. Understanding the test suite artifacts and reports +1. The $artifacts-dir$//junit.html contains the summary report of the test run. Note that each test yaml is treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not specified in the yaml, test suite is marked as skipped with 0 test cases. 2. For each test yaml a sub-directory is created with artifacts for it. 3. The comparison.csv contains diff for monitoring metrics between an ongoing run and a previous run which was ran for same MMS server. @@ -202,12 +184,10 @@ Metrics can be monitored in two ways: bzt tests/inference_taurus_local_monitoring.yaml tests/common/global_config.yaml ``` -#### 3. Add pass/fail criteria +#### 3.1 Add pass/fail criteria You can specify the pass/fail criteria for the test cases. Read more about it [here](https://gettaurus.org/docs/PassFail/) -**Note** The diff_percent is used by run_performance_suite script to compare the metric across different runs. - Relevant test yaml section: ```yaml reporting: @@ -224,18 +204,37 @@ reporting: ``` -Test yamls can be found [here](tests/inference_server_monitoring_criteria.yaml) and [here](tests/inference_taurus_local_monitoring_criteria.yaml). -Use command below to run the test case +#### 3.2 Add pass/fail criteria with previous run +On completion, the test suite runner script compares the monitoring metrics with values from a previous run which was executed on same environment. +Note that at least one test suite run on the same environment should have happened in order to do the comparison. The run results are stored in either a local folder or a S3 bucket based on the `compare-local` option +Metrics which have 'diff_percent' value specified in the pass/fail criterion are used for comparision with the previous run. See pass/fail criteria [section](#3-add-passfail-criteria) +Below are different options used by run_performance_suite script for coparison. +1. **artifacts-dir**: +This is an optional parameter. The default is './run_artifacts' directory. +A sub directory with '{env_name}_{git_commit_id}_{timestamp}' gets created in the artifacts dir. +2. **env-name**: +This is an optional parameter. The default is current hostname. This should be unique it is used while doing comparison between runs. +Comparison should happen between the runs of same environment. +3. **compare-local/no-compare-local**: +This is an optional parameter. The default is compare-local. If `compare-local` is set, previous run results from the local './run_artifacts' folder will be used used. +`experimental` If no-compare-local is set, previous run results from the public S3 bucket will be used. `no-compare-local is currently experimental` -```bash -export MMS_HOME= -cd $MMS_HOME/tests/performance -bzt inference_server_monitoring_criteria.yaml tests/common/global_config.yaml -bzt inference_taurus_local_monitoring_criteria.yaml tests/common/global_config.yaml -``` +```yaml +reporting: +- module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: mms-inference-server/sum_num_handles + condition: '>' + threshold: 180 + timeframe: 1s + fail: true + stop: true + diff_percent : 30 +``` -## Metrics that you can use for monitoring \ passfail criteria +#### 3.3 Metrics that you can use for passfail criteria **System Metrics** > disk_used, memory_percent, read_count, write_count, read_bytes, write_byte @@ -275,11 +274,9 @@ bzt inference_taurus_local_monitoring_criteria.yaml tests/common/global_config.y * total_processes - Total number of processes spawned for frontend & workers * total_workers - Total number of workers spawned * orphans - Total number of orphan processes - -## Work in Progress -1. Add more metrics for cpu and gpu both. Add documentation around those. -2. Add hooks to add custom metrics. Add a metrics registry. -3. Better reporting and artifact management -4. Enhance framework to add better abstraction to hide Taurus and other scripts. -5. Auto threshold calculation, environment profiles +## Test Strategy +More details about our testing strategy and test cases can be found [here](TESTS.md) + +## TODOs +1. Auto threshold calculation, environment profiles From 5b193b9fe3001261c663f762c8c145eea443f862 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 20:46:41 +0530 Subject: [PATCH 141/329] changes --- tests/performance/agents/config.ini | 2 +- tests/performance/run_performance_suite.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/performance/agents/config.ini b/tests/performance/agents/config.ini index 79ab92e68..aacbe97e1 100644 --- a/tests/performance/agents/config.ini +++ b/tests/performance/agents/config.ini @@ -6,4 +6,4 @@ HOST = PORT = 9009 [suite] -s3_bucket = regression-reports-123 \ No newline at end of file +s3_bucket = mms-performance-regression-reports \ No newline at end of file diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 7979450c3..b2d196cfd 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -367,6 +367,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam artifacts_dir = "{}/{}".format(run_artifacts_path, artifacts_folder_name) store_local = True else: + artifacts_dir = os.path.abspath(artifacts_dir) artifacts_dir = "{}/{}".format(artifacts_dir, artifacts_folder_name) logger.info("Artifacts will be stored in directory {}".format(artifacts_dir)) From 1dc558e51219ac36e879199ead4ffb0d38977f8d Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 20:48:51 +0530 Subject: [PATCH 142/329] Update README.md --- tests/performance/README.md | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 9897960cc..acaa60d5a 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -70,26 +70,26 @@ To get you started quickly, we have provided a sample JMeter script and a Taurus Here is how the sample call_jmx.yaml looks like. Note variables used by jmx script are specified in [tests/common/global_config.yaml](tests/common/global_config.yaml) file. - ```yaml - execution: - - concurrency: 1 - ramp-up: 1s - hold-for: 40s - scenario: Inference - - scenarios: - Inference: - script: register_and_inference.jmx - - ``` + ```yaml + execution: + - concurrency: 1 + ramp-up: 1s + hold-for: 40s + scenario: Inference + + scenarios: + Inference: + script: register_and_inference.jmx + + ``` To run this individual test using Taurus(bzt) run commands below: - ```bash - export MMS_HOME= - cd $MMS_HOME/tests/performance - bzt tests/call_jmx.yaml tests/common/global_config.yaml - ``` + ```bash + export MMS_HOME= + cd $MMS_HOME/tests/performance + python -m run_performance_suite -p call_jmx.yaml + ``` **Note**: Taurus provides support for different executors such as JMeter. You can use test script written in those frameworks as it is. @@ -132,12 +132,12 @@ Metrics can be monitored in two ways: - server_workers # no of mms workers ``` - Use Taurus command below to run the test yaml and observe the Metrics widget on CLI live report. + Use the command below to run the test yaml. ```bash export MMS_HOME= cd $MMS_HOME/tests/performance - bzt tests/inference_server_monitoring.yaml tests/common/global_config.yaml + python -m run_performance_suite -p inference_server_monitoring.yaml ``` @@ -176,12 +176,12 @@ Metrics can be monitored in two ways: ``` - Use Taurus command below to run the test yaml and observe the Metrics widget on CLI live report. + Use command below to run the test yaml ```bash export MMS_HOME= cd $MMS_HOME/tests/performance - bzt tests/inference_taurus_local_monitoring.yaml tests/common/global_config.yaml + python -m run_performance_suite -p inference_taurus_local_monitoring.yaml ``` #### 3.1 Add pass/fail criteria From 51c7f0119a956ce04afac24f8b790933721b9f79 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 21:08:30 +0530 Subject: [PATCH 143/329] folder --- tests/performance/run_artifacts/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/performance/run_artifacts/.gitkeep diff --git a/tests/performance/run_artifacts/.gitkeep b/tests/performance/run_artifacts/.gitkeep new file mode 100644 index 000000000..e69de29bb From b4058771987d53175572920dd6db6666922da380 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 21:40:05 +0530 Subject: [PATCH 144/329] fix issue --- tests/performance/run_performance_suite.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index b2d196cfd..bd95dcdfe 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -248,6 +248,7 @@ def get_compare_metric_list(dir, sub_dir): def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): + logger.info("Comparing artifacts from {} with {}".format(dir1, dir2)) sub_dirs_1 = get_sub_dirs(dir1) sub_dirs_2 = get_sub_dirs(dir2) @@ -321,8 +322,10 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): else: row.extend([sub_dir1, "log_file", "log file available", "log file not available", "NA", "pass"]) + rows.append(row) + out_path = "{}/comparison.csv".format(out_dir) - logger.info("Writing comparison report to log file ".format(out_path)) + logger.info("Writing comparison report to log file {}".format(out_path)) with open(out_path, 'w', newline='') as csvfile: csv_writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) @@ -349,7 +352,6 @@ def get_options(artifacts_dir, jmeter_path=None): return options_str - @click.command() @click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True)) @click.option('-t', '--test-dir', help='Directory containing tests.', type=click.Path(exists=True), default=None) From 667738e5d5c1975531d86a5799842d1a1d7d9e03 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 19 Jun 2020 22:04:32 +0530 Subject: [PATCH 145/329] Revert breaking changes --- tests/performance/agents/metrics/__init__.py | 42 +++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index 282006e8c..a802f099e 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -81,33 +81,13 @@ class ProcessType(Enum): for op in list(operators): AVAILABLE_METRICS.append('{}_{}_{}'.format(op, PNAME, metric)) -children = set() +__CHILDREN = set() def get_metrics(server_process, child_processes): """ Get Server processes specific metrics """ result = {} - children.update(child_processes) - - # as_dict() gets all stats in one shot - processes_stats = [] - reclaimed_pids = [] - processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) - for process in children: - if len(list(filter(lambda p: p == server_process.pid, process.parents()))) == 0: - reclaimed_pids.append(process) - else: - processes_stats.append({'type': ProcessType.WORKER, 'stats' : process.as_dict()}) - - for p in reclaimed_pids: - children.remove(p) - - ### PROCESS METRICS ### - worker_stats = list(map(lambda x: x['stats'], \ - filter(lambda x: x['type'] == ProcessType.WORKER, processes_stats))) - server_stats = list(map(lambda x: x['stats'], \ - filter(lambda x: x['type'] == ProcessType.FRONTEND, processes_stats))) - all_stats = list(map(lambda x: x['stats'], processes_stats)) + __CHILDREN.update(child_processes) def update_metric(metric_name, proc_type, stats): stats = stats if stats else [0] @@ -124,14 +104,28 @@ def update_metric(metric_name, proc_type, stats): for op_name in operators: result['{}_{}_{}'.format(op_name, proc_name, metric_name)] = operators[op_name](stats) + # as_dict() gets all stats in one shot + processes_stats = [] + processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) + for process in child_processes: + processes_stats.append({'type': ProcessType.WORKER, 'stats' : process.as_dict()}) + + ### PROCESS METRICS ### + worker_stats = list(map(lambda x: x['stats'], \ + filter(lambda x: x['type'] == ProcessType.WORKER, processes_stats))) + server_stats = list(map(lambda x: x['stats'], \ + filter(lambda x: x['type'] == ProcessType.FRONTEND, processes_stats))) + all_stats = list(map(lambda x: x['stats'], processes_stats)) + for k in process_metrics: update_metric(k, ProcessType.WORKER, list(map(process_metrics[k], worker_stats))) update_metric(k, ProcessType.ALL, list(map(process_metrics[k], all_stats))) update_metric(k, ProcessType.FRONTEND, list(map(process_metrics[k], server_stats))) + # Total processes - result['total_processes'] = len(worker_stats) + 1 - result['total_workers'] = max(len(worker_stats) - 1, 0) + result['total_processes'] = len(child_processes) + 1 + result['total_workers'] = max(len(child_processes) - 1, 0) result['orphans'] = len(list(filter(lambda p : p['ppid'] == 1, worker_stats))) ### SYSTEM METRICS ### From 8e083484294083f9ea17f29b07adf1416b70446d Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 22:42:21 +0530 Subject: [PATCH 146/329] fix write to comparision.csv Signed-off-by: Dhruva Ray --- tests/performance/run_performance_suite.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index bd95dcdfe..032225727 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -258,9 +258,7 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): header = ["run_name1", "run_name2", "test_suite", "metric", "run1", "run2", "percentage_diff", "result"] rows = [header] for sub_dir1 in sub_dirs_1: - row = [run_name1, run_name2] if sub_dir1 in sub_dirs_2: - metrics_file1 = glob.glob("{}/{}/SAlogs_*".format(dir1, sub_dir1)) metrics_file2 = glob.glob("{}/{}/SAlogs_*".format(dir2, sub_dir1)) if not (metrics_file1 and metrics_file2): @@ -268,7 +266,7 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): metrics_file2 = glob.glob("{}/{}/local_*".format(dir2, sub_dir1)) if not (metrics_file1 and metrics_file2): logger.info("Metrics monitoring logs are not captured for {} in either of the runs.".format(sub_dir1)) - row.extend([sub_dir1, "log_file", sub_dir1, sub_dir1, "metrics are not captured for either of the runs", "pass"]) + rows.append([run_name1, run_name2, sub_dir1, "log_file", sub_dir1, sub_dir1, "metrics are not captured for either of the runs", "pass"]) continue metrics_from_file1 = pd.read_csv(metrics_file1[0]) metrics_from_file2 = pd.read_csv(metrics_file2[0]) @@ -318,17 +316,14 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): if over_all_pass: over_all_pass = pass_fail == "pass" - row.extend([sub_dir1, name, val1, val2, diff, pass_fail]) + rows.append([run_name1, run_name2, sub_dir1, name, val1, val2, diff, pass_fail]) else: - row.extend([sub_dir1, "log_file", "log file available", "log file not available", "NA", "pass"]) - - rows.append(row) + rows.append([run_name1, run_name2, sub_dir1, "log_file", "log file available", "log file not available", "NA", "pass"]) out_path = "{}/comparison.csv".format(out_dir) logger.info("Writing comparison report to log file {}".format(out_path)) - with open(out_path, 'w', newline='') as csvfile: - csv_writer = csv.writer(csvfile, delimiter=',', - quotechar='|', quoting=csv.QUOTE_MINIMAL) + with open(out_path, 'w') as csvfile: + csv_writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) csv_writer.writerows(rows) return over_all_pass From ecd779519cf445e0c34ac34f669264feeb347f23 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 19 Jun 2020 22:43:14 +0530 Subject: [PATCH 147/329] changes --- tests/performance/run_performance_suite.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index bd95dcdfe..5eec182b1 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -41,8 +41,6 @@ logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) - - code = 0 metrics_monitoring_server = "agents/metrics_monitoring_server.py" base_file_path = pathlib.Path(__file__).parent.absolute() @@ -364,10 +362,8 @@ def get_options(artifacts_dir, jmeter_path=None): def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_name, compare_local): commit_id = subprocess.check_output('git rev-parse --short HEAD'.split()).decode("utf-8")[:-1] artifacts_folder_name = "{}_{}_{}".format(env_name, commit_id, int(time.time())) - store_local = False if artifacts_dir is None: artifacts_dir = "{}/{}".format(run_artifacts_path, artifacts_folder_name) - store_local = True else: artifacts_dir = os.path.abspath(artifacts_dir) artifacts_dir = "{}/{}".format(artifacts_dir, artifacts_folder_name) @@ -393,7 +389,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) if compare_local: - compare_dir, run_name = get_latest_dir(run_artifacts_path, env_name) + compare_dir, run_name = get_latest_dir(pathlib.Path(artifacts_dir).parent, env_name) else: compare_dir, run_name = download_s3_files(env_name, "{}/comp_data".format(artifacts_dir)) @@ -401,7 +397,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam if compare_dir: compare_result = compare_artifacts(artifacts_dir, compare_dir, artifacts_dir, artifacts_folder_name, run_name) - if not store_local: + if not compare_local: run_process("aws s3 cp {} s3://{}/{} --recursive".format(artifacts_dir, S3_BUCKET, artifacts_folder_name)) if junit_xml.errors or junit_xml.failures or junit_xml.skipped: From 8d7609823dee5420489b1813caba841467df07ac Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 22:51:41 +0530 Subject: [PATCH 148/329] remove empty run_artifacts dir, help change Signed-off-by: Dhruva Ray --- tests/performance/run_artifacts/.gitkeep | 0 tests/performance/run_performance_suite.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 tests/performance/run_artifacts/.gitkeep diff --git a/tests/performance/run_artifacts/.gitkeep b/tests/performance/run_artifacts/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index e9345c9cc..7f2793f94 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -353,7 +353,7 @@ def get_options(artifacts_dir, jmeter_path=None): @click.option('--monit/--no-monit', help='Start Monitoring server', default=True) @click.option('--env-name', help='Unique machine id on which MMS server is running', default=socket.gethostname()) @click.option('--compare-local/--no-compare-local', help='Compare with previous run with files stored' - ' in run_artifacts directory', default=True) + ' in artifacts directory', default=True) def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_name, compare_local): commit_id = subprocess.check_output('git rev-parse --short HEAD'.split()).decode("utf-8")[:-1] artifacts_folder_name = "{}_{}_{}".format(env_name, commit_id, int(time.time())) From 8d960a578dd96708828096d03e4950ecfcde7d0f Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 22:54:35 +0530 Subject: [PATCH 149/329] minor fix for README.md Signed-off-by: Dhruva Ray --- tests/performance/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index acaa60d5a..4254f317a 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -216,7 +216,7 @@ A sub directory with '{env_name}_{git_commit_id}_{timestamp}' gets created in th This is an optional parameter. The default is current hostname. This should be unique it is used while doing comparison between runs. Comparison should happen between the runs of same environment. 3. **compare-local/no-compare-local**: -This is an optional parameter. The default is compare-local. If `compare-local` is set, previous run results from the local './run_artifacts' folder will be used used. +This is an optional parameter. The default is compare-local. If `compare-local` is set, previous run results from the local `artifacts-dir` folder will be used used. `experimental` If no-compare-local is set, previous run results from the public S3 bucket will be used. `no-compare-local is currently experimental` ```yaml From 8ce37f4bda32105ac5dcc07263f908a445d1a4ec Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 19 Jun 2020 23:39:57 +0530 Subject: [PATCH 150/329] temo fix for s3 upload in case there is no prior run. Signed-off-by: Dhruva Ray --- tests/performance/run_performance_suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 7f2793f94..0ad585275 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -203,7 +203,7 @@ def download_s3_files(env_id, tgt_path, bucket_name=S3_BUCKET): latest_run = get_latest(run_names, env_id) if not latest_run: logger.info("No run found for env_id {}".format(env_id)) - return False + return '', '' if not os.path.exists(tgt_path): os.makedirs(tgt_path) From 2ce6244b2b87fd7641d90b39fe28c6fa767ec11a Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 22 Jun 2020 11:40:09 +0530 Subject: [PATCH 151/329] circleci integration with latest performance branch --- .circleci/config.yml | 78 ++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 19450916e..a56414d5d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -59,9 +59,9 @@ jobs: - run: name: Build frontend command: .circleci/scripts/linux_build.sh - - run: - name: Optimize before moving to workspace - command: rm -rf .git # Save disk space & time required for copy to\from workspace +# - run: +# name: Optimize before moving to workspace +# command: rm -rf .git # Save disk space & time required for copy to\from workspace - store_artifacts: name: Store gradle testng results path: frontend/server/build/reports/tests/test @@ -158,44 +158,44 @@ workflows: jobs: - build: executor: docker-python-3 - - modelarchiver: - executor: docker-python-3 - - python_tests: - executor: docker-python-3 - requires: - - build - - api_tests: - executor: docker-python-3 - requires: - - build - - benchmark: - executor: docker-python-3 - requires: - - build +# - modelarchiver: +# executor: docker-python-3 +# - python_tests: +# executor: docker-python-3 +# requires: +# - build +# - api_tests: +# executor: docker-python-3 +# requires: +# - build +# - benchmark: +# executor: docker-python-3 +# requires: +# - build - performance_regression: executor: docker-python-3 requires: - build - build_and_test_python27: - jobs: - - build: - executor: docker-python-2 - - modelarchiver: - executor: docker-python-2 - - python_tests: - executor: docker-python-2 - requires: - - build - - api_tests: - executor: docker-python-2 - requires: - - build - - benchmark: - executor: docker-python-2 - requires: - - build - - performance_regression: - executor: docker-python-2 - requires: - - build \ No newline at end of file +# build_and_test_python27: +# jobs: +# - build: +# executor: docker-python-2 +# - modelarchiver: +# executor: docker-python-2 +# - python_tests: +# executor: docker-python-2 +# requires: +# - build +# - api_tests: +# executor: docker-python-2 +# requires: +# - build +# - benchmark: +# executor: docker-python-2 +# requires: +# - build +# - performance_regression: +# executor: docker-python-2 +# requires: +# - build \ No newline at end of file From 7d60fa094bcb959529edc58f3ccdee3549b3fd5e Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Mon, 22 Jun 2020 11:49:27 +0530 Subject: [PATCH 152/329] fixed bug in computing reclaimed pids Signed-off-by: Dhruva Ray --- tests/performance/agents/metrics/__init__.py | 34 ++++++++++++++----- tests/performance/agents/metrics_collector.py | 2 +- .../agents/metrics_monitoring_taurus.py | 2 +- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index a802f099e..a9ba03570 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -81,13 +81,14 @@ class ProcessType(Enum): for op in list(operators): AVAILABLE_METRICS.append('{}_{}_{}'.format(op, PNAME, metric)) -__CHILDREN = set() +children = set() -def get_metrics(server_process, child_processes): +def get_metrics(server_process, child_processes, logger): """ Get Server processes specific metrics """ result = {} - __CHILDREN.update(child_processes) + children.update(child_processes) + logger.debug("children : {0}".format(",".join([str(c.pid) for c in children]))) def update_metric(metric_name, proc_type, stats): stats = stats if stats else [0] @@ -106,9 +107,27 @@ def update_metric(metric_name, proc_type, stats): # as_dict() gets all stats in one shot processes_stats = [] + reclaimed_pids = [] + processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) - for process in child_processes: - processes_stats.append({'type': ProcessType.WORKER, 'stats' : process.as_dict()}) + for child in children: + if psutil.pid_exists(child.pid): + if len(list(filter(lambda parent : parent.pid == server_process.pid, child.parents()))) == 0: + ppids = [str(parent.pid) for parent in child.parents()] + logger.info('server process id is {0}; child process id is {1}; {1} has the following parents {2} : '. + format( + server_process.pid, child.pid, ",".join(ppids) + ) + ) + reclaimed_pids.append(child) + else: + processes_stats.append({'type': ProcessType.WORKER, 'stats' : child.as_dict()}) + else: + reclaimed_pids.append(child) + logger.debug('child {0} no longer available'.format(child.pid)) + + for p in reclaimed_pids: + children.remove(p) ### PROCESS METRICS ### worker_stats = list(map(lambda x: x['stats'], \ @@ -122,10 +141,9 @@ def update_metric(metric_name, proc_type, stats): update_metric(k, ProcessType.ALL, list(map(process_metrics[k], all_stats))) update_metric(k, ProcessType.FRONTEND, list(map(process_metrics[k], server_stats))) - # Total processes - result['total_processes'] = len(child_processes) + 1 - result['total_workers'] = max(len(child_processes) - 1, 0) + result['total_processes'] = len(worker_stats) + 1 + result['total_workers'] = max(len(worker_stats) - 1, 0) result['orphans'] = len(list(filter(lambda p : p['ppid'] == 1, worker_stats))) ### SYSTEM METRICS ### diff --git a/tests/performance/agents/metrics_collector.py b/tests/performance/agents/metrics_collector.py index 845c606d9..4d6720db8 100755 --- a/tests/performance/agents/metrics_collector.py +++ b/tests/performance/agents/metrics_collector.py @@ -100,7 +100,7 @@ def monitor_processes(server_process, metrics, interval, socket): """ while True: message = [] - collected_metrics = get_metrics(server_process, get_child_processes(server_process)) + collected_metrics = get_metrics(server_process, get_child_processes(server_process), logger) #table = {} for metric in metrics: message.append(str(collected_metrics.get(metric, 0))) diff --git a/tests/performance/agents/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_taurus.py index 12d426ebe..9c78b633b 100644 --- a/tests/performance/agents/metrics_monitoring_taurus.py +++ b/tests/performance/agents/metrics_monitoring_taurus.py @@ -86,7 +86,7 @@ def _calc_resource_stats(self, interval): result = super()._calc_resource_stats(interval) server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) server_process = get_server_processes(server_pid) - result.update(get_metrics(server_process, get_child_processes(server_process))) + result.update(get_metrics(server_process, get_child_processes(server_process)), self.log) ''' table = {} From cbcb190c9865751c882ceb0718aa251d9ae2eb48 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 22 Jun 2020 13:07:01 +0530 Subject: [PATCH 153/329] tweaked for store_test_results --- .circleci/config.yml | 3 +++ .circleci/scripts/linux_test_perf_regression.sh | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a56414d5d..de724145d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,6 +46,9 @@ commands: - store_artifacts: name: Store << parameters.collection >> API test results path: test/<< parameters.collection >>-api-report.html + - store_test_results: + name: Store Taurus test results + path: /tmp/mms-performance-regression/report/ jobs: diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 460bcef57..10ff343d3 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -24,7 +24,7 @@ pip install bzt # Execute performance test suite and store exit code curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg -./run_performance_suite.py --artifacts-dir=$ARTIFACTS_DIR --jmeter-path=$JMETER_PATH +./run_performance_suite.py --artifacts-dir=$ARTIFACTS_DIR --jmeter-path=$JMETER_PATH --no-compare-local EXIT_CODE=$? # Stop server @@ -32,7 +32,7 @@ multi-model-server --stop >> mms.log 2>&1 # Collect and store test results in result directory to be picked up by CircleCI mkdir -p $RESULT_DIR -cp $ARTIFACTS_DIR'/junit.xml' $RESULT_DIR +cp $ARTIFACTS_DIR'/**/junit.xml' $RESULT_DIR # Exit with the same error code as that of test execution exit EXIT_CODE From 0141c8a0fc4bcaa960296e1640162ce65391003d Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 22 Jun 2020 13:27:03 +0530 Subject: [PATCH 154/329] tweaked for store_test_results --- .circleci/config.yml | 6 +++--- .circleci/scripts/linux_test_perf_regression.sh | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index de724145d..59bd2a170 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,9 +46,6 @@ commands: - store_artifacts: name: Store << parameters.collection >> API test results path: test/<< parameters.collection >>-api-report.html - - store_test_results: - name: Store Taurus test results - path: /tmp/mms-performance-regression/report/ jobs: @@ -138,6 +135,9 @@ jobs: - store_artifacts: name: Store artifacts from performance regression run path: /tmp/mms-performance-regression/ + - store_test_results: + name: Store Taurus test results + path: /tmp/mms-performance-regression/report/ modelarchiver: parameters: diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 10ff343d3..170552073 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -1,7 +1,7 @@ #!/bin/bash -ARTIFACTS_DIR='/tmp/mms-performance-regression/' -RESULT_DIR=$ARTIFACTS_DIR'/report/performance/' +ARTIFACTS_DIR='/tmp/mms-performance-regression' +RESULT_DIR=$ARTIFACTS_DIR'/report/performance' JMETER_PATH='/opt/apache-jmeter-5.3/bin/jmeter' # Start MMS server @@ -24,7 +24,7 @@ pip install bzt # Execute performance test suite and store exit code curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg -./run_performance_suite.py --artifacts-dir=$ARTIFACTS_DIR --jmeter-path=$JMETER_PATH --no-compare-local +./run_performance_suite.py --artifacts-dir=$ARTIFACTS_DIR --jmeter-path=$JMETER_PATH EXIT_CODE=$? # Stop server From c34fb0f47dc8abb01bde8c23bf909feb955e0f8a Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 22 Jun 2020 13:29:25 +0530 Subject: [PATCH 155/329] corrected indentation --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 59bd2a170..b9da2d25a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -135,9 +135,9 @@ jobs: - store_artifacts: name: Store artifacts from performance regression run path: /tmp/mms-performance-regression/ - - store_test_results: - name: Store Taurus test results - path: /tmp/mms-performance-regression/report/ + - store_test_results: + name: Store Taurus test results + path: /tmp/mms-performance-regression/report/ modelarchiver: parameters: From acf7c5200bb039a547f44b271a2873b9aed554a8 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 22 Jun 2020 13:58:37 +0530 Subject: [PATCH 156/329] test with artifact dir in env var --- .circleci/scripts/linux_test_perf_regression.sh | 4 ++-- tests/performance/run_performance_suite.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 170552073..b791b4afe 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -24,7 +24,7 @@ pip install bzt # Execute performance test suite and store exit code curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg -./run_performance_suite.py --artifacts-dir=$ARTIFACTS_DIR --jmeter-path=$JMETER_PATH +./run_performance_suite.py --artifacts-dir=$ARTIFACTS_DIR --jmeter-path=$JMETER_PATH -p health_check.yaml EXIT_CODE=$? # Stop server @@ -32,7 +32,7 @@ multi-model-server --stop >> mms.log 2>&1 # Collect and store test results in result directory to be picked up by CircleCI mkdir -p $RESULT_DIR -cp $ARTIFACTS_DIR'/**/junit.xml' $RESULT_DIR +cp $ARTIFACTS_DIR/$MMS_PERF_ARTIFACTS_DIR_NAME/junit.xml $RESULT_DIR # Exit with the same error code as that of test execution exit EXIT_CODE diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 0ad585275..b945614a8 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -362,6 +362,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam else: artifacts_dir = os.path.abspath(artifacts_dir) artifacts_dir = "{}/{}".format(artifacts_dir, artifacts_folder_name) + os.environ.update({"MMS_PERF_ARTIFACTS_DIR_NAME": artifacts_folder_name}) logger.info("Artifacts will be stored in directory {}".format(artifacts_dir)) with Monitoring(base_file_path, monit): From d007071136a8312d7d40fff552d3080129db44cc Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Mon, 22 Jun 2020 14:04:30 +0530 Subject: [PATCH 157/329] fix reclaim logic Signed-off-by: Dhruva Ray --- tests/performance/agents/metrics/__init__.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index a9ba03570..9014f9e91 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -112,16 +112,7 @@ def update_metric(metric_name, proc_type, stats): processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) for child in children: if psutil.pid_exists(child.pid): - if len(list(filter(lambda parent : parent.pid == server_process.pid, child.parents()))) == 0: - ppids = [str(parent.pid) for parent in child.parents()] - logger.info('server process id is {0}; child process id is {1}; {1} has the following parents {2} : '. - format( - server_process.pid, child.pid, ",".join(ppids) - ) - ) - reclaimed_pids.append(child) - else: - processes_stats.append({'type': ProcessType.WORKER, 'stats' : child.as_dict()}) + processes_stats.append({'type': ProcessType.WORKER, 'stats' : child.as_dict()}) else: reclaimed_pids.append(child) logger.debug('child {0} no longer available'.format(child.pid)) From 3ca4ebd8dfaf988fc287fa8928e3608548d5e87d Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 22 Jun 2020 14:14:23 +0530 Subject: [PATCH 158/329] Remove global variables --- .circleci/scripts/linux_test_perf_regression.sh | 2 +- tests/performance/run_performance_suite.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index b791b4afe..25aa9e81d 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -32,7 +32,7 @@ multi-model-server --stop >> mms.log 2>&1 # Collect and store test results in result directory to be picked up by CircleCI mkdir -p $RESULT_DIR -cp $ARTIFACTS_DIR/$MMS_PERF_ARTIFACTS_DIR_NAME/junit.xml $RESULT_DIR +cp $ARTIFACTS_DIR/**/junit.xml $RESULT_DIR # Exit with the same error code as that of test execution exit EXIT_CODE diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index b945614a8..024c32e19 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -362,7 +362,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam else: artifacts_dir = os.path.abspath(artifacts_dir) artifacts_dir = "{}/{}".format(artifacts_dir, artifacts_folder_name) - os.environ.update({"MMS_PERF_ARTIFACTS_DIR_NAME": artifacts_folder_name}) + # os.environ["MMS_PERF_ARTIFACTS_DIR_NAME"] = artifacts_folder_name logger.info("Artifacts will be stored in directory {}".format(artifacts_dir)) with Monitoring(base_file_path, monit): From d17441526cac2d122f8b1fa0f460013fb0672d67 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Mon, 22 Jun 2020 16:52:56 +0530 Subject: [PATCH 159/329] python executeable --- tests/performance/run_performance_suite.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 0ad585275..c2454edc3 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -81,14 +81,14 @@ def __enter__(self): raise Exception("Server is not running. Pinged url {}. Exiting...".format(server_ping_url)) if self.use: - start_monitoring_server = "python {} --start".format(self.path) + start_monitoring_server = "{} {} --start".format(sys.executable, self.path) run_process(start_monitoring_server, wait=False) time.sleep(2) return self def __exit__(self, type, value, traceback): if self.use: - stop_monitoring_server = "python {} --stop".format(self.path) + stop_monitoring_server = "{} {} --stop".format(sys.executable, self.path) run_process(stop_monitoring_server) From 77a4f09208e52496fbd3f845e27d550784bf3b80 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 22 Jun 2020 17:55:57 +0530 Subject: [PATCH 160/329] logger as 3rd parameter to get_metric --- tests/performance/agents/metrics_monitoring_taurus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/agents/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_taurus.py index 9c78b633b..e6d09242c 100644 --- a/tests/performance/agents/metrics_monitoring_taurus.py +++ b/tests/performance/agents/metrics_monitoring_taurus.py @@ -86,7 +86,7 @@ def _calc_resource_stats(self, interval): result = super()._calc_resource_stats(interval) server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) server_process = get_server_processes(server_pid) - result.update(get_metrics(server_process, get_child_processes(server_process)), self.log) + result.update(get_metrics(server_process, get_child_processes(server_process), self.log)) ''' table = {} From 15ea39fefbfcef3778ef7474f82dd496a76d0262 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Mon, 22 Jun 2020 18:09:41 +0530 Subject: [PATCH 161/329] run perfomance suite --- tests/performance/run_performance_suite.py | 151 +++++++++++---------- 1 file changed, 81 insertions(+), 70 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index c2454edc3..207454858 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -21,8 +21,6 @@ import pathlib import socket import subprocess -import yaml -import requests from agents import configuration import csv import pandas as pd @@ -40,8 +38,6 @@ logger = logging.getLogger(__name__) logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) - -code = 0 metrics_monitoring_server = "agents/metrics_monitoring_server.py" base_file_path = pathlib.Path(__file__).parent.absolute() run_artifacts_path = "{}/run_artifacts/".format(base_file_path) @@ -51,6 +47,9 @@ class Timer(object): + """ + Helper context manager class to capture time diff + """ def __init__(self, description): self.description = description @@ -64,8 +63,9 @@ def __exit__(self, type, value, traceback): def diff(self): return int(time.time()) - self.start + class Monitoring(object): - def __init__(self, path, use = True): + def __init__(self, path, use=True): self.path = "{}/{}".format(path, "agents/metrics_monitoring_server.py") self.use = use @@ -109,7 +109,7 @@ def __exit__(self, type, value, traceback): err_txt = "" if os.path.exists(xunit_file): xml = JUnitXml.fromfile(xunit_file) - for i, suite in enumerate(xml): # tqqdm + for i, suite in enumerate(xml): for case in suite: name = "scenario_{}: {}".format(i, case.name) result = case.result @@ -129,13 +129,9 @@ def __exit__(self, type, value, traceback): self.ts.add_testcase(tc) else: tc = TestCase(self.name) - if code: - tc.result = Error("Suite run failed", "Error") - else: - tc.result = Skipped() + tc.result = Skipped() self.ts.add_testcase(tc) - self.ts.hostname = socket.gethostname() self.ts.timestamp = self.timer.start self.ts.time = self.timer.diff() @@ -147,6 +143,77 @@ def __exit__(self, type, value, traceback): self.junit_xml.add_testsuite(self.ts) +class Compare(): + def __init__(self, artifacts_dir, current_run_name, env_name): + self.artifacts_dir = artifacts_dir + self.current_run_name = current_run_name + self.env_name = env_name + self.result = True + + @staticmethod + def get_latest(names, env_id): + max_ts = 0 + latest_run = '' + for run_name in names: + run_name_list = run_name.split('_') + if env_id == run_name_list[0]: + if int(run_name_list[2]) > max_ts: + max_ts = int(run_name_list[2]) + latest_run = run_name + + return latest_run + + def get_dir_to_compare(self): + parent_dir = pathlib.Path(self.artifacts_dir).parent + names = [di for di in os.listdir(parent_dir) if os.path.isdir(os.path.join(parent_dir, di))] + latest_run = self.get_latest(names, self.env_name) + return os.path.join(parent_dir, latest_run), latest_run + + def store_results(self): + pass + + def compare(self): + compare_dir, compare_run_name = self.get_dir_to_compare() + if compare_dir: + self.result = compare_artifacts(self.artifacts_dir, compare_dir, self.artifacts_dir, + self.current_run_name, compare_run_name) + self.store_results() + return self.resultc1 + + +class LocalCompare(Compare): + pass + + +class S3Compare(Compare): + def get_dir_to_compare(self): + tgt_path = "{}/comp_data".format(self.artifacts_dir) + s3 = boto3.resource('s3') + bucket = s3.Bucket(S3_BUCKET) + result = bucket.meta.client.list_objects(Bucket=bucket.name, + Delimiter='/') + run_names = [] + for o in result.get('CommonPrefixes'): + run_names.append(o.get('Prefix')[:-1]) + + latest_run = self.get_latest(run_names, self.env_name) + if not latest_run: + logger.info("No run found for env_id {}".format(self.env_name)) + return '', '' + + if not os.path.exists(tgt_path): + os.makedirs(tgt_path) + + tgt_path = "{}/{}".format(tgt_path, latest_run) + run_process("aws s3 cp s3://{}/{} {} --recursive".format(bucket.name, latest_run, tgt_path)) + + return tgt_path, latest_run + + def store_results(self): + run_process("aws s3 cp {} s3://{}/{} --recursive".format(self.artifacts_dir, S3_BUCKET, + self.current_run_name)) + + def run_process(cmd, wait=True): print("running command : {}".format(cmd)) @@ -166,54 +233,6 @@ def run_process(cmd, wait=True): p = subprocess.Popen(cmd, shell=True) return p.returncode, '' - -def get_folder_names(dir1): - return [di for di in os.listdir(dir1) if os.path.isdir(os.path.join(dir1, di))] - - -def get_latest(names, env_id): - max_ts = 0 - latest_run = '' - for run_name in names: - run_name_list = run_name.split('_') - if env_id == run_name_list[0]: - if int(run_name_list[2]) > max_ts: - max_ts = int(run_name_list[2]) - latest_run = run_name - - return latest_run - - -def get_latest_dir(dir1, env_id): - names= get_folder_names(dir1) - latest_run= get_latest(names, env_id) - return os.path.join(dir1, latest_run), latest_run - - -def download_s3_files(env_id, tgt_path, bucket_name=S3_BUCKET): - s3 = boto3.resource('s3') - bucket = s3.Bucket(bucket_name) - result = bucket.meta.client.list_objects(Bucket=bucket.name, - Delimiter='/') - - run_names = [] - for o in result.get('CommonPrefixes'): - run_names.append(o.get('Prefix')[:-1]) - - latest_run = get_latest(run_names, env_id) - if not latest_run: - logger.info("No run found for env_id {}".format(env_id)) - return '', '' - - if not os.path.exists(tgt_path): - os.makedirs(tgt_path) - - tgt_path = "{}/{}".format(tgt_path, latest_run) - run_process("aws s3 cp s3://{}/{} {} --recursive".format(bucket.name, latest_run, tgt_path)) - - return tgt_path, latest_run - - def get_sub_dirs(dir, exclude_list=['comp_data']): dir = dir.strip() if not os.path.exists(dir): @@ -383,17 +402,9 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam junit_xml.write(junit_xml_path) run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) - if compare_local: - compare_dir, run_name = get_latest_dir(pathlib.Path(artifacts_dir).parent, env_name) - else: - compare_dir, run_name = download_s3_files(env_name, "{}/comp_data".format(artifacts_dir)) - - compare_result = True - if compare_dir: - compare_result = compare_artifacts(artifacts_dir, compare_dir, artifacts_dir, artifacts_folder_name, run_name) - - if not compare_local: - run_process("aws s3 cp {} s3://{}/{} --recursive".format(artifacts_dir, S3_BUCKET, artifacts_folder_name)) + compare_class = LocalCompare if compare_local else S3Compare + compare_obj = compare_class(artifacts_dir, artifacts_folder_name, env_name) + compare_result = compare_obj.compare() if junit_xml.errors or junit_xml.failures or junit_xml.skipped: sys.exit(3) From f02cde6b03093d47bdbbb49c7d671f113225052f Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 22 Jun 2020 18:35:02 +0530 Subject: [PATCH 162/329] Tweaked register/unregister to capture total processes --- .../performance/tests/register_unregister.jmx | 8 +----- .../tests/register_unregister.yaml | 28 ++++--------------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/tests/performance/tests/register_unregister.jmx b/tests/performance/tests/register_unregister.jmx index 5031f750d..c5ae18180 100644 --- a/tests/performance/tests/register_unregister.jmx +++ b/tests/performance/tests/register_unregister.jmx @@ -26,12 +26,6 @@ URL to model store on s3 = - - initial_workers - 1 - Number of initial workers to create - = - @@ -71,7 +65,7 @@ ${__P(management_port,8444)} - /models?url=${model_url}&initial_workers=${initial_workers}&synchronous=true + /models?url=${model_url} POST true false diff --git a/tests/performance/tests/register_unregister.yaml b/tests/performance/tests/register_unregister.yaml index 3080cf875..69a7cd36b 100644 --- a/tests/performance/tests/register_unregister.yaml +++ b/tests/performance/tests/register_unregister.yaml @@ -1,12 +1,12 @@ --- execution: - concurrency: 1 - ramp-up: 1s + ramp-up: 0s iterations: 5 - scenario: scaledown + scenario: register_unregister scenarios: - scaledown: + register_unregister: script: register_unregister.jmx modules: @@ -18,31 +18,15 @@ services: ServerLocalClient: - interval: 1s metrics: - - total_workers - - sum_workers_file_descriptors - - orphans + - total_processes reporting: - module: passfail criteria: - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers + subject: ServerLocalClient/total_processes condition: '>' threshold: 1 - timeframe: 1s - stop : true - fail : true - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_workers_file_descriptors - condition: '>' - threshold: 11 - timeframe: 1s - stop : true - fail : true - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/orphans - condition: '>' - threshold: 0 - timeframe: 1s + timeframe: 5s stop : true fail : true \ No newline at end of file From d4aeae44d308a976790a30789e5ca6b8e78e6393 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 22 Jun 2020 19:23:51 +0530 Subject: [PATCH 163/329] frontend's resident set size memory --- tests/performance/tests/register_unregister.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/performance/tests/register_unregister.yaml b/tests/performance/tests/register_unregister.yaml index 69a7cd36b..5917b5a76 100644 --- a/tests/performance/tests/register_unregister.yaml +++ b/tests/performance/tests/register_unregister.yaml @@ -19,6 +19,7 @@ services: - interval: 1s metrics: - total_processes + - frontend_memory_rss reporting: - module: passfail @@ -29,4 +30,11 @@ reporting: threshold: 1 timeframe: 5s stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/frontend_memory_rss + condition: '>' + threshold: 75000000 #75MB + timeframe: 5s + stop : true fail : true \ No newline at end of file From 1f5be2b3d39352cee5ca5053ed54de49b85c0501 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 22 Jun 2020 23:25:11 +0530 Subject: [PATCH 164/329] revert changes to metrics module --- tests/performance/agents/metrics/__init__.py | 25 ++++++------------- tests/performance/agents/metrics_collector.py | 2 +- .../agents/metrics_monitoring_taurus.py | 2 +- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index 9014f9e91..a802f099e 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -81,14 +81,13 @@ class ProcessType(Enum): for op in list(operators): AVAILABLE_METRICS.append('{}_{}_{}'.format(op, PNAME, metric)) -children = set() +__CHILDREN = set() -def get_metrics(server_process, child_processes, logger): +def get_metrics(server_process, child_processes): """ Get Server processes specific metrics """ result = {} - children.update(child_processes) - logger.debug("children : {0}".format(",".join([str(c.pid) for c in children]))) + __CHILDREN.update(child_processes) def update_metric(metric_name, proc_type, stats): stats = stats if stats else [0] @@ -107,18 +106,9 @@ def update_metric(metric_name, proc_type, stats): # as_dict() gets all stats in one shot processes_stats = [] - reclaimed_pids = [] - processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) - for child in children: - if psutil.pid_exists(child.pid): - processes_stats.append({'type': ProcessType.WORKER, 'stats' : child.as_dict()}) - else: - reclaimed_pids.append(child) - logger.debug('child {0} no longer available'.format(child.pid)) - - for p in reclaimed_pids: - children.remove(p) + for process in child_processes: + processes_stats.append({'type': ProcessType.WORKER, 'stats' : process.as_dict()}) ### PROCESS METRICS ### worker_stats = list(map(lambda x: x['stats'], \ @@ -132,9 +122,10 @@ def update_metric(metric_name, proc_type, stats): update_metric(k, ProcessType.ALL, list(map(process_metrics[k], all_stats))) update_metric(k, ProcessType.FRONTEND, list(map(process_metrics[k], server_stats))) + # Total processes - result['total_processes'] = len(worker_stats) + 1 - result['total_workers'] = max(len(worker_stats) - 1, 0) + result['total_processes'] = len(child_processes) + 1 + result['total_workers'] = max(len(child_processes) - 1, 0) result['orphans'] = len(list(filter(lambda p : p['ppid'] == 1, worker_stats))) ### SYSTEM METRICS ### diff --git a/tests/performance/agents/metrics_collector.py b/tests/performance/agents/metrics_collector.py index 4d6720db8..845c606d9 100755 --- a/tests/performance/agents/metrics_collector.py +++ b/tests/performance/agents/metrics_collector.py @@ -100,7 +100,7 @@ def monitor_processes(server_process, metrics, interval, socket): """ while True: message = [] - collected_metrics = get_metrics(server_process, get_child_processes(server_process), logger) + collected_metrics = get_metrics(server_process, get_child_processes(server_process)) #table = {} for metric in metrics: message.append(str(collected_metrics.get(metric, 0))) diff --git a/tests/performance/agents/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_taurus.py index e6d09242c..12d426ebe 100644 --- a/tests/performance/agents/metrics_monitoring_taurus.py +++ b/tests/performance/agents/metrics_monitoring_taurus.py @@ -86,7 +86,7 @@ def _calc_resource_stats(self, interval): result = super()._calc_resource_stats(interval) server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) server_process = get_server_processes(server_pid) - result.update(get_metrics(server_process, get_child_processes(server_process), self.log)) + result.update(get_metrics(server_process, get_child_processes(server_process))) ''' table = {} From 1003bea85095473c5c3b46e2bef2961785ad2687 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 22 Jun 2020 23:26:13 +0530 Subject: [PATCH 165/329] add FDs count to reg_unreg scenario --- tests/performance/tests/register_unregister.yaml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/performance/tests/register_unregister.yaml b/tests/performance/tests/register_unregister.yaml index 5917b5a76..c7b2d9a06 100644 --- a/tests/performance/tests/register_unregister.yaml +++ b/tests/performance/tests/register_unregister.yaml @@ -19,6 +19,7 @@ services: - interval: 1s metrics: - total_processes + - sum_all_file_descriptors - frontend_memory_rss reporting: @@ -32,9 +33,16 @@ reporting: stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/frontend_memory_rss + subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 75000000 #75MB + threshold: 66 timeframe: 5s stop : true - fail : true \ No newline at end of file + fail : true +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/frontend_memory_rss +# condition: '>' +# threshold: 75000000 #75MB +# timeframe: 5s +# stop : true +# fail : true \ No newline at end of file From a4c4072ea5023984f71e553f4be4562fc79b2d0e Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 22 Jun 2020 19:34:54 +0000 Subject: [PATCH 166/329] fix bugs --- tests/performance/agents/metrics_collector.py | 2 +- .../agents/metrics_monitoring_taurus.py | 2 +- tests/performance/run_performance_suite.py | 58 +++++++++++++++---- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/tests/performance/agents/metrics_collector.py b/tests/performance/agents/metrics_collector.py index 4d6720db8..f1cd51fc9 100755 --- a/tests/performance/agents/metrics_collector.py +++ b/tests/performance/agents/metrics_collector.py @@ -108,7 +108,7 @@ def monitor_processes(server_process, metrics, interval, socket): # table[metric] = [collected_metrics.get(metric, 0)] - message = "\t".join(message)+"\n" + message = "\t".join(message)+"\t\n" #logger.info("\n{}".format(tabulate(table, headers=table.keys(), tablefmt="pretty"))) if socket: diff --git a/tests/performance/agents/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_taurus.py index 9c78b633b..e6d09242c 100644 --- a/tests/performance/agents/metrics_monitoring_taurus.py +++ b/tests/performance/agents/metrics_monitoring_taurus.py @@ -86,7 +86,7 @@ def _calc_resource_stats(self, interval): result = super()._calc_resource_stats(interval) server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) server_process = get_server_processes(server_pid) - result.update(get_metrics(server_process, get_child_processes(server_process)), self.log) + result.update(get_metrics(server_process, get_child_processes(server_process), self.log)) ''' table = {} diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 207454858..db5d98c5f 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -21,6 +21,7 @@ import pathlib import socket import subprocess +import shutil from agents import configuration import csv import pandas as pd @@ -151,12 +152,12 @@ def __init__(self, artifacts_dir, current_run_name, env_name): self.result = True @staticmethod - def get_latest(names, env_id): + def get_latest(names, env_id, exclude_run_name): max_ts = 0 latest_run = '' for run_name in names: run_name_list = run_name.split('_') - if env_id == run_name_list[0]: + if env_id == run_name_list[0] and run_name != exclude_run_name: if int(run_name_list[2]) > max_ts: max_ts = int(run_name_list[2]) latest_run = run_name @@ -166,7 +167,7 @@ def get_latest(names, env_id): def get_dir_to_compare(self): parent_dir = pathlib.Path(self.artifacts_dir).parent names = [di for di in os.listdir(parent_dir) if os.path.isdir(os.path.join(parent_dir, di))] - latest_run = self.get_latest(names, self.env_name) + latest_run = self.get_latest(names, self.env_name, self.current_run_name) return os.path.join(parent_dir, latest_run), latest_run def store_results(self): @@ -178,7 +179,7 @@ def compare(self): self.result = compare_artifacts(self.artifacts_dir, compare_dir, self.artifacts_dir, self.current_run_name, compare_run_name) self.store_results() - return self.resultc1 + return self.result class LocalCompare(Compare): @@ -196,7 +197,7 @@ def get_dir_to_compare(self): for o in result.get('CommonPrefixes'): run_names.append(o.get('Prefix')[:-1]) - latest_run = self.get_latest(run_names, self.env_name) + latest_run = self.get_latest(run_names, self.env_name, self.current_run_name) if not latest_run: logger.info("No run found for env_id {}".format(self.env_name)) return '', '' @@ -242,6 +243,18 @@ def get_sub_dirs(dir, exclude_list=['comp_data']): return list([x for x in os.listdir(dir) if os.path.isdir(dir + "/" + x) and x not in exclude_list]) +def get_mon_metrics_list(test_yaml): + metrics = [] + with open(test_yaml) as test_yaml: + test_yaml = yaml.safe_load(test_yaml) + for rep_section in test_yaml.get('services', []): + if rep_section.get('module', None) == 'monitoring' and "server-agent" in rep_section: + for mon_section in rep_section.get('server-agent', []): + if isinstance(mon_section, dict): + metrics.extend(mon_section.get('metrics', [])) + + return metrics + def get_compare_metric_list(dir, sub_dir): diff_percents = [] metrics = [] @@ -285,9 +298,11 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): logger.info("Metrics monitoring logs are not captured for {} in either of the runs.".format(sub_dir1)) rows.append([run_name1, run_name2, sub_dir1, "log_file", sub_dir1, sub_dir1, "metrics are not captured for either of the runs", "pass"]) continue + metrics_from_file1 = pd.read_csv(metrics_file1[0]) metrics_from_file2 = pd.read_csv(metrics_file2[0]) + metrics, diff_percents = get_compare_metric_list(dir1, sub_dir1) for col, diff_percent in zip(metrics, diff_percents): for agg_func in aggregates: @@ -396,6 +411,16 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam s.code, s.err = run_process("{} bzt {} {} {}".format(pre_command, options_str, test_file, GLOBAL_CONFIG_PATH)) + metrics_log_file = glob.glob("{}/SAlogs_*".format(suite_artifacts_dir)) + if metrics_log_file: + metrics = get_mon_metrics_list(test_file) + if metrics: + with open(metrics_log_file[0]) as from_file: + line = from_file.readline() + with open(metrics_log_file[0], mode="w") as to_file: + to_file.write(','.join(line.split(',')[0:1] + metrics)+"\n") + shutil.copyfileobj(from_file, to_file) + junit_xml.update_statistics() junit_xml_path = '{}/junit.xml'.format(artifacts_dir) junit_html_path = '{}/junit.html'.format(artifacts_dir) @@ -405,12 +430,25 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam compare_class = LocalCompare if compare_local else S3Compare compare_obj = compare_class(artifacts_dir, artifacts_folder_name, env_name) compare_result = compare_obj.compare() - - if junit_xml.errors or junit_xml.failures or junit_xml.skipped: - sys.exit(3) - + + logger.info("\n\nResult Summary:") + exit_code = 0 if not compare_result: - sys.exit(4) + compare_status = 'failed' + exit_code = 4 + else: + compare_status = 'passed' + logger.info("Comparison with monitoring metrics of previous run has {}.".format(compare_status)) + + if junit_xml.errors or junit_xml.failures or junit_xml.skipped: + suite_status = 'failed' + exit_code = 3 + else: + suite_status = 'passed' + logger.info("Test suite run has {}.".format(suite_status)) + logger.info("Test suite report - {}/junit.html".format(artifacts_dir)) + + sys.exit(exit_code) if __name__ == "__main__": From b5a070a3dcf197c5e65dd71dd5cd1b6d9c4d38da Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Tue, 23 Jun 2020 12:17:39 +0530 Subject: [PATCH 167/329] dump metrics as tests are running, filter out the none stats from zombies Signed-off-by: Dhruva Ray --- tests/performance/agents/metrics/__init__.py | 27 +++++++++++++------ tests/performance/agents/metrics_collector.py | 11 ++++---- .../agents/metrics_monitoring_taurus.py | 10 +++---- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index a802f099e..ed27cd037 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -81,16 +81,18 @@ class ProcessType(Enum): for op in list(operators): AVAILABLE_METRICS.append('{}_{}_{}'.format(op, PNAME, metric)) -__CHILDREN = set() +children = set() -def get_metrics(server_process, child_processes): +def get_metrics(server_process, child_processes, logger): """ Get Server processes specific metrics """ result = {} - __CHILDREN.update(child_processes) + children.update(child_processes) + logger.debug("children : {0}".format(",".join([str(c.pid) for c in children]))) def update_metric(metric_name, proc_type, stats): stats = stats if stats else [0] + stats = list(filter(lambda x: isinstance(x, int) or isinstance(x, float), stats)) if proc_type == ProcessType.WORKER: proc_name = 'workers' @@ -106,9 +108,18 @@ def update_metric(metric_name, proc_type, stats): # as_dict() gets all stats in one shot processes_stats = [] + reclaimed_pids = [] + processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) - for process in child_processes: - processes_stats.append({'type': ProcessType.WORKER, 'stats' : process.as_dict()}) + for child in children: + if psutil.pid_exists(child.pid): + processes_stats.append({'type': ProcessType.WORKER, 'stats' : child.as_dict()}) + else: + reclaimed_pids.append(child) + logger.debug('child {0} no longer available'.format(child.pid)) + + for p in reclaimed_pids: + children.remove(p) ### PROCESS METRICS ### worker_stats = list(map(lambda x: x['stats'], \ @@ -122,10 +133,9 @@ def update_metric(metric_name, proc_type, stats): update_metric(k, ProcessType.ALL, list(map(process_metrics[k], all_stats))) update_metric(k, ProcessType.FRONTEND, list(map(process_metrics[k], server_stats))) - # Total processes - result['total_processes'] = len(child_processes) + 1 - result['total_workers'] = max(len(child_processes) - 1, 0) + result['total_processes'] = len(worker_stats) + 1 + result['total_workers'] = max(len(worker_stats) - 1, 0) result['orphans'] = len(list(filter(lambda p : p['ppid'] == 1, worker_stats))) ### SYSTEM METRICS ### @@ -137,4 +147,5 @@ def update_metric(metric_name, proc_type, stats): result['system_read_bytes'] = system_disk_io_counters.read_bytes result['system_write_bytes'] = system_disk_io_counters.write_bytes + return result diff --git a/tests/performance/agents/metrics_collector.py b/tests/performance/agents/metrics_collector.py index 75feb2ccf..23704886d 100755 --- a/tests/performance/agents/metrics_collector.py +++ b/tests/performance/agents/metrics_collector.py @@ -100,16 +100,15 @@ def monitor_processes(server_process, metrics, interval, socket): """ while True: message = [] - collected_metrics = get_metrics(server_process, get_child_processes(server_process)) - #table = {} + collected_metrics = get_metrics(server_process, get_child_processes(server_process), logger) + metrics_msg = [] for metric in metrics: message.append(str(collected_metrics.get(metric, 0))) - #if collected_metrics.get(metric) is not None: - # table[metric] = [collected_metrics.get(metric, 0)] - + if collected_metrics.get(metric) is not None: + metrics_msg.append("{0} : {1}".format(metric, collected_metrics.get(metric, 0))) message = "\t".join(message)+"\t\n" - #logger.info("\n{}".format(tabulate(table, headers=table.keys(), tablefmt="pretty"))) + logger.info("{}".format("{0}".format(" -- ".join(metrics_msg)))) if socket: try: diff --git a/tests/performance/agents/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_taurus.py index 12d426ebe..40451d4eb 100644 --- a/tests/performance/agents/metrics_monitoring_taurus.py +++ b/tests/performance/agents/metrics_monitoring_taurus.py @@ -86,14 +86,12 @@ def _calc_resource_stats(self, interval): result = super()._calc_resource_stats(interval) server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) server_process = get_server_processes(server_pid) - result.update(get_metrics(server_process, get_child_processes(server_process))) + result.update(get_metrics(server_process, get_child_processes(server_process), self.log)) - ''' - table = {} + metrics_msg = [] for key in self.metrics: if result.get(key) is not None: - table[key] = [result[key]] - self.log.info("\n{0}".format(tabulate(table, headers=table.keys(), tablefmt="pretty"))) - ''' + metrics_msg.append("{0} : {1}".format(key, result[key])) + self.log.info("{0}".format(" -- ".join(metrics_msg))) return result From ac24c427f71675c7eb20f4c02a443857d5b7fe60 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 15:15:55 +0530 Subject: [PATCH 168/329] Start Stop MMS server in TCs --- tests/performance/run_performance_suite.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index db5d98c5f..1faff5336 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -71,15 +71,18 @@ def __init__(self, path, use=True): self.use = use def __enter__(self): - with open(GLOBAL_CONFIG_PATH) as conf_file: - global_config = yaml.safe_load(conf_file) - server_props = global_config["modules"]["jmeter"]["properties"] - server_ping_url = "{}://{}:{}/ping".format(server_props["protocol"], server_props["hostname"], - server_props["port"]) - try: - requests.get(server_ping_url) - except requests.exceptions.ConnectionError: - raise Exception("Server is not running. Pinged url {}. Exiting...".format(server_ping_url)) + # Commented as - + # START \ STOP of MMS server is handled in individual scenario's prepare \ post-process stages + # + # with open(GLOBAL_CONFIG_PATH) as conf_file: + # global_config = yaml.safe_load(conf_file) + # server_props = global_config["modules"]["jmeter"]["properties"] + # server_ping_url = "{}://{}:{}/ping".format(server_props["protocol"], server_props["hostname"], + # server_props["port"]) + # try: + # requests.get(server_ping_url) + # except requests.exceptions.ConnectionError: + # raise Exception("Server is not running. Pinged url {}. Exiting...".format(server_ping_url)) if self.use: start_monitoring_server = "{} {} --start".format(sys.executable, self.path) From 06e6ae294f177d48f28826ad90177d895a3eb792 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 15:16:19 +0530 Subject: [PATCH 169/329] reg_unreg: avg-rt, --- tests/performance/tests/register_unregister.jmx | 4 ++-- tests/performance/tests/register_unregister.yaml | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/performance/tests/register_unregister.jmx b/tests/performance/tests/register_unregister.jmx index c5ae18180..504c3ae8c 100644 --- a/tests/performance/tests/register_unregister.jmx +++ b/tests/performance/tests/register_unregister.jmx @@ -57,7 +57,7 @@ true - + @@ -76,7 +76,7 @@ - + diff --git a/tests/performance/tests/register_unregister.yaml b/tests/performance/tests/register_unregister.yaml index c7b2d9a06..8bb7f0ff7 100644 --- a/tests/performance/tests/register_unregister.yaml +++ b/tests/performance/tests/register_unregister.yaml @@ -14,6 +14,12 @@ modules: class : metrics_monitoring_taurus.Monitor services: + - module: shellexec + prepare: + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 10s" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" - module: server_local_monitoring ServerLocalClient: - interval: 1s @@ -25,6 +31,12 @@ services: reporting: - module: passfail criteria: + # Inbuilt Criteria + - success of RegisterModel<100%, stop as failed + - success of UnregisterModel<100%, stop as failed + - avg-rt of RegisterModel>15s, stop as failed + - avg-rt of UnregisterModel>10ms, stop as failed + # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' From 5b506ed0404229deb4f4861790707d5c37ed5a24 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 16:07:56 +0530 Subject: [PATCH 170/329] enable logging of local metrics --- tests/performance/tests/register_unregister.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/performance/tests/register_unregister.yaml b/tests/performance/tests/register_unregister.yaml index 8bb7f0ff7..74aa065fd 100644 --- a/tests/performance/tests/register_unregister.yaml +++ b/tests/performance/tests/register_unregister.yaml @@ -23,6 +23,7 @@ services: - module: server_local_monitoring ServerLocalClient: - interval: 1s + logging : True metrics: - total_processes - sum_all_file_descriptors From e3e3ffa1a48e92dae977e8102c237a918acba73a Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 16:24:08 +0530 Subject: [PATCH 171/329] download kitten as part of tc --- tests/performance/kitten.jpg | Bin 110969 -> 0 bytes .../performance/tests/register_unregister.yaml | 2 ++ 2 files changed, 2 insertions(+) delete mode 100644 tests/performance/kitten.jpg diff --git a/tests/performance/kitten.jpg b/tests/performance/kitten.jpg deleted file mode 100644 index ffcd2be2c674fddc4886117c12f3b29e4367aba8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110969 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

J66l>z7T20v&BrMUK^shcTlD$)2!& z5`D6QGPrTQv=zN=2yoX7`b0JZ<7eEw>PtzYQ0@7e|0=C)kk-{g3ZpRi)C^3!!<8%g z!&_L7FU-EiMfl4VX6cSGvAF_JKmka$3~V94p7gGvDe+H2qRvIIESKyy_tU1ewy`ntT`-A zm_UBg*vY8U?MMe_7Uy?s^ax1=NK1B&p>4w-mJ22v;h` zu6QF1;AMlTUPtg=DTq5Q4ilqNvW=7qt*aJlMz>MUgUQ|C^x8!=q3pL5qt;qSa6ad= zO~+w8y<)J~iAeegu7H(*wqRl~a#k}@5H;HBI|}R4%EV;+88jwmJneP3u)fGM!qqW~9qqvtrMC&5c@Vt-e><%2E{hc8W2j9nXpE zSeH}W-kCKTdV}0yR39>}Kj3j%KR=+&h_lT)O_fwROc@d*hRVb_S5>QM+7Fd`d0WkU zYwk~22hfs(={SaTx6~nW4$)&#akaWUu{^j#??-=lh&%1B9Tmi?Z=fS_4Vsrt>)O59 z(LXb<{S*b=v_Zhs##=bP*@r*%*c99$tFP!umy=wW6}C$c1}V&X+N4!1zOXU8d#7Sy zTY_c3cEAD5At+4#?VD99TXwoMbh4~9#SLl7Sa{=TE3&XMF^Dhvyb&h<=CsQ!QKXo7 z)18YnNKVe3!*{@?Mh=0pG? z`$qW`Q?+c*<3ErSX;!QKZb{JbFFErH3D zj84IjTP*^;F8_m-4kk8NGcMkh@6x~Jv1Z5Z5QTXzljP=KpH2 zs+M92lq;2S`>B4&>(!q4eW~aEBGP3_XWh&_3}83oV_ovBHkN(0P8GDi2Ap?$kAmne7&I#_)dNHkkzC{`1%QPk&O1rsd zUbhYT{U*5;6!oAJ38VB$mkvCFoSE$Dd6qQtqX7PHDcW&uOp6fXlPXNO43~`fl_`%8p2gNG+Bi8I#aoU!$;gt*+9#YT8Lt%Mj$frL@qyQWp1$eUI&FU ze1rAjve}byr0*pzW%4Rg-!i*vJm2ifV=Dd0aPS=|A}iy!sbiv(CI2I=Hs=uX-PPz$ zlFdIxZX*5aE14im+3%Ez0I_ zg_#CFg@mxU5*9Wi5TY>-3vrm)vKN*JUX+L;;71CMeL>KL)bn(n~$&c=3jD za3-?Ms&--&a5M0zm(DpMlZ5kVx^`mSa_lGDkSwh-?F@$Abk^E-=GOCA05hX#pa3%p zrC8^9ivn)qmU@PNx)%1^kQ#kxY^DyTVh)(y_=At=1RqduHofQ-E?SZ1P>V4A(w84*aYEy*c3EQAqM&)!X&O#eqbu6~>NBPS*(f#gxmnZonDAlT>-6WKU(Vsw zpi^(1Q-Y}}%XdLX4<=j6FeZ3bGdK_Er}*3P@eI(R$3$O9W8~BtiH-}avO?vrx$W_7 zUkHC|-A`Jvve^`S+&tYujkF4Sy2850t0hR+Sv=)jdh64}?q0c<)G*EY_ePG4zP0Iq z6S8g1Pu?ZGF+Ya)etxd7N-wN`67WW|q(LC++VKoN=9NRkgE#kv8=fdCYjT3c>9Tt& zM@w7ghcd7N1wTn1S$&*166H7Ewi2vq4~S@ld|7??Jm9T-nf&Z?daOPf2GUI^tp@%! zV_#=tFS2<_e3bO?4#$s2_X5z>D|eC$%Z!hhcINds;Q=J*b4MR-2boaks@af1Whr~& zfH#1-vPG*=u9`2oYT^1vdp2t5QrY`QRiV{ok1LK(4AhzovP+e3&Py@gg2}{0*OX?; zpxM^GX)zSf)^`{;@rd23Wy|}+SV&YU_N!3Lj*|i39F6C$nBa zjtTIE#>iX-9NsXR2@P6$ged;3@Rk)mnoyfL>%o+jp6T;_1WRMqrK>;=EZt7}eXz|# z#AjAm*Di{jKz64%R#cmv9p+$quw6B-RPfQ!=O2hZtEm5h7ZE!VYLH1uj=%4SM&$i2F09$2^!%e zl&?%SzAd>eqtriRrXM%I0*iWEOrSA96CpoD^X9$E-;7<>z;Ke{+&wqN?e!v2b+g}7 zqg!)1wd(NQ=8MjU(<+Om8Gi0o?=gN99wG>UgAvz9*JpOa0y)-y^A3#QCbFSYc_T7{=A+G4@;e?D5J zxRJ*n9^@=^$3}-?_Y4pN&n1q(p!;ayjnjwQF+S>lHhA`CC$=JQkA30}HIy1l=cT+{B?K5AHkwM0C(Fxu;Uvm{ZT)>Dr_!t&ny4`v-_d;l%`#h$k z(YI9UeOilLnMf0;Z%E+f;v~ApgDKQ~MSZp+ZPU!stbnAcNN2dcPsH_`MNG`}^=$Fv zETYL^qMl2^O)+1iFHbOTW~3%Za(5iw-r!=Y;jMyHE}(1omIm;#^Y5x@1y#PliW{+n zqHG7kzD=Nql3A>l?}DHgn0hOyXhof)FRi}notWEA73*y0;kx^wNKkhmiZCN)gZ)q` z%$toGlJJmof;l&gJ^I*|6efEdpQ z(|O%PXVZWqVjC0u`+Vz2q=4*@uNp9GovPL=lH@O-5%Z>_rD2lW>!3DVX&q%iQQ5Q= zSDG#xL(&CZ8x5PpPYkrv?B&c!Tu2h={NB!Z)*o0~<)g}v87iZz4QWBPdh^1eI@vlU zZB&IT?+nj4cGV&s5Lpdz_fES)GDIyQZX?bpQ7cmglO5YKhyn=+dLg!MlSAyl64MEY zM51nBpp_o1s8Ff&WN1LlQeigh-L9!#0ecPo3(uB_=D;7EdH|iMG5T6ixFHprOeR9v z5Br+uJAs#>toh^AH}2N=v^fgbXAEFXnNi$#7SrV#k8u|r8ae#jR~O#SpQbpocUmZ(m+J5 zps3yn=iyuxxKB}+jxD#@@-G0_uVLkFrtkcTF^RY^^=xDLL4NXg8u)cgg-B=rOO|v6 zNFCaOLXQH3z05wL(ucPkebf$6)&9Z8_Ci8GyvZO0$>G_jlN8UF48edo`V65!&qr** ztj;Qn`iUkkBwk8&qOAG7k5&}6b&yFezm(ljnI5jxNPA^rq?a{D$KKD#j^6fn^!$cGmB1SQBA0;IBVtO@mRTyK&}sj z1ejVmIoSId3SLz!DLuCMC>R;PTUmiIdQY0B3vWqTqAs2ohoT zO8UVZAHR=3HS0XqaznX zjj3E$t^Kqitc?nKUg1L2E5Q^+Yh)wxbbPa&;jpXNs;mDEO3l1*l^PT|YMfFcyf@k~ zz5J_WT5l;yYek?!_gJ~X2BA^)7r>y?BDd0KWX`6SdUus8>*{Pea{9xJ1>33G;fdT! zac3&X_NJkZ3;64=<5Ki-37>#_e<=+d>ra@}#QKh!;TvA<3KKw>?Tp0^M? zPpZk4Apnx3VvYK8dq%a2PsV-V3AKK%PhO5r z9t*!O?=kZ9#A5FUs2y1$G;T0_Zi%+Q_dK^e!D2z?|1ifgq>&*BjtT=&$IwA9Ki_P zklm8QjZtK4-!b?&Y@sD9Qdy7xSkOaGMcuVx>mUGNcybKx(D^n@mzv_%^@R94r`hX= z_E^0aD=(rPYyH}KlN()p#%dTosPes7FhKkDpCwD}n7y--W+dAut=iUgVEV6I=IBH7 zxrTj0TGe6sBbzTqw6xiZaDtmR?log za$=*l(hJ!Od1!{x-iU-82TO`G^$@*cTUryzYp0&2#}~~z=bR;-JLn;fglS~&LgtRi zM*jksz0H1$UE}}$hUJNY{tM6aCqjfbtecK;6~dnCiZX+jqN8UY%W4bQ-)q3_)k6h` zL`@dUoJgER1)?!nHB6NHRJ_3C$N|VkLfXvw)NU+rW%+`L?nmLCrmj%HR1osUh9Exf zUW+rCCFRw*&o}gy%HLF_{slZ82r0WOm|k}|!p964R;vC%$HkmYk{TMEe+CxKZ9O*I zX$LP#fUb-ct|Ev+T0I-kfz#Ykt( z`#?|X3~!uaii`*9b`N|%f9t>1>b~{Zuxc}|fk(!%wJDx4=r3S`5Jh;Ih5t0LQ-5Bj zqFuo`-gTkv#ymw_^nLp$(^7*sZsNKr<^3{I$jLzl@-Kj05&Y2Z{BEU$FXyutp2QGM zkdcWqObxhjqUyHOvssc&1ilIv>#G@DQ48z}Ot^)Fl|Q5i+d?())`DM3wEH|9Y~sJy zifcAvq=lw@REoHNSFGY^ME5Jy`_F`z?)o;8ikU6!b&%cDOw3=vFgWi?zynY{8Zv`K zk38=fGV8poQuwpkFsO`~wcgV?Tn0DK{?x<_fGt&66N;beY+ks|tguPMS5Rd)>aUyC;{(Ln?hWj7HDc=ZuN_-mQU+wx-f2z_I}bhU61tdcC1XqF8H zwp1IPWUkSk$>8L}tlAtj5RA``#Mr1`yV5ZRt~iaS8#9iEW5vXyyD!<2+^~W&4I8rF{lS^Bb8iFU>0M7|U!-|5Ekl z0qaw1WJYYY?!W->5;Mwr`|S%+(O!tZ+9E=ff=3hCn`UD zgbkQyZZK=tuXSVEE0nNQ*49bW2|E0$QlPf9R5?6X07f*G#pXnD_T$Ku*WfuN)uMWX zhqV#~31j25nne?I5$1`Oivk%JcPn2hD<(=!%uvG#bA|VZ5KOA@QURF$O3{qn0BT|; zfE;9GED$!Hzp?^x6LXHFyBYNJ;RsRJcTz=6y*S3SG~G0#leSMR^>BL$ZoS9fx`2#A zq$6NcmrpAou!mWwj%UBO-&dXGq!5;puFY(OzqX&s7IZUBz)-t#3W|Za9#<5sS2w-W z^Oe29YQm6_nJHUkIe50^o--G6gs@#^)YN z9lWT?3;7ctFNXFPj`#AHEbYqiW;h8gK)Qn5;frpjt;w;6oZinTc&{&r8qBhu%eMf; z>(ig)j_ivN$CCrOQZ_#CjV#vb{h?{PO9)!&$4;cyI|#a^vwo4(LmqBiiUZixglS0l zR&u|nNc^$Aao%>U(C#5Y(OaY79b7qty7_EkX_{P;n(J->b9Ay$tF$W1pNqeMuJ6P> zkq*OO$Fc>9yd0rcdcG4sjtKlTI#2W5l*~;5eeo3@g2Fy=Q3Xf?hj8e%()ZJ+8`ur~ zw#GS8_kL?pXAPZ2jXaB?7OpsPN8X0=ilSSe^Lu{`{M`nQ{Kg_JW! zAQhoTrY9_k(1Oc}P#XtNwO8IbQ@@IGUH~NsYdK?OB#YSkX@ZUqBO(8X3Q}>~oa3!& z!RTFmC4+GOps4d9-Te~%H2&rXeY1kKx@Nx8u&jHC!CKc#`gMjO8ILaLlm1s)8PsBKHh+^7ey)}GC!d06-kUGNBF)fQC^g!SSzVtvD znPpsnZ?>V)FNwGu3H&TOvz+{-XD~xvEPd8<3+)Q#7YHJErm_wC`XpuFZ4yPiYu8V4b!IjVv!YD+v#z1E@apxts32jk4fim5j!oB=(FZO?ou!UZY}d

hPAkbbTr(^R-tMK2 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%0DL;n=L?~l?ng&I8MPm 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 z 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%0D>0jKMROl@?0Zb`>KdeJYDW6roP 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 NZ`_ZLr78E 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 /dev/null 2>&1" - "sleep 10s" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" - module: server_local_monitoring ServerLocalClient: - interval: 1s From 032b5481b554d2986b22ccb90ff33f625ca4fbd1 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 17:58:26 +0530 Subject: [PATCH 172/329] reg\unreg with resent-18 preloaded --- .../tests/register_unregister_multiple.jmx | 101 ++++++++++++++++++ .../tests/register_unregister_multiple.yaml | 64 +++++++++++ 2 files changed, 165 insertions(+) create mode 100644 tests/performance/tests/register_unregister_multiple.jmx create mode 100644 tests/performance/tests/register_unregister_multiple.yaml diff --git a/tests/performance/tests/register_unregister_multiple.jmx b/tests/performance/tests/register_unregister_multiple.jmx new file mode 100644 index 000000000..504c3ae8c --- /dev/null +++ b/tests/performance/tests/register_unregister_multiple.jmx @@ -0,0 +1,101 @@ + + + + + + false + true + false + + + + + + + + + + model + squeezenet_v1.1 + = + Model name + + + model_url + https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + URL to model store on s3 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${model_url} + POST + true + false + true + false + + + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model} + DELETE + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/register_unregister_multiple.yaml b/tests/performance/tests/register_unregister_multiple.yaml new file mode 100644 index 000000000..80c6abf81 --- /dev/null +++ b/tests/performance/tests/register_unregister_multiple.yaml @@ -0,0 +1,64 @@ +--- +execution: +- concurrency: 1 + ramp-up: 0s + iterations: 5 + scenario: register_unregister + +scenarios: + register_unregister: + script: register_unregister.jmx + +modules: + server_local_monitoring: + class : metrics_monitoring_taurus.Monitor + +services: + - module: shellexec + prepare: + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + logging : True + metrics: + - total_processes + - sum_all_file_descriptors + - frontend_memory_rss + +reporting: +- module: passfail + criteria: + # Inbuilt Criteria + - success of RegisterModel<100%, stop as failed + - success of UnregisterModel<100%, stop as failed + - avg-rt of RegisterModel>15s, stop as failed + - avg-rt of UnregisterModel>10ms, stop as failed + # Custom Criteria + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_processes + condition: '>' + threshold: 2 + timeframe: 5s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_all_file_descriptors + condition: '>' + threshold: 73 + timeframe: 5s + stop : true + fail : true +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/frontend_memory_rss +# condition: '>' +# threshold: 120000000 #120MB +# timeframe: 5s +# stop : true +# fail : true \ No newline at end of file From 194ac0564444d199c0f6f5ea8e0cdb72216f8bdf Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 18:43:36 +0530 Subject: [PATCH 173/329] updated single inference --- .../tests/inference_single_worker.jmx | 2 +- .../tests/inference_single_worker.yaml | 53 ++++++++++--------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/tests/performance/tests/inference_single_worker.jmx b/tests/performance/tests/inference_single_worker.jmx index 8dcfe0702..ea05cc1ef 100644 --- a/tests/performance/tests/inference_single_worker.jmx +++ b/tests/performance/tests/inference_single_worker.jmx @@ -51,7 +51,7 @@ true - + diff --git a/tests/performance/tests/inference_single_worker.yaml b/tests/performance/tests/inference_single_worker.yaml index e6f27bb33..30d258b7c 100644 --- a/tests/performance/tests/inference_single_worker.yaml +++ b/tests/performance/tests/inference_single_worker.yaml @@ -17,53 +17,54 @@ modules: services: - module: shellexec prepare: - - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true" post-process: - - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" - module: server_local_monitoring ServerLocalClient: - interval: 1s metrics: - - total_workers - - sum_workers_file_descriptors - - orphans + - total_processes + - sum_all_file_descriptors + - sum_all_memory_rss reporting: - module: passfail criteria: + # Inbuilt Criteria + - success of Inference<100%, stop as failed + - avg-rt of Inference>290ms, stop as failed + # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers + subject: ServerLocalClient/total_processes condition: '>' - threshold: 1 + threshold: 3 timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers + subject: ServerLocalClient/total_processes condition: '<' - threshold: 1 + threshold: 3 timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_workers_file_descriptors + subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 11 + threshold: 90 timeframe: 1s stop : true fail : true - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_workers_file_descriptors - condition: '<' - threshold: 11 - timeframe: 1s - stop : true - fail : true - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/orphans - condition: '>' - threshold: 0 - timeframe: 1s - stop : true - fail : true \ No newline at end of file +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/frontend_memory_rss +# condition: '>' +# threshold: 290000000 #290MB +# timeframe: 5s +# stop : true +# fail : true From e8a7e672e61014d0a0e1823af24231cf9387a3a5 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 18:59:38 +0530 Subject: [PATCH 174/329] Updated inference multiple worker scenario --- .../tests/inference_multiple_worker.jmx | 2 +- .../tests/inference_multiple_worker.yaml | 57 ++++++++++--------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/tests/performance/tests/inference_multiple_worker.jmx b/tests/performance/tests/inference_multiple_worker.jmx index 867b30162..1251a56b8 100644 --- a/tests/performance/tests/inference_multiple_worker.jmx +++ b/tests/performance/tests/inference_multiple_worker.jmx @@ -51,7 +51,7 @@ true - + diff --git a/tests/performance/tests/inference_multiple_worker.yaml b/tests/performance/tests/inference_multiple_worker.yaml index 84e1d8543..fbb23b605 100644 --- a/tests/performance/tests/inference_multiple_worker.yaml +++ b/tests/performance/tests/inference_multiple_worker.yaml @@ -4,10 +4,10 @@ execution: ramp-up: 1s hold-for: 1m iterations: 100 - scenario: inference + scenario: inference_multiple_worker scenarios: - inference: + inference_multiple_worker: script: inference_multiple_worker.jmx modules: @@ -17,53 +17,54 @@ modules: services: - module: shellexec prepare: - - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true" post-process: - - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" - module: server_local_monitoring ServerLocalClient: - interval: 1s metrics: - - total_workers - - sum_workers_file_descriptors - - orphans + - total_processes + - sum_all_file_descriptors + - sum_all_memory_rss reporting: - module: passfail criteria: + # Inbuilt Criteria + - success of Inference<100%, stop as failed + - avg-rt of Inference>140ms, stop as failed + # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers + subject: ServerLocalClient/total_processes condition: '>' - threshold: 4 + threshold: 6 timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers + subject: ServerLocalClient/total_processes condition: '<' - threshold: 4 + threshold: 6 timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_workers_file_descriptors + subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 38 + threshold: 126 timeframe: 1s stop : true fail : true - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_workers_file_descriptors - condition: '<' - threshold: 38 - timeframe: 1s - stop : true - fail : true - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/orphans - condition: '>' - threshold: 0 - timeframe: 1s - stop : true - fail : true \ No newline at end of file +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/frontend_memory_rss +# condition: '>' +# threshold: 750000000 #750MB +# timeframe: 5s +# stop : true +# fail : true \ No newline at end of file From 43809a31f7da4c9ae8d5382bdaa872f7b5e466a6 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 18:59:50 +0530 Subject: [PATCH 175/329] Updated inference single worker scenario --- tests/performance/tests/inference_single_worker.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/tests/inference_single_worker.yaml b/tests/performance/tests/inference_single_worker.yaml index 30d258b7c..f2735d9aa 100644 --- a/tests/performance/tests/inference_single_worker.yaml +++ b/tests/performance/tests/inference_single_worker.yaml @@ -4,10 +4,10 @@ execution: ramp-up: 1s hold-for: 1m iterations: 100 - scenario: inference + scenario: inference_single_worker scenarios: - inference: + inference_single_worker: script: inference_single_worker.jmx modules: From 34070965b3be5096f24cbae09a4f588bb816e2c2 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 19:23:15 +0530 Subject: [PATCH 176/329] Updated health check scenario --- tests/performance/tests/health_check.jmx | 4 +-- tests/performance/tests/health_check.yaml | 42 ++++++++++++++++++----- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/tests/performance/tests/health_check.jmx b/tests/performance/tests/health_check.jmx index ada3f72e1..422c45cf9 100644 --- a/tests/performance/tests/health_check.jmx +++ b/tests/performance/tests/health_check.jmx @@ -12,7 +12,7 @@ - + continue false @@ -26,7 +26,7 @@ true - + diff --git a/tests/performance/tests/health_check.yaml b/tests/performance/tests/health_check.yaml index 306dfdd1b..903cb70aa 100644 --- a/tests/performance/tests/health_check.yaml +++ b/tests/performance/tests/health_check.yaml @@ -14,28 +14,52 @@ modules: class : metrics_monitoring_taurus.Monitor services: + - module: shellexec + prepare: + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 10s" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" - module: server_local_monitoring ServerLocalClient: - interval: 1s metrics: - - total_workers - - sum_workers_file_descriptors - - orphans + - total_processes + - sum_all_file_descriptors + - sum_all_memory_rss reporting: - module: passfail criteria: + # Inbuilt Criteria + - success of HealthCheck<100%, stop as failed + - avg-rt of HealthCheck>14ms, stop as failed + # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers + subject: ServerLocalClient/total_processes condition: '>' - threshold: 0 + threshold: 1 timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/orphans - condition: '>' - threshold: 0 + subject: ServerLocalClient/total_processes + condition: '<' + threshold: 1 timeframe: 1s stop : true - fail : true \ No newline at end of file + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_all_file_descriptors + condition: '>' + threshold: 67 + timeframe: 5s + stop : true + fail : true +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/frontend_memory_rss +# condition: '>' +# threshold: 750000000 #750MB +# timeframe: 5s +# stop : true +# fail : true \ No newline at end of file From 37244e2f50572f7c8a98ee191115bc8f338f5d46 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 19:34:23 +0530 Subject: [PATCH 177/329] Updated API description scenario --- tests/performance/tests/api_description.jmx | 4 +- tests/performance/tests/api_description.yaml | 44 ++++++++++++++++---- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/tests/performance/tests/api_description.jmx b/tests/performance/tests/api_description.jmx index e2b2dd867..a026cb312 100644 --- a/tests/performance/tests/api_description.jmx +++ b/tests/performance/tests/api_description.jmx @@ -44,7 +44,7 @@ true - + @@ -63,7 +63,7 @@ - + diff --git a/tests/performance/tests/api_description.yaml b/tests/performance/tests/api_description.yaml index ff1ad5b71..b428e27bb 100644 --- a/tests/performance/tests/api_description.yaml +++ b/tests/performance/tests/api_description.yaml @@ -14,28 +14,54 @@ modules: class : metrics_monitoring_taurus.Monitor services: + - module: shellexec + prepare: + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 10s" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" - module: server_local_monitoring ServerLocalClient: - interval: 1s metrics: - - total_workers - - sum_workers_file_descriptors - - orphans + - total_processes + - sum_all_file_descriptors + - sum_all_memory_rss reporting: - module: passfail criteria: + # Inbuilt Criteria + - success of ManagementAPIDescription<100%, stop as failed + - success of InferenceAPIDescription<100%, stop as failed + - avg-rt of ManagementAPIDescription>10ms, stop as failed + - avg-rt of InferenceAPIDescription>10ms, stop as failed + # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers + subject: ServerLocalClient/total_processes condition: '>' - threshold: 0 + threshold: 1 timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/orphans - condition: '>' - threshold: 0 + subject: ServerLocalClient/total_processes + condition: '<' + threshold: 1 timeframe: 1s stop : true - fail : true \ No newline at end of file + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_all_file_descriptors + condition: '>' + threshold: 73 + timeframe: 5s + stop : true + fail : true +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/frontend_memory_rss +# condition: '>' +# threshold: 100000000 #100MB +# timeframe: 5s +# stop : true +# fail : true \ No newline at end of file From fc0915474ef37368870d272592d8ac96de2ae08f Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 19:49:02 +0530 Subject: [PATCH 178/329] Updated model description scenario --- tests/performance/tests/model_description.jmx | 2 +- .../performance/tests/model_description.yaml | 51 +++++++++---------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/tests/performance/tests/model_description.jmx b/tests/performance/tests/model_description.jmx index f6d33c99c..4d8898adb 100644 --- a/tests/performance/tests/model_description.jmx +++ b/tests/performance/tests/model_description.jmx @@ -51,7 +51,7 @@ true - + diff --git a/tests/performance/tests/model_description.yaml b/tests/performance/tests/model_description.yaml index ff26a7268..2e5144e6b 100644 --- a/tests/performance/tests/model_description.yaml +++ b/tests/performance/tests/model_description.yaml @@ -16,53 +16,52 @@ modules: services: - module: shellexec prepare: - - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true" post-process: - - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - "multi-model-server --stop > /dev/null 2>&1" - module: server_local_monitoring ServerLocalClient: - interval: 1s metrics: - - total_workers - - sum_workers_file_descriptors - - orphans + - total_processes + - sum_all_file_descriptors + - sum_all_memory_rss reporting: - module: passfail criteria: + # Inbuilt Criteria + - success of ModelDescription<100%, stop as failed + - avg-rt of ModelDescription>14ms, stop as failed + # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers + subject: ServerLocalClient/total_processes condition: '>' - threshold: 1 + threshold: 3 timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers + subject: ServerLocalClient/total_processes condition: '<' - threshold: 1 + threshold: 3 timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_workers_file_descriptors + subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 11 + threshold: 90 timeframe: 1s stop : true fail : true - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_workers_file_descriptors - condition: '<' - threshold: 11 - timeframe: 1s - stop : true - fail : true - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/orphans - condition: '>' - threshold: 0 - timeframe: 1s - stop : true - fail : true \ No newline at end of file +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/frontend_memory_rss +# condition: '>' +# threshold: 300000000 #300MB +# timeframe: 5s +# stop : true +# fail : true \ No newline at end of file From 7391d83cc3fc23a3ea828ce44e7209d84d26feb7 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 20:00:54 +0530 Subject: [PATCH 179/329] Updated list models scenario --- tests/performance/tests/list_models.jmx | 2 +- tests/performance/tests/list_models.yaml | 43 +++++++++++++++--------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/tests/performance/tests/list_models.jmx b/tests/performance/tests/list_models.jmx index 2030b122c..cd5490dc4 100644 --- a/tests/performance/tests/list_models.jmx +++ b/tests/performance/tests/list_models.jmx @@ -51,7 +51,7 @@ true - + diff --git a/tests/performance/tests/list_models.yaml b/tests/performance/tests/list_models.yaml index d43b169b7..8b2ca93a0 100644 --- a/tests/performance/tests/list_models.yaml +++ b/tests/performance/tests/list_models.yaml @@ -16,40 +16,53 @@ modules: services: - module: shellexec prepare: - - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/shufflenet.mar + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/shufflenet.mar" post-process: - - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 - - curl -s -X DELETE http://localhost:8081/models/shufflenet + - "multi-model-server --stop > /dev/null 2>&1" - module: server_local_monitoring ServerLocalClient: - interval: 1s + logging : True metrics: - - total_workers - - sum_workers_file_descriptors - - orphans + - total_processes + - sum_all_file_descriptors + - sum_all_memory_rss reporting: - module: passfail criteria: + # Inbuilt Criteria + - success of ListModels<100%, stop as failed + - avg-rt of ListModels>14ms, stop as failed + # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers + subject: ServerLocalClient/total_processes condition: '>' - threshold: 1 + threshold: 3 timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_workers_file_descriptors - condition: '>' - threshold: 8 + subject: ServerLocalClient/total_processes + condition: '<' + threshold: 3 timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/orphans + subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 0 + threshold: 86 timeframe: 1s stop : true - fail : true \ No newline at end of file + fail : true +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/frontend_memory_rss +# condition: '>' +# threshold: 185000000 #185MB +# timeframe: 5s +# stop : true +# fail : true \ No newline at end of file From 3b0e1925fa2aa182698a38bebe1e8a22951bd3bd Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 20:01:32 +0530 Subject: [PATCH 180/329] logging: True for all scenarios --- tests/performance/tests/api_description.yaml | 1 + tests/performance/tests/health_check.yaml | 1 + tests/performance/tests/inference_multiple_worker.yaml | 1 + tests/performance/tests/inference_single_worker.yaml | 1 + tests/performance/tests/model_description.yaml | 1 + tests/performance/tests/scale_down_workers.yaml | 1 + tests/performance/tests/scale_up_workers.yaml | 1 + 7 files changed, 7 insertions(+) diff --git a/tests/performance/tests/api_description.yaml b/tests/performance/tests/api_description.yaml index b428e27bb..92b33829b 100644 --- a/tests/performance/tests/api_description.yaml +++ b/tests/performance/tests/api_description.yaml @@ -23,6 +23,7 @@ services: - module: server_local_monitoring ServerLocalClient: - interval: 1s + logging : True metrics: - total_processes - sum_all_file_descriptors diff --git a/tests/performance/tests/health_check.yaml b/tests/performance/tests/health_check.yaml index 903cb70aa..848232ce1 100644 --- a/tests/performance/tests/health_check.yaml +++ b/tests/performance/tests/health_check.yaml @@ -23,6 +23,7 @@ services: - module: server_local_monitoring ServerLocalClient: - interval: 1s + logging : True metrics: - total_processes - sum_all_file_descriptors diff --git a/tests/performance/tests/inference_multiple_worker.yaml b/tests/performance/tests/inference_multiple_worker.yaml index fbb23b605..911e25517 100644 --- a/tests/performance/tests/inference_multiple_worker.yaml +++ b/tests/performance/tests/inference_multiple_worker.yaml @@ -28,6 +28,7 @@ services: - module: server_local_monitoring ServerLocalClient: - interval: 1s + logging : True metrics: - total_processes - sum_all_file_descriptors diff --git a/tests/performance/tests/inference_single_worker.yaml b/tests/performance/tests/inference_single_worker.yaml index f2735d9aa..5704c68b7 100644 --- a/tests/performance/tests/inference_single_worker.yaml +++ b/tests/performance/tests/inference_single_worker.yaml @@ -28,6 +28,7 @@ services: - module: server_local_monitoring ServerLocalClient: - interval: 1s + logging : True metrics: - total_processes - sum_all_file_descriptors diff --git a/tests/performance/tests/model_description.yaml b/tests/performance/tests/model_description.yaml index 2e5144e6b..f085f54a8 100644 --- a/tests/performance/tests/model_description.yaml +++ b/tests/performance/tests/model_description.yaml @@ -25,6 +25,7 @@ services: - module: server_local_monitoring ServerLocalClient: - interval: 1s + logging : True metrics: - total_processes - sum_all_file_descriptors diff --git a/tests/performance/tests/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers.yaml index beeb3ec88..9aa67247a 100644 --- a/tests/performance/tests/scale_down_workers.yaml +++ b/tests/performance/tests/scale_down_workers.yaml @@ -23,6 +23,7 @@ services: - module: server_local_monitoring ServerLocalClient: - interval: 1s + logging : True metrics: - total_workers - sum_workers_file_descriptors diff --git a/tests/performance/tests/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers.yaml index 19d643320..586f0bc63 100644 --- a/tests/performance/tests/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers.yaml @@ -22,6 +22,7 @@ services: - module: server_local_monitoring ServerLocalClient: - interval: 1s + logging : True metrics: - total_workers - sum_workers_file_descriptors From 5837cffde591288824bf26ecc86daa19d777eeea Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 20:21:42 +0530 Subject: [PATCH 181/329] corrected metric name --- tests/performance/tests/api_description.yaml | 2 +- tests/performance/tests/health_check.yaml | 2 +- tests/performance/tests/inference_multiple_worker.yaml | 2 +- tests/performance/tests/inference_single_worker.yaml | 2 +- tests/performance/tests/list_models.yaml | 2 +- tests/performance/tests/model_description.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/performance/tests/api_description.yaml b/tests/performance/tests/api_description.yaml index 92b33829b..cfb168ba5 100644 --- a/tests/performance/tests/api_description.yaml +++ b/tests/performance/tests/api_description.yaml @@ -60,7 +60,7 @@ reporting: stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria -# subject: ServerLocalClient/frontend_memory_rss +# subject: ServerLocalClient/sum_all_memory_rss # condition: '>' # threshold: 100000000 #100MB # timeframe: 5s diff --git a/tests/performance/tests/health_check.yaml b/tests/performance/tests/health_check.yaml index 848232ce1..8fd9a66ec 100644 --- a/tests/performance/tests/health_check.yaml +++ b/tests/performance/tests/health_check.yaml @@ -58,7 +58,7 @@ reporting: stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria -# subject: ServerLocalClient/frontend_memory_rss +# subject: ServerLocalClient/sum_all_memory_rss # condition: '>' # threshold: 750000000 #750MB # timeframe: 5s diff --git a/tests/performance/tests/inference_multiple_worker.yaml b/tests/performance/tests/inference_multiple_worker.yaml index 911e25517..9a2f946a5 100644 --- a/tests/performance/tests/inference_multiple_worker.yaml +++ b/tests/performance/tests/inference_multiple_worker.yaml @@ -63,7 +63,7 @@ reporting: stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria -# subject: ServerLocalClient/frontend_memory_rss +# subject: ServerLocalClient/sum_all_memory_rss # condition: '>' # threshold: 750000000 #750MB # timeframe: 5s diff --git a/tests/performance/tests/inference_single_worker.yaml b/tests/performance/tests/inference_single_worker.yaml index 5704c68b7..d231b4f8a 100644 --- a/tests/performance/tests/inference_single_worker.yaml +++ b/tests/performance/tests/inference_single_worker.yaml @@ -63,7 +63,7 @@ reporting: stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria -# subject: ServerLocalClient/frontend_memory_rss +# subject: ServerLocalClient/sum_all_memory_rss # condition: '>' # threshold: 290000000 #290MB # timeframe: 5s diff --git a/tests/performance/tests/list_models.yaml b/tests/performance/tests/list_models.yaml index 8b2ca93a0..fde041030 100644 --- a/tests/performance/tests/list_models.yaml +++ b/tests/performance/tests/list_models.yaml @@ -60,7 +60,7 @@ reporting: stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria -# subject: ServerLocalClient/frontend_memory_rss +# subject: ServerLocalClient/sum_all_memory_rss # condition: '>' # threshold: 185000000 #185MB # timeframe: 5s diff --git a/tests/performance/tests/model_description.yaml b/tests/performance/tests/model_description.yaml index f085f54a8..0884fe18d 100644 --- a/tests/performance/tests/model_description.yaml +++ b/tests/performance/tests/model_description.yaml @@ -60,7 +60,7 @@ reporting: stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria -# subject: ServerLocalClient/frontend_memory_rss +# subject: ServerLocalClient/sum_all_memory_rss # condition: '>' # threshold: 300000000 #300MB # timeframe: 5s From d7dea3e6b055f1d15f71d2695c4290feb3b9f210 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 23 Jun 2020 20:27:15 +0530 Subject: [PATCH 182/329] batch and --- .../agents/metrics_monitoring_taurus.py | 6 +- .../tests/batch_and_single_inference.jmx | 117 ++++++++++++++++++ .../tests/batch_and_single_inference.yaml | 93 ++++++++++++++ tests/performance/tests/batch_inference.jmx | 84 +++++++++++++ tests/performance/tests/batch_inference.yaml | 80 ++++++++++++ ...ence_taurus_local_monitoring_criteria.yaml | 6 +- 6 files changed, 381 insertions(+), 5 deletions(-) create mode 100644 tests/performance/tests/batch_and_single_inference.jmx create mode 100644 tests/performance/tests/batch_and_single_inference.yaml create mode 100644 tests/performance/tests/batch_inference.jmx create mode 100644 tests/performance/tests/batch_inference.yaml diff --git a/tests/performance/agents/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_taurus.py index 40451d4eb..277b0f5b2 100644 --- a/tests/performance/agents/metrics_monitoring_taurus.py +++ b/tests/performance/agents/metrics_monitoring_taurus.py @@ -89,9 +89,11 @@ def _calc_resource_stats(self, interval): result.update(get_metrics(server_process, get_child_processes(server_process), self.log)) metrics_msg = [] + + updated_result = {} for key in self.metrics: if result.get(key) is not None: metrics_msg.append("{0} : {1}".format(key, result[key])) + updated_result[key] = result.get(key) self.log.info("{0}".format(" -- ".join(metrics_msg))) - - return result + return updated_result diff --git a/tests/performance/tests/batch_and_single_inference.jmx b/tests/performance/tests/batch_and_single_inference.jmx new file mode 100644 index 000000000..d119e9b7f --- /dev/null +++ b/tests/performance/tests/batch_and_single_inference.jmx @@ -0,0 +1,117 @@ + + + + + + false + true + false + + + + + + + + + + model1 + ${__P(model_name1,resnet-152)} + = + + + model2 + ${__P(model_name2,squeezenet_v1.1)} + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,200)} + + ${__P(threads,20)} + ${__P(rampup,5)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model1} + POST + true + false + true + true + + + + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model2} + POST + true + false + true + true + + + + + + + + + diff --git a/tests/performance/tests/batch_and_single_inference.yaml b/tests/performance/tests/batch_and_single_inference.yaml new file mode 100644 index 000000000..353f68735 --- /dev/null +++ b/tests/performance/tests/batch_and_single_inference.yaml @@ -0,0 +1,93 @@ +--- +execution: +- concurrency: 10 + ramp-up: 5s + hold-for: 20s + scenario: Inference + +scenarios: + Inference: + script: batch_and_single_inference.jmx + +modules: + server_local_monitoring: + # metrics_monitoring_taurus and dependencies should be in python path + class : metrics_monitoring_taurus.Monitor # monitoring class. + +services: + - module: shellexec + prepare: + - curl -s -X POST curl -X POST "http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-152.mar&batch_size=8&max_batch_delay=50" + # uncomment below and comment prev and use downloaded model with model-store + #- curl -s -X POST "http://localhost:8081/models?url=resnet-152.mar&batch_size=8&max_batch_delay=6000&initial_workers=1" + - curl -s -X PUT http://localhost:8081/models/resnet-152?min_worker=4&synchronous=true + - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true + post-process: + - curl -s -X DELETE http://localhost:8081/models/resnet-152 + - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - module: server_local_monitoring # should be added in modules section + ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + - interval: 1s + logging : True + metrics: + - sum_workers_memory_rss + - sum_workers_file_descriptors + - total_workers + - orphans + +reporting: +- module: passfail + criteria: + - subject: avg-rt # required + label: 'Inference1' # optional, default is '' + condition: '>' # required + threshold: 6s # required + logic: for # optional, logic to aggregate values within timeframe. + # Default 'for' means take latest, + # 'within' and 'over' means take sum/avg of all values within interval + timeframe: 10s # optional, default is none + stop: true # optional, default is true. false for nonstop testing until the end + fail: true # optional, default is true + - subject: avg-rt # required + label: 'Inference2' # optional, default is '' + condition: '>' # required + threshold: 0.08s # required + logic: for # optional, logic to aggregate values within timeframe. + # Default 'for' means take latest, + # 'within' and 'over' means take sum/avg of all values within interval + timeframe: 10s # optional, default is none + stop: true # optional, default is true. false for nonstop testing until the end + fail: true # optional, default is true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_memory_rss + condition: '>' + threshold: 4000000000 + timeframe: 1s + stop : true + fail : true + diff_percent : 30 + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '>' + threshold: 0 + timeframe: 1s + stop : true + fail : true + diff_percent : 0 + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 9 + timeframe: 1s + stop: true + fail: true + diff_percent: 0 + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '>' + threshold: 78 + timeframe: 1s + stop: true + fail: true + diff_percent: 30 \ No newline at end of file diff --git a/tests/performance/tests/batch_inference.jmx b/tests/performance/tests/batch_inference.jmx new file mode 100644 index 000000000..885fac295 --- /dev/null +++ b/tests/performance/tests/batch_inference.jmx @@ -0,0 +1,84 @@ + + + + + + false + true + false + + + + + + + + + + model + ${__P(model_name,resnet-152)} + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,200)} + + ${__P(threads,20)} + ${__P(rampup,5)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + + diff --git a/tests/performance/tests/batch_inference.yaml b/tests/performance/tests/batch_inference.yaml new file mode 100644 index 000000000..2349cb774 --- /dev/null +++ b/tests/performance/tests/batch_inference.yaml @@ -0,0 +1,80 @@ +--- +execution: +- concurrency: 10 + ramp-up: 5s + hold-for: 20s + scenario: Inference + +scenarios: + Inference: + script: batch_inference.jmx + +modules: + server_local_monitoring: + # metrics_monitoring_taurus and dependencies should be in python path + class : metrics_monitoring_taurus.Monitor # monitoring class. + +services: + - module: shellexec + prepare: + - curl -s -X POST curl -X POST "http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-152.mar&batch_size=8&max_batch_delay=50" + # uncomment below and comment prev and use downloaded model with model-store + #- curl -s -X POST "http://localhost:8081/models?url=resnet-152.mar&batch_size=8&max_batch_delay=6000&initial_workers=1" + - curl -s -X PUT http://localhost:8081/models/resnet-152?min_worker=4&synchronous=true + post-process: + - curl -s -X DELETE http://localhost:8081/models/resnet-152 + - module: server_local_monitoring # should be added in modules section + ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + - interval: 1s + logging : True + metrics: + - sum_workers_memory_rss + - sum_workers_file_descriptors + - total_workers + - orphans + +reporting: +- module: passfail + criteria: + - subject: avg-rt # required + label: 'Inference' # optional, default is '' + condition: '>' # required + threshold: 6s # required + logic: for # optional, logic to aggregate values within timeframe. + # Default 'for' means take latest, + # 'within' and 'over' means take sum/avg of all values within interval + timeframe: 10s # optional, default is none + stop: true # optional, default is true. false for nonstop testing until the end + fail: true # optional, default is true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_memory_rss + condition: '>' + threshold: 3000000000 + timeframe: 1s + stop : true + fail : true + diff_percent : 30 + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '>' + threshold: 0 + timeframe: 1s + stop : true + fail : true + diff_percent : 0 + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' + threshold: 4 + timeframe: 1s + stop: true + fail: true + diff_percent: 0 + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '>' + threshold: 38 + timeframe: 1s + stop: true + fail: true + diff_percent: 30 \ No newline at end of file diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml index 3e24f5420..9fea0eb31 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml @@ -28,9 +28,9 @@ services: reporting: - module: passfail criteria: - - fail of s2_fail >50%, stop as failed - - p90 of s2_p90 >250ms , stop as failed - - avg-rt of s2_avg_rt >1s , stop as failed + - fail >50%, stop as failed + - p90 >250ms , stop as failed + - avg-rt >1s , stop as failed - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' From e3855934f7adb5193acda6c1efcb2058cca96e0c Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 23 Jun 2020 20:30:01 +0530 Subject: [PATCH 183/329] fix --- .../tests/inference_server_monitoring_criteria.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/performance/tests/inference_server_monitoring_criteria.yaml b/tests/performance/tests/inference_server_monitoring_criteria.yaml index 46ea8fbeb..6002e2a70 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_server_monitoring_criteria.yaml @@ -28,9 +28,9 @@ services: reporting: - module: passfail criteria: - - fail of s2_fail >50%, stop as failed - - p90 of s2_p90 >250ms , stop as failed - - avg-rt of s2_avg_rt >1s , stop as failed + - fail >50%, stop as failed + - p90 >250ms , stop as failed + - avg-rt >1s , stop as failed - class: bzt.modules.monitoring.MonitoringCriteria subject: mms-inference-server/sum_workers_file_descriptors condition: '>' From 0646d30e6936bff4e948dfb0c5e963953e5b695a Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 20:31:19 +0530 Subject: [PATCH 184/329] Updated scale up workers --- tests/performance/tests/scale_up_workers.jmx | 2 +- tests/performance/tests/scale_up_workers.yaml | 48 +++++++++++++++---- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/tests/performance/tests/scale_up_workers.jmx b/tests/performance/tests/scale_up_workers.jmx index e8afa3f10..997547d66 100644 --- a/tests/performance/tests/scale_up_workers.jmx +++ b/tests/performance/tests/scale_up_workers.jmx @@ -57,7 +57,7 @@ true - + diff --git a/tests/performance/tests/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers.yaml index 586f0bc63..5c7c7be7b 100644 --- a/tests/performance/tests/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers.yaml @@ -16,32 +16,55 @@ modules: services: - module: shellexec prepare: - - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" post-process: - - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - "multi-model-server --stop > /dev/null 2>&1" - module: server_local_monitoring ServerLocalClient: - interval: 1s logging : True metrics: + - total_processes - total_workers + - frontend_file_descriptors - sum_workers_file_descriptors - - orphans + - frontend_memory_rss + - sum_workers_memory_rss reporting: - module: passfail criteria: + # Inbuilt Criteria + - success of ScaleUp<100%, stop as failed + - avg-rt of ScaleUp>10ms, stop as failed + # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_workers + subject: ServerLocalClient/total_processes condition: '>' - threshold: 4 + threshold: 6 timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_workers_file_descriptors + subject: ServerLocalClient/total_processes condition: '<' + threshold: 2 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '>' threshold: 4 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/frontend_file_descriptors + condition: '>' + threshold: 88 timeframe: 5s stop : true fail : true @@ -52,10 +75,17 @@ reporting: timeframe: 5s stop : true fail : true +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/frontend_memory_rss +# condition: '>' +# threshold: 290000000 #290MB +# timeframe: 5s +# stop : true +# fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/orphans + subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: 0 - timeframe: 1s + threshold: 450000000 #450MB + timeframe: 5s stop : true fail : true \ No newline at end of file From d4d740352b1a7a3a63718b5f5e46e75e8a64073c Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 22:05:11 +0530 Subject: [PATCH 185/329] guard against zombie and recycled process --- tests/performance/agents/metrics/__init__.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index ed27cd037..893db8394 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -15,6 +15,8 @@ from statistics import mean import psutil +from psutil import ZombieProcess + class ProcessType(Enum): """ Type of MMS processes to compute metrics on """ @@ -112,9 +114,13 @@ def update_metric(metric_name, proc_type, stats): processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) for child in children: - if psutil.pid_exists(child.pid): - processes_stats.append({'type': ProcessType.WORKER, 'stats' : child.as_dict()}) - else: + try: + if psutil.pid_exists(child.pid) and 'model_service_worker.py' in child.cmdline()[1]: + processes_stats.append({'type': ProcessType.WORKER, 'stats' : child.as_dict()}) + else: + reclaimed_pids.append(child) + logger.debug('child {0} no longer available'.format(child.pid)) + except ZombieProcess: reclaimed_pids.append(child) logger.debug('child {0} no longer available'.format(child.pid)) From 77d1d37725462a57c0698ff077c8f5dbe9ca7224 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 23 Jun 2020 22:17:30 +0530 Subject: [PATCH 186/329] Updated scale down scenario --- .../performance/tests/scale_down_workers.jmx | 2 +- .../performance/tests/scale_down_workers.yaml | 64 ++++++++++++++++--- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/tests/performance/tests/scale_down_workers.jmx b/tests/performance/tests/scale_down_workers.jmx index cca760b5f..512444b07 100644 --- a/tests/performance/tests/scale_down_workers.jmx +++ b/tests/performance/tests/scale_down_workers.jmx @@ -57,7 +57,7 @@ true - + diff --git a/tests/performance/tests/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers.yaml index 9aa67247a..a9dd432ac 100644 --- a/tests/performance/tests/scale_down_workers.yaml +++ b/tests/performance/tests/scale_down_workers.yaml @@ -16,22 +16,45 @@ modules: services: - module: shellexec prepare: - - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true" post-process: - - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - "multi-model-server --stop > /dev/null 2>&1" - module: server_local_monitoring ServerLocalClient: - interval: 1s logging : True metrics: + - total_processes - total_workers + - frontend_file_descriptors - sum_workers_file_descriptors - - orphans + - frontend_memory_rss + - sum_workers_memory_rss reporting: - module: passfail criteria: + # Inbuilt Criteria + - success of ScaleDown<100%, stop as failed + - avg-rt of ScaleDown>10ms, stop as failed + # Custom Criteria + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_processes + condition: '>' + threshold: 6 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_processes + condition: '<' + threshold: 4 + timeframe: 1s + stop : true + fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '>' @@ -46,24 +69,45 @@ reporting: timeframe: 1s stop : true fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/frontend_file_descriptors + condition: '>' + threshold: 78 + timeframe: 5s + stop : true + fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '>' threshold: 38 - timeframe: 1s + timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '<' - threshold: 21 - timeframe: 1s + threshold: 22 + timeframe: 5s stop : true fail : true +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/frontend_memory_rss +# condition: '>' +# threshold: 290000000 #290MB +# timeframe: 5s +# stop : true +# fail : true - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/orphans + subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: 0 - timeframe: 1s + threshold: 450000000 #450MB + timeframe: 5s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_memory_rss + condition: '<' + threshold: 210000000 #230MB + timeframe: 5s stop : true fail : true \ No newline at end of file From 5bf2127fd2d3659f45cd501955a4e6b2efc867ca Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 23 Jun 2020 17:41:21 +0000 Subject: [PATCH 187/329] batch fixes --- .../tests/batch_and_single_inference.yaml | 14 +++++++------- tests/performance/tests/batch_inference.yaml | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/performance/tests/batch_and_single_inference.yaml b/tests/performance/tests/batch_and_single_inference.yaml index 353f68735..912a10adf 100644 --- a/tests/performance/tests/batch_and_single_inference.yaml +++ b/tests/performance/tests/batch_and_single_inference.yaml @@ -17,12 +17,12 @@ modules: services: - module: shellexec prepare: - - curl -s -X POST curl -X POST "http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-152.mar&batch_size=8&max_batch_delay=50" + - curl -s -X POST "http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/examples/resnet-152-batching/resnet-152.mar&batch_size=8&max_batch_delay=50" # uncomment below and comment prev and use downloaded model with model-store - #- curl -s -X POST "http://localhost:8081/models?url=resnet-152.mar&batch_size=8&max_batch_delay=6000&initial_workers=1" - - curl -s -X PUT http://localhost:8081/models/resnet-152?min_worker=4&synchronous=true + #- curl -s -X POST "http://localhost:8081/models?url=resnet-152.mar&batch_size=8&max_batch_delay=60&initial_workers=1" + - curl -s -X PUT http://localhost:8081/models/resnet-152?min_worker=2&synchronous=true - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true + - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=2&synchronous=true post-process: - curl -s -X DELETE http://localhost:8081/models/resnet-152 - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 @@ -46,7 +46,7 @@ reporting: logic: for # optional, logic to aggregate values within timeframe. # Default 'for' means take latest, # 'within' and 'over' means take sum/avg of all values within interval - timeframe: 10s # optional, default is none + timeframe: 1s # optional, default is none stop: true # optional, default is true. false for nonstop testing until the end fail: true # optional, default is true - subject: avg-rt # required @@ -56,7 +56,7 @@ reporting: logic: for # optional, logic to aggregate values within timeframe. # Default 'for' means take latest, # 'within' and 'over' means take sum/avg of all values within interval - timeframe: 10s # optional, default is none + timeframe: 1s # optional, default is none stop: true # optional, default is true. false for nonstop testing until the end fail: true # optional, default is true - class: bzt.modules.monitoring.MonitoringCriteria @@ -90,4 +90,4 @@ reporting: timeframe: 1s stop: true fail: true - diff_percent: 30 \ No newline at end of file + diff_percent: 30 diff --git a/tests/performance/tests/batch_inference.yaml b/tests/performance/tests/batch_inference.yaml index 2349cb774..bd1256f5b 100644 --- a/tests/performance/tests/batch_inference.yaml +++ b/tests/performance/tests/batch_inference.yaml @@ -17,10 +17,10 @@ modules: services: - module: shellexec prepare: - - curl -s -X POST curl -X POST "http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-152.mar&batch_size=8&max_batch_delay=50" + - curl -s -X POST "http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/examples/resnet-152-batching/resnet-152.mar&batch_size=8&max_batch_delay=50" # uncomment below and comment prev and use downloaded model with model-store - #- curl -s -X POST "http://localhost:8081/models?url=resnet-152.mar&batch_size=8&max_batch_delay=6000&initial_workers=1" - - curl -s -X PUT http://localhost:8081/models/resnet-152?min_worker=4&synchronous=true + #- curl -s -X POST "http://localhost:8081/models?url=resnet-152.mar&batch_size=8&max_batch_delay=60&initial_workers=1" + - curl -s -X PUT http://localhost:8081/models/resnet-152?min_worker=2&synchronous=true post-process: - curl -s -X DELETE http://localhost:8081/models/resnet-152 - module: server_local_monitoring # should be added in modules section @@ -43,7 +43,7 @@ reporting: logic: for # optional, logic to aggregate values within timeframe. # Default 'for' means take latest, # 'within' and 'over' means take sum/avg of all values within interval - timeframe: 10s # optional, default is none + timeframe: 1s # optional, default is none stop: true # optional, default is true. false for nonstop testing until the end fail: true # optional, default is true - class: bzt.modules.monitoring.MonitoringCriteria @@ -77,4 +77,4 @@ reporting: timeframe: 1s stop: true fail: true - diff_percent: 30 \ No newline at end of file + diff_percent: 30 From 6899de278bb0101940a69c5c95149a22d6b6a20a Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 23 Jun 2020 17:44:36 +0000 Subject: [PATCH 188/329] log collected --- tests/performance/run_performance_suite.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index db5d98c5f..f7eb3be47 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -402,6 +402,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam junit_xml = JUnitXml() pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(base_file_path)) test_yamls = get_test_yamls(test_dir, pattern) + logger.info("Collected test yamls {}".format(test_yamls)) for test_file in tqdm(test_yamls, desc="Test Suites"): suite_name = os.path.basename(test_file).rsplit('.', 1)[0] with Timer("Test suite {} execution time".format(suite_name)) as t: From 1c4d82b006f4f058e14e2f9e9f43428d54caabd4 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 09:06:04 +0530 Subject: [PATCH 189/329] inference with multiple models scenario --- .../tests/inference_multiple_models.jmx | 134 ++++++++++++++++++ .../tests/inference_multiple_models.yaml | 74 ++++++++++ 2 files changed, 208 insertions(+) create mode 100644 tests/performance/tests/inference_multiple_models.jmx create mode 100644 tests/performance/tests/inference_multiple_models.yaml diff --git a/tests/performance/tests/inference_multiple_models.jmx b/tests/performance/tests/inference_multiple_models.jmx new file mode 100644 index 000000000..1ceeaf2c2 --- /dev/null +++ b/tests/performance/tests/inference_multiple_models.jmx @@ -0,0 +1,134 @@ + + + + + + false + true + false + + + + + + + + + + model1 + squeezenet_v1.1 + = + Model1 Name + + + model2 + resnet-18 + Model2 Name + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model1} + POST + true + false + true + true + + + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model2} + POST + true + false + true + true + + + + + + + + + diff --git a/tests/performance/tests/inference_multiple_models.yaml b/tests/performance/tests/inference_multiple_models.yaml new file mode 100644 index 000000000..7a1f6732d --- /dev/null +++ b/tests/performance/tests/inference_multiple_models.yaml @@ -0,0 +1,74 @@ +--- +execution: +- concurrency: 10 + ramp-up: 1s + hold-for: 30s + scenario: inference_multiple_models + +scenarios: + inference_multiple_models: + script: inference_multiple_models.jmx + +modules: + server_local_monitoring: + class : metrics_monitoring_taurus.Monitor + +services: + - module: shellexec + prepare: + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar" + - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true" + - "curl -s -X PUT http://localhost:8081/models/resnet-18?min_worker=1&synchronous=true" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + logging : True + metrics: + - total_processes + - sum_all_file_descriptors + - sum_all_memory_rss + +reporting: +- module: passfail + criteria: + # Inbuilt Criteria + - success of Inference1<100%, stop as failed + - success of Inference2<100%, stop as failed + - avg-rt of Inference1>290ms, stop as failed + - avg-rt of Inference2>450ms, stop as failed + # Custom Criteria + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_processes + condition: '>' + threshold: 5 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_processes + condition: '<' + threshold: 5 + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_all_file_descriptors + condition: '>' + threshold: 107 + timeframe: 5s + stop : true + fail : true +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/sum_all_memory_rss +# condition: '>' +# threshold: 600000000 #600MB +# timeframe: 5s +# stop : true +# fail : true From 645a08c84cc56457acaeda6507a9a1de933b9019 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 09:36:24 +0530 Subject: [PATCH 190/329] Add scaleup to reg_unreg scenario clean up test --- .../tests/register_unregister_multiple.jmx | 27 ++++++++++++++++++- .../tests/register_unregister_multiple.yaml | 10 ++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/tests/performance/tests/register_unregister_multiple.jmx b/tests/performance/tests/register_unregister_multiple.jmx index 504c3ae8c..1dac0d5fe 100644 --- a/tests/performance/tests/register_unregister_multiple.jmx +++ b/tests/performance/tests/register_unregister_multiple.jmx @@ -26,6 +26,12 @@ URL to model store on s3 = + + scale_up_workers + 2 + Numer of workers to scale up to + = + @@ -43,7 +49,7 @@ - + continue false @@ -76,6 +82,25 @@ + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_up_workers}&synchronous=true + PUT + true + false + true + false + + + + + diff --git a/tests/performance/tests/register_unregister_multiple.yaml b/tests/performance/tests/register_unregister_multiple.yaml index 80c6abf81..0fc59628e 100644 --- a/tests/performance/tests/register_unregister_multiple.yaml +++ b/tests/performance/tests/register_unregister_multiple.yaml @@ -3,11 +3,11 @@ execution: - concurrency: 1 ramp-up: 0s iterations: 5 - scenario: register_unregister + scenario: register_unregister_multiple scenarios: - register_unregister: - script: register_unregister.jmx + register_unregister_multiple: + script: register_unregister_multiple.jmx modules: server_local_monitoring: @@ -37,9 +37,11 @@ reporting: criteria: # Inbuilt Criteria - success of RegisterModel<100%, stop as failed + - success of ScaleUp<100%, stop as failed - success of UnregisterModel<100%, stop as failed - avg-rt of RegisterModel>15s, stop as failed - - avg-rt of UnregisterModel>10ms, stop as failed + - avg-rt of ScaleUp>1.5s, stop as failed + - avg-rt of UnregisterModel>18ms, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes From 696bafa1ef043145260a4572ea2f7dfc7dbc53de Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 09:56:36 +0530 Subject: [PATCH 191/329] scale up\down linearinc\dec wokrer fd and memory --- .../performance/tests/scale_down_workers.yaml | 2 +- tests/performance/tests/scale_up_workers.yaml | 24 ++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/performance/tests/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers.yaml index a9dd432ac..c55eba736 100644 --- a/tests/performance/tests/scale_down_workers.yaml +++ b/tests/performance/tests/scale_down_workers.yaml @@ -86,7 +86,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '<' - threshold: 22 + threshold: 23 timeframe: 5s stop : true fail : true diff --git a/tests/performance/tests/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers.yaml index 5c7c7be7b..fbb17d237 100644 --- a/tests/performance/tests/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers.yaml @@ -19,6 +19,7 @@ services: - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true" post-process: - "multi-model-server --stop > /dev/null 2>&1" - module: server_local_monitoring @@ -50,7 +51,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: 2 + threshold: 3 timeframe: 1s stop : true fail : true @@ -61,6 +62,13 @@ reporting: timeframe: 1s stop : true fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_workers + condition: '<' + threshold: 1 + timeframe: 1s + stop : true + fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/frontend_file_descriptors condition: '>' @@ -75,6 +83,13 @@ reporting: timeframe: 5s stop : true fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_file_descriptors + condition: '<' + threshold: 11 + timeframe: 5s + stop : true + fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/frontend_memory_rss # condition: '>' @@ -88,4 +103,11 @@ reporting: threshold: 450000000 #450MB timeframe: 5s stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_workers_memory_rss + condition: '<' + threshold: 122000000 #122MB + timeframe: 5s + stop : true fail : true \ No newline at end of file From c65b476e5ecd8541588e71f56a60bf72dc841c23 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 24 Jun 2020 10:39:36 +0530 Subject: [PATCH 192/329] testmd batch --- tests/performance/TESTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/TESTS.md b/tests/performance/TESTS.md index 6a4413333..285cf9da3 100644 --- a/tests/performance/TESTS.md +++ b/tests/performance/TESTS.md @@ -15,8 +15,8 @@ |---|----|---| |Register Model|A,B,F,J|[WIP] [register_unregister.yaml](tests/register_unregister.yaml)| |Inference|A,B,C|[WIP] [inference_single_worker.yaml](tests/inference_single_worker.yaml), [inference_multiple_worker.yaml](tests/inference_multiple_worker.yaml)| -|Batch Inference|A,B,C|| -|Custom Model Handlers|C|| +|Batch Inference|A,B,C|[batch_inference.yaml](tests/batch_inference.yaml)| +|Custom Model Handlers|C|[batch_and_single_inference.yaml](tests/batch_and_single_inference.yaml)| |Scale Workers - UP/DOWN|D,G,I,F,J|[WIP] [scale_up_workers.yaml](tests/scale_up_workers.yaml), [scale_down_workers.yaml](tests/scale_down_workers.yaml)| |Unregister Models|D,G,I,J|[WIP] [register_unregister.yaml](tests/register_unregister.yaml)| |Health Check|A,B,E|[WIP] [health_check.yaml](tests/health_check.yaml)| From def9c2ebbd02925009920710a9f1fe7c856f9568 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 10:40:24 +0530 Subject: [PATCH 193/329] update batch scenarios for start\stop mms --- .../tests/batch_and_single_inference.yaml | 15 +++++++++------ tests/performance/tests/batch_inference.yaml | 12 ++++++++---- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/tests/performance/tests/batch_and_single_inference.yaml b/tests/performance/tests/batch_and_single_inference.yaml index 912a10adf..6fafac9a0 100644 --- a/tests/performance/tests/batch_and_single_inference.yaml +++ b/tests/performance/tests/batch_and_single_inference.yaml @@ -17,15 +17,18 @@ modules: services: - module: shellexec prepare: - - curl -s -X POST "http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/examples/resnet-152-batching/resnet-152.mar&batch_size=8&max_batch_delay=50" + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/examples/resnet-152-batching/resnet-152.mar&batch_size=8&max_batch_delay=50" # uncomment below and comment prev and use downloaded model with model-store #- curl -s -X POST "http://localhost:8081/models?url=resnet-152.mar&batch_size=8&max_batch_delay=60&initial_workers=1" - - curl -s -X PUT http://localhost:8081/models/resnet-152?min_worker=2&synchronous=true - - curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar - - curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=2&synchronous=true + - "curl -s -X PUT http://localhost:8081/models/resnet-152?min_worker=2&synchronous=true" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=2&synchronous=true" post-process: - - curl -s -X DELETE http://localhost:8081/models/resnet-152 - - curl -s -X DELETE http://localhost:8081/models/squeezenet_v1.1 + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - interval: 1s diff --git a/tests/performance/tests/batch_inference.yaml b/tests/performance/tests/batch_inference.yaml index bd1256f5b..a8c6dd37f 100644 --- a/tests/performance/tests/batch_inference.yaml +++ b/tests/performance/tests/batch_inference.yaml @@ -17,12 +17,16 @@ modules: services: - module: shellexec prepare: - - curl -s -X POST "http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/examples/resnet-152-batching/resnet-152.mar&batch_size=8&max_batch_delay=50" + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/examples/resnet-152-batching/resnet-152.mar&batch_size=8&max_batch_delay=50" # uncomment below and comment prev and use downloaded model with model-store - #- curl -s -X POST "http://localhost:8081/models?url=resnet-152.mar&batch_size=8&max_batch_delay=60&initial_workers=1" - - curl -s -X PUT http://localhost:8081/models/resnet-152?min_worker=2&synchronous=true + #- "curl -s -X POST http://localhost:8081/models?url=resnet-152.mar&batch_size=8&max_batch_delay=60&initial_workers=1" + - "curl -s -X PUT http://localhost:8081/models/resnet-152?min_worker=2&synchronous=true" post-process: - - curl -s -X DELETE http://localhost:8081/models/resnet-152 + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - interval: 1s From 4b8807b07e461796a937ed0fb1bd29b38a03a5b7 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 24 Jun 2020 13:44:16 +0530 Subject: [PATCH 194/329] environment separation --- tests/performance/run_performance_suite.py | 17 ++++++++++--- tests/performance/tests/api_description.yaml | 12 +++++----- .../tests/batch_and_single_inference.yaml | 12 +++++----- tests/performance/tests/batch_inference.yaml | 10 ++++---- .../dev_env1/api_description.yaml | 8 +++++++ .../dev_env1/batch_and_single_inference.yaml | 9 +++++++ .../dev_env1/batch_inference.yaml | 8 +++++++ .../environments/dev_env1/health_check.yaml | 9 +++++++ .../dev_env1/inference_multiple_models.yaml | 10 ++++++++ .../dev_env1/inference_multiple_worker.yaml | 9 +++++++ .../inference_server_monitoring_criteria.yaml | 8 +++++++ .../dev_env1/inference_single_worker.yaml | 8 +++++++ ...ence_taurus_local_monitoring_criteria.yaml | 9 +++++++ .../environments/dev_env1/list_models.yaml | 8 +++++++ .../dev_env1/model_description.yaml | 8 +++++++ .../dev_env1/register_unregister.yaml | 11 +++++++++ .../register_unregister_multiple.yaml | 13 ++++++++++ .../dev_env1/scale_down_workers.yaml | 16 +++++++++++++ .../dev_env1/scale_up_workers.yaml | 15 ++++++++++++ tests/performance/tests/health_check.yaml | 12 +++++----- .../tests/inference_multiple_models.yaml | 16 ++++++------- .../tests/inference_multiple_worker.yaml | 12 +++++----- .../inference_server_monitoring_criteria.yaml | 8 +++---- .../tests/inference_single_worker.yaml | 12 +++++----- ...ence_taurus_local_monitoring_criteria.yaml | 10 ++++---- tests/performance/tests/list_models.yaml | 12 +++++----- .../performance/tests/model_description.yaml | 12 +++++----- .../tests/register_unregister.yaml | 14 +++++------ .../tests/register_unregister_multiple.yaml | 18 +++++++------- .../performance/tests/scale_down_workers.yaml | 24 +++++++++---------- tests/performance/tests/scale_up_workers.yaml | 24 +++++++++---------- 31 files changed, 267 insertions(+), 107 deletions(-) create mode 100644 tests/performance/tests/environments/dev_env1/api_description.yaml create mode 100644 tests/performance/tests/environments/dev_env1/batch_and_single_inference.yaml create mode 100644 tests/performance/tests/environments/dev_env1/batch_inference.yaml create mode 100644 tests/performance/tests/environments/dev_env1/health_check.yaml create mode 100644 tests/performance/tests/environments/dev_env1/inference_multiple_models.yaml create mode 100644 tests/performance/tests/environments/dev_env1/inference_multiple_worker.yaml create mode 100644 tests/performance/tests/environments/dev_env1/inference_server_monitoring_criteria.yaml create mode 100644 tests/performance/tests/environments/dev_env1/inference_single_worker.yaml create mode 100644 tests/performance/tests/environments/dev_env1/inference_taurus_local_monitoring_criteria.yaml create mode 100644 tests/performance/tests/environments/dev_env1/list_models.yaml create mode 100644 tests/performance/tests/environments/dev_env1/model_description.yaml create mode 100644 tests/performance/tests/environments/dev_env1/register_unregister.yaml create mode 100644 tests/performance/tests/environments/dev_env1/register_unregister_multiple.yaml create mode 100644 tests/performance/tests/environments/dev_env1/scale_down_workers.yaml create mode 100644 tests/performance/tests/environments/dev_env1/scale_up_workers.yaml diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index c5cd0cfe1..b16ced2d4 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -388,10 +388,18 @@ def get_options(artifacts_dir, jmeter_path=None): @click.option('-p', '--pattern', help='Test case file name pattern. Example --> *.yaml', default="*.yaml") @click.option('-j', '--jmeter-path', help='JMeter executable path.') @click.option('--monit/--no-monit', help='Start Monitoring server', default=True) -@click.option('--env-name', help='Unique machine id on which MMS server is running', default=socket.gethostname()) +@click.option('--env-name', help='Unique machine id on which MMS server is running', required=True) @click.option('--compare-local/--no-compare-local', help='Compare with previous run with files stored' ' in artifacts directory', default=True) def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_name, compare_local): + env_path = "{}/tests/environments".format(base_file_path) + environments = get_sub_dirs(env_path, exclude_list=[]) + + if env_name not in environments: + msg = "Provided environment (--env-name) {} is not registered in the tests/environments directory." \ + "Available environemnts are {}".format(env_name, environments) + raise Exception(msg) + commit_id = subprocess.check_output('git rev-parse --short HEAD'.split()).decode("utf-8")[:-1] artifacts_folder_name = "{}_{}_{}".format(env_name, commit_id, int(time.time())) if artifacts_dir is None: @@ -411,9 +419,12 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam with Timer("Test suite {} execution time".format(suite_name)) as t: suite_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) options_str = get_options(suite_artifacts_dir, jmeter_path) + env_yaml_path = "{}/{}/{}.yaml".format(env_path, env_name, suite_name) + env_yaml_path = "" if not os.path.exists(env_yaml_path) else env_yaml_path + with Suite(suite_name, suite_artifacts_dir, junit_xml, t) as s: - s.code, s.err = run_process("{} bzt {} {} {}".format(pre_command, options_str, - test_file, GLOBAL_CONFIG_PATH)) + s.code, s.err = run_process("{} bzt {} {} {} {}".format(pre_command, options_str, + test_file, env_yaml_path, GLOBAL_CONFIG_PATH)) metrics_log_file = glob.glob("{}/SAlogs_*".format(suite_artifacts_dir)) if metrics_log_file: diff --git a/tests/performance/tests/api_description.yaml b/tests/performance/tests/api_description.yaml index cfb168ba5..a67ae8b05 100644 --- a/tests/performance/tests/api_description.yaml +++ b/tests/performance/tests/api_description.yaml @@ -35,34 +35,34 @@ reporting: # Inbuilt Criteria - success of ManagementAPIDescription<100%, stop as failed - success of InferenceAPIDescription<100%, stop as failed - - avg-rt of ManagementAPIDescription>10ms, stop as failed - - avg-rt of InferenceAPIDescription>10ms, stop as failed + - avg-rt of ManagementAPIDescription>${AVG_RT_ManagementAPIDescription}, stop as failed + - avg-rt of InferenceAPIDescription>${AVG_RT_InferenceAPIDescription}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: 1 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: 1 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 73 + threshold: ${sum_all_file_descriptors} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: 100000000 #100MB +# threshold: ${sum_all_memory_rss} # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/batch_and_single_inference.yaml b/tests/performance/tests/batch_and_single_inference.yaml index 6fafac9a0..b4d92bca9 100644 --- a/tests/performance/tests/batch_and_single_inference.yaml +++ b/tests/performance/tests/batch_and_single_inference.yaml @@ -45,7 +45,7 @@ reporting: - subject: avg-rt # required label: 'Inference1' # optional, default is '' condition: '>' # required - threshold: 6s # required + threshold: ${Inference1} # required logic: for # optional, logic to aggregate values within timeframe. # Default 'for' means take latest, # 'within' and 'over' means take sum/avg of all values within interval @@ -55,7 +55,7 @@ reporting: - subject: avg-rt # required label: 'Inference2' # optional, default is '' condition: '>' # required - threshold: 0.08s # required + threshold: ${Inference2} # required logic: for # optional, logic to aggregate values within timeframe. # Default 'for' means take latest, # 'within' and 'over' means take sum/avg of all values within interval @@ -65,7 +65,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: 4000000000 + threshold: ${sum_workers_memory_rss} timeframe: 1s stop : true fail : true @@ -73,7 +73,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/orphans condition: '>' - threshold: 0 + threshold: ${orphans} timeframe: 1s stop : true fail : true @@ -81,7 +81,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '>' - threshold: 9 + threshold: ${total_workers} timeframe: 1s stop: true fail: true @@ -89,7 +89,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '>' - threshold: 78 + threshold: ${sum_workers_file_descriptors} timeframe: 1s stop: true fail: true diff --git a/tests/performance/tests/batch_inference.yaml b/tests/performance/tests/batch_inference.yaml index a8c6dd37f..043d72129 100644 --- a/tests/performance/tests/batch_inference.yaml +++ b/tests/performance/tests/batch_inference.yaml @@ -43,7 +43,7 @@ reporting: - subject: avg-rt # required label: 'Inference' # optional, default is '' condition: '>' # required - threshold: 6s # required + threshold: ${Inference} # required logic: for # optional, logic to aggregate values within timeframe. # Default 'for' means take latest, # 'within' and 'over' means take sum/avg of all values within interval @@ -53,7 +53,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: 3000000000 + threshold: ${sum_workers_memory_rss} timeframe: 1s stop : true fail : true @@ -61,7 +61,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/orphans condition: '>' - threshold: 0 + threshold: ${orphans} timeframe: 1s stop : true fail : true @@ -69,7 +69,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '>' - threshold: 4 + threshold: ${total_workers} timeframe: 1s stop: true fail: true @@ -77,7 +77,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '>' - threshold: 38 + threshold: ${sum_workers_file_descriptors} timeframe: 1s stop: true fail: true diff --git a/tests/performance/tests/environments/dev_env1/api_description.yaml b/tests/performance/tests/environments/dev_env1/api_description.yaml new file mode 100644 index 000000000..b81945759 --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/api_description.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + AVG_RT_ManagementAPIDescription : 10ms + AVG_RT_InferenceAPIDescription : 10ms + total_processes : 1 + sum_all_file_descriptors : 73 + sum_all_memory_rss : 100000000 #100MB diff --git a/tests/performance/tests/environments/dev_env1/batch_and_single_inference.yaml b/tests/performance/tests/environments/dev_env1/batch_and_single_inference.yaml new file mode 100644 index 000000000..38930777b --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/batch_and_single_inference.yaml @@ -0,0 +1,9 @@ +--- +settings: + env: + Inference1 : 6s + Inference2 : 0.08s + sum_workers_file_descriptors : 78 + total_workers : 9 + orphans : 0 + sum_workers_memory_rss : 4000000000 diff --git a/tests/performance/tests/environments/dev_env1/batch_inference.yaml b/tests/performance/tests/environments/dev_env1/batch_inference.yaml new file mode 100644 index 000000000..f294d535d --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/batch_inference.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + Inference : 1s + sum_workers_file_descriptors : 38 + total_workers : 4 + orphans : 0 + sum_workers_memory_rss : 3000000000 \ No newline at end of file diff --git a/tests/performance/tests/environments/dev_env1/health_check.yaml b/tests/performance/tests/environments/dev_env1/health_check.yaml new file mode 100644 index 000000000..477387117 --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/health_check.yaml @@ -0,0 +1,9 @@ + +--- +settings: + env: + success_HealthCheck : 14s + avg_rt_HealthCheck : 100% + sum_all_file_descriptors : 67 + sum_all_memory_rss : 750000000 + total_processes : 1 \ No newline at end of file diff --git a/tests/performance/tests/environments/dev_env1/inference_multiple_models.yaml b/tests/performance/tests/environments/dev_env1/inference_multiple_models.yaml new file mode 100644 index 000000000..018c6c453 --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/inference_multiple_models.yaml @@ -0,0 +1,10 @@ +--- +settings: + env: + success_Inference1 : 100% + avg_rt_Inference1 : 290ms + success_Inference2: 100% + avg_rt_Inference2: 450ms + sum_all_file_descriptors : 107 + sum_all_memory_rss : 600000000 + total_processes : 5 \ No newline at end of file diff --git a/tests/performance/tests/environments/dev_env1/inference_multiple_worker.yaml b/tests/performance/tests/environments/dev_env1/inference_multiple_worker.yaml new file mode 100644 index 000000000..2391dd514 --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/inference_multiple_worker.yaml @@ -0,0 +1,9 @@ + +--- +settings: + env: + success_Inference : 100% + avg_rt_Inference : 140ms + sum_all_file_descriptors : 126 + sum_all_memory_rss : 750000000 + total_processes : 6 \ No newline at end of file diff --git a/tests/performance/tests/environments/dev_env1/inference_server_monitoring_criteria.yaml b/tests/performance/tests/environments/dev_env1/inference_server_monitoring_criteria.yaml new file mode 100644 index 000000000..320f9372f --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/inference_server_monitoring_criteria.yaml @@ -0,0 +1,8 @@ + +--- +settings: + env: + fail : 50% + p90 : 250ms + avg_rt : 1s + sum_workers_file_descriptors : 80 diff --git a/tests/performance/tests/environments/dev_env1/inference_single_worker.yaml b/tests/performance/tests/environments/dev_env1/inference_single_worker.yaml new file mode 100644 index 000000000..656ed23f5 --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/inference_single_worker.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + success_Inference : 100% + avg_rt_Inference : 290ms + sum_all_file_descriptors : 90 + sum_all_memory_rss : 290000000 + total_processes : 3 \ No newline at end of file diff --git a/tests/performance/tests/environments/dev_env1/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/environments/dev_env1/inference_taurus_local_monitoring_criteria.yaml new file mode 100644 index 000000000..149a251a1 --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/inference_taurus_local_monitoring_criteria.yaml @@ -0,0 +1,9 @@ + +--- +settings: + env: + fail : 100% + p90 : 290ms + avg_rt : 1s + sum_workers_memory_rss : 132000000 + sum_workers_memory_rss_diff_percent : 30 diff --git a/tests/performance/tests/environments/dev_env1/list_models.yaml b/tests/performance/tests/environments/dev_env1/list_models.yaml new file mode 100644 index 000000000..fa5d2f77e --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/list_models.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + success_ListModels : 100% + avg_rt_ListModels : 14ms + sum_all_file_descriptors : 86 + sum_all_memory_rss : 185000000 + total_processes : 3 \ No newline at end of file diff --git a/tests/performance/tests/environments/dev_env1/model_description.yaml b/tests/performance/tests/environments/dev_env1/model_description.yaml new file mode 100644 index 000000000..c04a741c1 --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/model_description.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + success_ModelDescription : 100% + avg_rt_ModelDescription : 14ms + sum_all_file_descriptors : 90 + sum_all_memory_rss : 300000000 + total_processes : 3 \ No newline at end of file diff --git a/tests/performance/tests/environments/dev_env1/register_unregister.yaml b/tests/performance/tests/environments/dev_env1/register_unregister.yaml new file mode 100644 index 000000000..c5ff1754a --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/register_unregister.yaml @@ -0,0 +1,11 @@ + +--- +settings: + env: + success_RegisterModel : 100% + avg_rt_RegisterModel : 15s + success_UnRegisterModel: 100% + avg_rt_UnRegisterModel: 10ms + sum_all_file_descriptors : 66 + frontend_memory_rss : 75000000 + total_processes : 1 \ No newline at end of file diff --git a/tests/performance/tests/environments/dev_env1/register_unregister_multiple.yaml b/tests/performance/tests/environments/dev_env1/register_unregister_multiple.yaml new file mode 100644 index 000000000..e98d3589f --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/register_unregister_multiple.yaml @@ -0,0 +1,13 @@ + +--- +settings: + env: + success_RegisterModel : 100% + avg_rt_RegisterModel : 15s + success_UnRegisterModel: 100% + avg_rt_UnRegisterModel: 18ms + success_ScaleUp: 100% + avg_rt_ScaleUp: 1.5s + sum_all_file_descriptors : 73 + frontend_memory_rss : 120000000 + total_processes : 2 \ No newline at end of file diff --git a/tests/performance/tests/environments/dev_env1/scale_down_workers.yaml b/tests/performance/tests/environments/dev_env1/scale_down_workers.yaml new file mode 100644 index 000000000..10bcb3d7e --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/scale_down_workers.yaml @@ -0,0 +1,16 @@ +--- +settings: + env: + success_ScaleDown : 100% + avg_rt_ScaleDown : 10ms + min_sum_workers_file_descriptors : 23 + max_sum_workers_file_descriptors : 38 + frontend_file_descriptors : 78 + min_sum_workers_memory_rss : 210000000 + max_sum_workers_memory_rss : 450000000 + frontend_memory_rss : 290000000 + min_total_workers : 2 + max_total_workers : 4 + max_total_processes : 6 + min_total_processes : 4 + diff --git a/tests/performance/tests/environments/dev_env1/scale_up_workers.yaml b/tests/performance/tests/environments/dev_env1/scale_up_workers.yaml new file mode 100644 index 000000000..148231f8c --- /dev/null +++ b/tests/performance/tests/environments/dev_env1/scale_up_workers.yaml @@ -0,0 +1,15 @@ +--- +settings: + env: + success_ScaleUp : 100% + avg_rt_ScaleUp : 10ms + min_sum_workers_file_descriptors : 11 + max_sum_workers_file_descriptors : 38 + frontend_file_descriptors : 88 + min_sum_workers_memory_rss : 122000000 + max_sum_workers_memory_rss : 450000000 + frontend_memory_rss : 290000000 + min_total_workers : 1 + max_total_workers : 4 + max_total_processes : 6 + min_total_processes : 3 \ No newline at end of file diff --git a/tests/performance/tests/health_check.yaml b/tests/performance/tests/health_check.yaml index 8fd9a66ec..c06c95465 100644 --- a/tests/performance/tests/health_check.yaml +++ b/tests/performance/tests/health_check.yaml @@ -33,34 +33,34 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of HealthCheck<100%, stop as failed - - avg-rt of HealthCheck>14ms, stop as failed + - success of HealthCheck<${success_HealthCheck}, stop as failed + - avg-rt of HealthCheck>${avg_rt_HealthCheck}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: 1 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: 1 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 67 + threshold: ${sum_all_file_descriptors} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: 750000000 #750MB +# threshold: ${sum_all_memory_rss} # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/inference_multiple_models.yaml b/tests/performance/tests/inference_multiple_models.yaml index 7a1f6732d..c8f72faa3 100644 --- a/tests/performance/tests/inference_multiple_models.yaml +++ b/tests/performance/tests/inference_multiple_models.yaml @@ -39,36 +39,36 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of Inference1<100%, stop as failed - - success of Inference2<100%, stop as failed - - avg-rt of Inference1>290ms, stop as failed - - avg-rt of Inference2>450ms, stop as failed + - success of Inference1<${success_Inference1}, stop as failed + - success of Inference2<${success_Inference2}, stop as failed + - avg-rt of Inference1>${avg_rt_Inference1}, stop as failed + - avg-rt of Inference2>${avg_rt_Inference2}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: 5 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: 5 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 107 + threshold: ${sum_all_file_descriptors} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: 600000000 #600MB +# threshold: ${sum_all_memory_rss} #600MB # timeframe: 5s # stop : true # fail : true diff --git a/tests/performance/tests/inference_multiple_worker.yaml b/tests/performance/tests/inference_multiple_worker.yaml index 9a2f946a5..42cf5a8f7 100644 --- a/tests/performance/tests/inference_multiple_worker.yaml +++ b/tests/performance/tests/inference_multiple_worker.yaml @@ -38,34 +38,34 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of Inference<100%, stop as failed - - avg-rt of Inference>140ms, stop as failed + - success of Inference<${success_Inference}, stop as failed + - avg-rt of Inference>${avg_rt_Inference}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: 6 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: 6 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 126 + threshold: ${sum_all_file_descriptors} timeframe: 1s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: 750000000 #750MB +# threshold: ${sum_all_memory_rss} # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/inference_server_monitoring_criteria.yaml b/tests/performance/tests/inference_server_monitoring_criteria.yaml index 6002e2a70..afa450feb 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_server_monitoring_criteria.yaml @@ -28,13 +28,13 @@ services: reporting: - module: passfail criteria: - - fail >50%, stop as failed - - p90 >250ms , stop as failed - - avg-rt >1s , stop as failed + - fail >${fail}, stop as failed + - p90 >${p90} , stop as failed + - avg-rt >${avg_rt} , stop as failed - class: bzt.modules.monitoring.MonitoringCriteria subject: mms-inference-server/sum_workers_file_descriptors condition: '>' - threshold: 80 + threshold: ${sum_workers_file_descriptors} timeframe: 1s fail: true stop: true diff --git a/tests/performance/tests/inference_single_worker.yaml b/tests/performance/tests/inference_single_worker.yaml index d231b4f8a..6a60e37ad 100644 --- a/tests/performance/tests/inference_single_worker.yaml +++ b/tests/performance/tests/inference_single_worker.yaml @@ -38,34 +38,34 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of Inference<100%, stop as failed - - avg-rt of Inference>290ms, stop as failed + - success of Inference<${success_Inference}, stop as failed + - avg-rt of Inference>${avg_rt_Inference}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: 3 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: 3 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 90 + threshold: ${sum_all_file_descriptors} timeframe: 1s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: 290000000 #290MB +# threshold: ${sum_all_memory_rss} #290MB # timeframe: 5s # stop : true # fail : true diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml index 9fea0eb31..728a88e84 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml @@ -28,14 +28,14 @@ services: reporting: - module: passfail criteria: - - fail >50%, stop as failed - - p90 >250ms , stop as failed - - avg-rt >1s , stop as failed + - fail >${fail}, stop as failed + - p90 >${p90} , stop as failed + - avg-rt >${avg_rt} , stop as failed - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: 132000000 + threshold: ${sum_workers_memory_rss} timeframe: 1s stop : true fail : true - diff_percent : 30 + diff_percent : ${sum_workers_memory_rss_diff_percent} diff --git a/tests/performance/tests/list_models.yaml b/tests/performance/tests/list_models.yaml index fde041030..ca7270af8 100644 --- a/tests/performance/tests/list_models.yaml +++ b/tests/performance/tests/list_models.yaml @@ -35,34 +35,34 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of ListModels<100%, stop as failed - - avg-rt of ListModels>14ms, stop as failed + - success of ListModels<${success_ListModels}, stop as failed + - avg-rt of ListModels>${avg_rt_ListModels}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: 3 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: 3 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 86 + threshold: ${sum_all_file_descriptors} timeframe: 1s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: 185000000 #185MB +# threshold: ${sum_all_memory_rss} #185MB # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/model_description.yaml b/tests/performance/tests/model_description.yaml index 0884fe18d..35dd9a7f0 100644 --- a/tests/performance/tests/model_description.yaml +++ b/tests/performance/tests/model_description.yaml @@ -35,34 +35,34 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of ModelDescription<100%, stop as failed - - avg-rt of ModelDescription>14ms, stop as failed + - success of ModelDescription<${success_ModelDescription}, stop as failed + - avg-rt of ModelDescription>${avg_rt_ModelDescription}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: 3 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: 3 + threshold: ${total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 90 + threshold: ${sum_all_file_descriptors} timeframe: 1s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: 300000000 #300MB +# threshold: ${sum_all_memory_rss} # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/register_unregister.yaml b/tests/performance/tests/register_unregister.yaml index 5a6d2a680..108487403 100644 --- a/tests/performance/tests/register_unregister.yaml +++ b/tests/performance/tests/register_unregister.yaml @@ -35,29 +35,29 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of RegisterModel<100%, stop as failed - - success of UnregisterModel<100%, stop as failed - - avg-rt of RegisterModel>15s, stop as failed - - avg-rt of UnregisterModel>10ms, stop as failed + - success of RegisterModel<${success_RegisterModel}, stop as failed + - success of UnregisterModel<${success_UnRegisterModel}, stop as failed + - avg-rt of RegisterModel>${avg_rt_RegisterModel}, stop as failed + - avg-rt of UnregisterModel>${avg_rt_UnRegisterModel}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: 1 + threshold: ${total_processes} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 66 + threshold: ${sum_all_file_descriptors} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/frontend_memory_rss # condition: '>' -# threshold: 75000000 #75MB +# threshold: ${frontend_memory_rss} #75MB # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/register_unregister_multiple.yaml b/tests/performance/tests/register_unregister_multiple.yaml index 0fc59628e..d99294b99 100644 --- a/tests/performance/tests/register_unregister_multiple.yaml +++ b/tests/performance/tests/register_unregister_multiple.yaml @@ -36,31 +36,31 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of RegisterModel<100%, stop as failed - - success of ScaleUp<100%, stop as failed - - success of UnregisterModel<100%, stop as failed - - avg-rt of RegisterModel>15s, stop as failed - - avg-rt of ScaleUp>1.5s, stop as failed - - avg-rt of UnregisterModel>18ms, stop as failed + - success of RegisterModel<${success_RegisterModel}, stop as failed + - success of ScaleUp<${success_ScaleUp}, stop as failed + - success of UnregisterModel<${success_UnRegisterModel}, stop as failed + - avg-rt of RegisterModel>${avg_rt_RegisterModel}, stop as failed + - avg-rt of ScaleUp>${avg_rt_ScaleUp}, stop as failed + - avg-rt of UnregisterModel>${avg_rt_UnRegisterModel}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: 2 + threshold: ${total_processes} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: 73 + threshold: ${sum_all_file_descriptors} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/frontend_memory_rss # condition: '>' -# threshold: 120000000 #120MB +# threshold: ${frontend_memory_rss} #120MB # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers.yaml index c55eba736..ed071bed1 100644 --- a/tests/performance/tests/scale_down_workers.yaml +++ b/tests/performance/tests/scale_down_workers.yaml @@ -38,76 +38,76 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of ScaleDown<100%, stop as failed - - avg-rt of ScaleDown>10ms, stop as failed + - success of ScaleDown<${success_ScaleDown}, stop as failed + - avg-rt of ScaleDown>${avg_rt_ScaleDown}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: 6 + threshold: ${max_total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: 4 + threshold: ${min_total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '>' - threshold: 4 + threshold: ${max_total_workers} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '<' - threshold: 2 + threshold: ${min_total_workers} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/frontend_file_descriptors condition: '>' - threshold: 78 + threshold: ${frontend_file_descriptors} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '>' - threshold: 38 + threshold: ${max_sum_workers_file_descriptors} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '<' - threshold: 23 + threshold: ${min_sum_workers_file_descriptors} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/frontend_memory_rss # condition: '>' -# threshold: 290000000 #290MB +# threshold: ${frontend_memory_rss} #290MB # timeframe: 5s # stop : true # fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: 450000000 #450MB + threshold: ${max_sum_workers_memory_rss} #450MB timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '<' - threshold: 210000000 #230MB + threshold: ${min_sum_workers_memory_rss} #230MB timeframe: 5s stop : true fail : true \ No newline at end of file diff --git a/tests/performance/tests/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers.yaml index fbb17d237..0d584fc1a 100644 --- a/tests/performance/tests/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers.yaml @@ -38,76 +38,76 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of ScaleUp<100%, stop as failed - - avg-rt of ScaleUp>10ms, stop as failed + - success of ScaleUp<${success_ScaleUp}, stop as failed + - avg-rt of ScaleUp>${avg_rt_ScaleUp}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: 6 + threshold: ${max_total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: 3 + threshold: ${min_total_processes} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '>' - threshold: 4 + threshold: ${max_total_workers} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '<' - threshold: 1 + threshold: ${min_total_workers} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/frontend_file_descriptors condition: '>' - threshold: 88 + threshold: ${frontend_file_descriptors} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '>' - threshold: 38 + threshold: ${max_sum_workers_file_descriptors} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '<' - threshold: 11 + threshold: ${min_sum_workers_file_descriptors} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/frontend_memory_rss # condition: '>' -# threshold: 290000000 #290MB +# threshold: ${frontend_memory_rss} #290MB # timeframe: 5s # stop : true # fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: 450000000 #450MB + threshold: ${max_sum_workers_memory_rss} #450MB timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '<' - threshold: 122000000 #122MB + threshold: ${min_sum_workers_memory_rss} #122MB timeframe: 5s stop : true fail : true \ No newline at end of file From 19d4193108a5159d02f914078ab127fa428f27da Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 24 Jun 2020 15:40:39 +0530 Subject: [PATCH 195/329] test suite movement --- tests/performance/README.md | 35 ++-- tests/performance/run_performance_suite.py | 40 ++-- tests/performance/tests/api_description.jmx | 88 -------- .../api_description.yaml | 0 .../environments/dev_env1.yaml} | 0 .../batch_and_single_inference.jmx | 0 .../batch_and_single_inference.yaml | 0 .../environments/dev_env1.yaml} | 0 .../{ => batch_inference}/batch_inference.jmx | 0 .../batch_inference.yaml | 0 .../environments/dev_env1.yaml} | 0 tests/performance/tests/call_jmx.yaml | 10 - .../performance/tests/call_jmx/call_jmx.yaml | 20 ++ .../{ => call_jmx}/register_and_inference.jmx | 0 .../tests/{common => }/global_config.yaml | 0 .../environments/dev_env1.yaml} | 0 .../tests/{ => health_check}/health_check.jmx | 0 .../{ => health_check}/health_check.yaml | 0 .../environments/dev_env1.yaml} | 0 .../inference_multiple_models.jmx | 0 .../inference_multiple_models.yaml | 0 .../environments/dev_env1.yaml} | 0 .../inference_multiple_worker.jmx | 0 .../inference_multiple_worker.yaml | 0 .../inference_server_monitoring.yaml | 8 + .../register_and_inference.jmx | 189 ++++++++++++++++++ .../environments/dev_env1.yaml} | 0 .../inference_server_monitoring_criteria.yaml | 8 + .../register_and_inference.jmx | 189 ++++++++++++++++++ .../environments/dev_env1.yaml} | 0 .../inference_single_worker.jmx | 0 .../inference_single_worker.yaml | 0 .../inference_taurus_local_monitoring.yaml | 8 + .../register_and_inference.jmx | 189 ++++++++++++++++++ .../environments/dev_env1.yaml} | 0 ...ence_taurus_local_monitoring_criteria.yaml | 8 + .../register_and_inference.jmx | 189 ++++++++++++++++++ .../environments/dev_env1.yaml} | 0 .../tests/{ => list_models}/list_models.jmx | 0 .../tests/{ => list_models}/list_models.yaml | 0 .../environments/dev_env1.yaml} | 0 .../model_description.jmx | 0 .../model_description.yaml | 0 .../environments/dev_env1.yaml} | 0 .../register_unregister.jmx | 0 .../register_unregister.yaml | 0 .../environments/dev_env1.yaml} | 0 .../register_unregister_multiple.jmx | 0 .../register_unregister_multiple.yaml | 0 .../environments/dev_env1.yaml} | 0 .../scale_down_workers.jmx | 0 .../scale_down_workers.yaml | 0 .../environments/dev_env1.yaml} | 0 .../scale_up_workers.jmx | 0 .../scale_up_workers.yaml | 0 55 files changed, 839 insertions(+), 142 deletions(-) delete mode 100644 tests/performance/tests/api_description.jmx rename tests/performance/tests/{ => api_description}/api_description.yaml (100%) rename tests/performance/tests/{environments/dev_env1/api_description.yaml => api_description/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => batch_and_single_inference}/batch_and_single_inference.jmx (100%) rename tests/performance/tests/{ => batch_and_single_inference}/batch_and_single_inference.yaml (100%) rename tests/performance/tests/{environments/dev_env1/batch_and_single_inference.yaml => batch_and_single_inference/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => batch_inference}/batch_inference.jmx (100%) rename tests/performance/tests/{ => batch_inference}/batch_inference.yaml (100%) rename tests/performance/tests/{environments/dev_env1/batch_inference.yaml => batch_inference/environments/dev_env1.yaml} (100%) delete mode 100644 tests/performance/tests/call_jmx.yaml create mode 100644 tests/performance/tests/call_jmx/call_jmx.yaml rename tests/performance/tests/{ => call_jmx}/register_and_inference.jmx (100%) rename tests/performance/tests/{common => }/global_config.yaml (100%) rename tests/performance/tests/{environments/dev_env1/health_check.yaml => health_check/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => health_check}/health_check.jmx (100%) rename tests/performance/tests/{ => health_check}/health_check.yaml (100%) rename tests/performance/tests/{environments/dev_env1/inference_multiple_models.yaml => inference_multiple_models/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => inference_multiple_models}/inference_multiple_models.jmx (100%) rename tests/performance/tests/{ => inference_multiple_models}/inference_multiple_models.yaml (100%) rename tests/performance/tests/{environments/dev_env1/inference_multiple_worker.yaml => inference_multiple_worker/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => inference_multiple_worker}/inference_multiple_worker.jmx (100%) rename tests/performance/tests/{ => inference_multiple_worker}/inference_multiple_worker.yaml (100%) rename tests/performance/tests/{ => inference_server_monitoring}/inference_server_monitoring.yaml (74%) create mode 100644 tests/performance/tests/inference_server_monitoring/register_and_inference.jmx rename tests/performance/tests/{environments/dev_env1/inference_server_monitoring_criteria.yaml => inference_server_monitoring_criteria/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => inference_server_monitoring_criteria}/inference_server_monitoring_criteria.yaml (81%) create mode 100644 tests/performance/tests/inference_server_monitoring_criteria/register_and_inference.jmx rename tests/performance/tests/{environments/dev_env1/inference_single_worker.yaml => inference_single_worker/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => inference_single_worker}/inference_single_worker.jmx (100%) rename tests/performance/tests/{ => inference_single_worker}/inference_single_worker.yaml (100%) rename tests/performance/tests/{ => inference_taurus_local_monitoring}/inference_taurus_local_monitoring.yaml (68%) create mode 100644 tests/performance/tests/inference_taurus_local_monitoring/register_and_inference.jmx rename tests/performance/tests/{environments/dev_env1/inference_taurus_local_monitoring_criteria.yaml => inference_taurus_local_monitoring_criteria/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => inference_taurus_local_monitoring_criteria}/inference_taurus_local_monitoring_criteria.yaml (79%) create mode 100644 tests/performance/tests/inference_taurus_local_monitoring_criteria/register_and_inference.jmx rename tests/performance/tests/{environments/dev_env1/list_models.yaml => list_models/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => list_models}/list_models.jmx (100%) rename tests/performance/tests/{ => list_models}/list_models.yaml (100%) rename tests/performance/tests/{environments/dev_env1/model_description.yaml => model_description/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => model_description}/model_description.jmx (100%) rename tests/performance/tests/{ => model_description}/model_description.yaml (100%) rename tests/performance/tests/{environments/dev_env1/register_unregister.yaml => register_unregister/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => register_unregister}/register_unregister.jmx (100%) rename tests/performance/tests/{ => register_unregister}/register_unregister.yaml (100%) rename tests/performance/tests/{environments/dev_env1/register_unregister_multiple.yaml => register_unregister_multiple/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => register_unregister_multiple}/register_unregister_multiple.jmx (100%) rename tests/performance/tests/{ => register_unregister_multiple}/register_unregister_multiple.yaml (100%) rename tests/performance/tests/{environments/dev_env1/scale_down_workers.yaml => scale_down_workers/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => scale_down_workers}/scale_down_workers.jmx (100%) rename tests/performance/tests/{ => scale_down_workers}/scale_down_workers.yaml (100%) rename tests/performance/tests/{environments/dev_env1/scale_up_workers.yaml => scale_up_workers/environments/dev_env1.yaml} (100%) rename tests/performance/tests/{ => scale_up_workers}/scale_up_workers.jmx (100%) rename tests/performance/tests/{ => scale_up_workers}/scale_up_workers.yaml (100%) diff --git a/tests/performance/README.md b/tests/performance/README.md index 4254f317a..3c6b75c6f 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -28,31 +28,28 @@ Refer the [link](https://gettaurus.org/docs/Installation/) for more details on i 3. Make sure that `git` is installed and the test suites are run from the MMS working directory. This is used to compare performance regresssions across runs and the run artifacts are stored in a folder which have the commit SHA. ### B. Running the test suite -1. Run MMS server -2. Make sure parameters set in the [tests/common/global_config.yaml](tests/common/global_config.yaml) are correct. -3. Run the test suite runner script -4. Check the console logs, $artifacts-dir$//junit.html report, comparison.csv and other artifacts. +1. Make sure parameters set in the [tests/common/global_config.yaml](tests/performance/tests/global_config.yaml) are correct. +2. Run the test suite runner script +3. Check the console logs, $artifacts-dir$//junit.html report, comparison.csv and other artifacts. **Steps are provided below** ```bash export MMS_HOME= cd $MMS_HOME/tests/performance - # Run the command below in different terminal to start MMS - # multi-model-server --start - + + # Note that MMS server started and stopped by the individual test suite. # check variables # vi tests/common/global_config.yaml - # jpeg download command for quick reference. Set input_filepath in global_config.yaml - # curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg + - python -m run_performance_suite --artifacts-dir='' --pattern='*criteria*.yaml' + python -m run_performance_suite --pattern='*' ``` ### C. Understanding the test suite artifacts and reports 1. The $artifacts-dir$//junit.html contains the summary report of the test run. Note that each test yaml is treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not specified in the yaml, test suite is marked as skipped with 0 test cases. -2. For each test yaml a sub-directory is created with artifacts for it. +2. For each test suite a sub-directory is created with artifacts for it. 3. The comparison.csv contains diff for monitoring metrics between an ongoing run and a previous run which was ran for same MMS server. @@ -66,9 +63,9 @@ To add test case follow steps below. #### 1. Add scenario You can specify the test scenarios, in the scenario section of the yaml. -To get you started quickly, we have provided a sample JMeter script and a Taurus yaml file [here](tests/register_and_inference.jmx) and [here](tests/call_jmx.yaml) . +To get you started quickly, we have provided a sample JMeter script and a Taurus yaml file [here](tests/call_jmx/register_and_inference.jmx) and [here](tests/call_jmx/call_jmx.yaml) . -Here is how the sample call_jmx.yaml looks like. Note variables used by jmx script are specified in [tests/common/global_config.yaml](tests/common/global_config.yaml) file. +Here is how the sample call_jmx.yaml looks like. Note variables used by jmx script are specified in [tests/global_config.yaml](tests/performance/tests/global_config.yaml) file. ```yaml execution: @@ -88,7 +85,7 @@ To run this individual test using Taurus(bzt) run commands below: ```bash export MMS_HOME= cd $MMS_HOME/tests/performance - python -m run_performance_suite -p call_jmx.yaml + python -m run_performance_suite -p call_jmx ``` **Note**: @@ -115,7 +112,7 @@ Metrics can be monitored in two ways: python $MMS_HOME/tests/performance/metrics_monitoring_server.py --start ``` - Sample yaml with monitoring section config. Complete yaml can be found [here](tests/inference_server_monitoring.yaml) + Sample yaml with monitoring section config. Complete yaml can be found [here](tests/inference_server_monitoring/inference_server_monitoring.yaml) ```yaml services: @@ -137,7 +134,7 @@ Metrics can be monitored in two ways: ```bash export MMS_HOME= cd $MMS_HOME/tests/performance - python -m run_performance_suite -p inference_server_monitoring.yaml + python -m run_performance_suite -p inference_server_monitoring ``` @@ -156,7 +153,7 @@ Metrics can be monitored in two ways: export PYTHONPATH=$MMS_HOME/tests/performance:$PYTHONPATH ``` - Relevant test yaml sections. Test yaml can be found [here](tests/inference_taurus_local_monitoring.yaml) + Relevant test yaml sections. Test yaml can be found [here](tests/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml) ```yaml modules: @@ -181,7 +178,7 @@ Metrics can be monitored in two ways: ```bash export MMS_HOME= cd $MMS_HOME/tests/performance - python -m run_performance_suite -p inference_taurus_local_monitoring.yaml + python -m run_performance_suite -p inference_taurus_local_monitoring ``` #### 3.1 Add pass/fail criteria @@ -279,4 +276,4 @@ reporting: More details about our testing strategy and test cases can be found [here](TESTS.md) ## TODOs -1. Auto threshold calculation, environment profiles +1. Auto threshold calculation diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index b16ced2d4..76380de52 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -42,7 +42,7 @@ metrics_monitoring_server = "agents/metrics_monitoring_server.py" base_file_path = pathlib.Path(__file__).parent.absolute() run_artifacts_path = "{}/run_artifacts/".format(base_file_path) -GLOBAL_CONFIG_PATH = "{}/tests/common/global_config.yaml".format(base_file_path) +GLOBAL_CONFIG_PATH = "{}/tests/global_config.yaml".format(base_file_path) S3_BUCKET = configuration.get('suite', 's3_bucket') @@ -237,13 +237,17 @@ def run_process(cmd, wait=True): p = subprocess.Popen(cmd, shell=True) return p.returncode, '' -def get_sub_dirs(dir, exclude_list=['comp_data']): + +def get_sub_dirs(dir, exclude_list=['comp_data'], include_pattern='*'): dir = dir.strip() if not os.path.exists(dir): msg = "The path {} does not exit".format(dir) logger.error("The path {} does not exit".format(dir)) raise Exception(msg) - return list([x for x in os.listdir(dir) if os.path.isdir(dir + "/" + x) and x not in exclude_list]) + + pattern_list = glob.glob(dir + "/" + include_pattern) + return list([x for x in os.listdir(dir) if os.path.isdir(dir + "/" + x) and x not in exclude_list + and dir + "/" + x in pattern_list]) def get_mon_metrics_list(test_yaml): @@ -363,14 +367,6 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): return over_all_pass -def get_test_yamls(dir_path=None, pattern="*.yaml"): - if not dir_path: - dir_path = str(base_file_path) + "/tests" - - path_pattern = "{}/{}".format(dir_path, pattern) - return glob.glob(path_pattern) - - def get_options(artifacts_dir, jmeter_path=None): options=[] if jmeter_path: @@ -385,20 +381,15 @@ def get_options(artifacts_dir, jmeter_path=None): @click.command() @click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True)) @click.option('-t', '--test-dir', help='Directory containing tests.', type=click.Path(exists=True), default=None) -@click.option('-p', '--pattern', help='Test case file name pattern. Example --> *.yaml', default="*.yaml") +@click.option('-p', '--pattern', help='Test case folder name pattern', default="*") @click.option('-j', '--jmeter-path', help='JMeter executable path.') @click.option('--monit/--no-monit', help='Start Monitoring server', default=True) @click.option('--env-name', help='Unique machine id on which MMS server is running', required=True) @click.option('--compare-local/--no-compare-local', help='Compare with previous run with files stored' ' in artifacts directory', default=True) def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_name, compare_local): - env_path = "{}/tests/environments".format(base_file_path) - environments = get_sub_dirs(env_path, exclude_list=[]) - - if env_name not in environments: - msg = "Provided environment (--env-name) {} is not registered in the tests/environments directory." \ - "Available environemnts are {}".format(env_name, environments) - raise Exception(msg) + if test_dir is None: + test_dir = "{}/tests".format(base_file_path) commit_id = subprocess.check_output('git rev-parse --short HEAD'.split()).decode("utf-8")[:-1] artifacts_folder_name = "{}_{}_{}".format(env_name, commit_id, int(time.time())) @@ -412,16 +403,15 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam with Monitoring(base_file_path, monit): junit_xml = JUnitXml() pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(base_file_path)) - test_yamls = get_test_yamls(test_dir, pattern) - logger.info("Collected test yamls {}".format(test_yamls)) - for test_file in tqdm(test_yamls, desc="Test Suites"): - suite_name = os.path.basename(test_file).rsplit('.', 1)[0] + test_dirs = get_sub_dirs(test_dir, exclude_list=[], include_pattern=pattern) + logger.info("Collected tests {}".format(test_dirs)) + for suite_name in tqdm(test_dirs, desc="Test Suites"): with Timer("Test suite {} execution time".format(suite_name)) as t: suite_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) options_str = get_options(suite_artifacts_dir, jmeter_path) - env_yaml_path = "{}/{}/{}.yaml".format(env_path, env_name, suite_name) + env_yaml_path = "{}/{}/environments/{}.yaml".format(test_dir, suite_name, env_name) env_yaml_path = "" if not os.path.exists(env_yaml_path) else env_yaml_path - + test_file = "{}/{}/{}.yaml".format(test_dir, suite_name, suite_name) with Suite(suite_name, suite_artifacts_dir, junit_xml, t) as s: s.code, s.err = run_process("{} bzt {} {} {} {}".format(pre_command, options_str, test_file, env_yaml_path, GLOBAL_CONFIG_PATH)) diff --git a/tests/performance/tests/api_description.jmx b/tests/performance/tests/api_description.jmx deleted file mode 100644 index a026cb312..000000000 --- a/tests/performance/tests/api_description.jmx +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - false - true - false - - - - - - - - - - - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - - 6 - - - - - - continue - - false - ${__P(loops,4)} - - ${__P(threads,10)} - ${__P(rampup,10)} - false - - - true - - - - - - - - ${__P(management_port,8444)} - - - - OPTIONS - true - false - true - false - - - - - - - - - - - ${__P(port,8443)} - - - - OPTIONS - true - false - true - false - - - - - - - - - diff --git a/tests/performance/tests/api_description.yaml b/tests/performance/tests/api_description/api_description.yaml similarity index 100% rename from tests/performance/tests/api_description.yaml rename to tests/performance/tests/api_description/api_description.yaml diff --git a/tests/performance/tests/environments/dev_env1/api_description.yaml b/tests/performance/tests/api_description/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/api_description.yaml rename to tests/performance/tests/api_description/environments/dev_env1.yaml diff --git a/tests/performance/tests/batch_and_single_inference.jmx b/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.jmx similarity index 100% rename from tests/performance/tests/batch_and_single_inference.jmx rename to tests/performance/tests/batch_and_single_inference/batch_and_single_inference.jmx diff --git a/tests/performance/tests/batch_and_single_inference.yaml b/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml similarity index 100% rename from tests/performance/tests/batch_and_single_inference.yaml rename to tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml diff --git a/tests/performance/tests/environments/dev_env1/batch_and_single_inference.yaml b/tests/performance/tests/batch_and_single_inference/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/batch_and_single_inference.yaml rename to tests/performance/tests/batch_and_single_inference/environments/dev_env1.yaml diff --git a/tests/performance/tests/batch_inference.jmx b/tests/performance/tests/batch_inference/batch_inference.jmx similarity index 100% rename from tests/performance/tests/batch_inference.jmx rename to tests/performance/tests/batch_inference/batch_inference.jmx diff --git a/tests/performance/tests/batch_inference.yaml b/tests/performance/tests/batch_inference/batch_inference.yaml similarity index 100% rename from tests/performance/tests/batch_inference.yaml rename to tests/performance/tests/batch_inference/batch_inference.yaml diff --git a/tests/performance/tests/environments/dev_env1/batch_inference.yaml b/tests/performance/tests/batch_inference/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/batch_inference.yaml rename to tests/performance/tests/batch_inference/environments/dev_env1.yaml diff --git a/tests/performance/tests/call_jmx.yaml b/tests/performance/tests/call_jmx.yaml deleted file mode 100644 index 0332cc6d9..000000000 --- a/tests/performance/tests/call_jmx.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 1s - hold-for: 40s - scenario: Inference -scenarios: - Inference: - script: register_and_inference.jmx - diff --git a/tests/performance/tests/call_jmx/call_jmx.yaml b/tests/performance/tests/call_jmx/call_jmx.yaml new file mode 100644 index 000000000..2cd4f348d --- /dev/null +++ b/tests/performance/tests/call_jmx/call_jmx.yaml @@ -0,0 +1,20 @@ +--- +execution: +- concurrency: 1 + ramp-up: 1s + hold-for: 40s + scenario: Inference +scenarios: + Inference: + script: register_and_inference.jmx + +services: + - module: shellexec + prepare: + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 10s" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" + diff --git a/tests/performance/tests/register_and_inference.jmx b/tests/performance/tests/call_jmx/register_and_inference.jmx similarity index 100% rename from tests/performance/tests/register_and_inference.jmx rename to tests/performance/tests/call_jmx/register_and_inference.jmx diff --git a/tests/performance/tests/common/global_config.yaml b/tests/performance/tests/global_config.yaml similarity index 100% rename from tests/performance/tests/common/global_config.yaml rename to tests/performance/tests/global_config.yaml diff --git a/tests/performance/tests/environments/dev_env1/health_check.yaml b/tests/performance/tests/health_check/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/health_check.yaml rename to tests/performance/tests/health_check/environments/dev_env1.yaml diff --git a/tests/performance/tests/health_check.jmx b/tests/performance/tests/health_check/health_check.jmx similarity index 100% rename from tests/performance/tests/health_check.jmx rename to tests/performance/tests/health_check/health_check.jmx diff --git a/tests/performance/tests/health_check.yaml b/tests/performance/tests/health_check/health_check.yaml similarity index 100% rename from tests/performance/tests/health_check.yaml rename to tests/performance/tests/health_check/health_check.yaml diff --git a/tests/performance/tests/environments/dev_env1/inference_multiple_models.yaml b/tests/performance/tests/inference_multiple_models/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/inference_multiple_models.yaml rename to tests/performance/tests/inference_multiple_models/environments/dev_env1.yaml diff --git a/tests/performance/tests/inference_multiple_models.jmx b/tests/performance/tests/inference_multiple_models/inference_multiple_models.jmx similarity index 100% rename from tests/performance/tests/inference_multiple_models.jmx rename to tests/performance/tests/inference_multiple_models/inference_multiple_models.jmx diff --git a/tests/performance/tests/inference_multiple_models.yaml b/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml similarity index 100% rename from tests/performance/tests/inference_multiple_models.yaml rename to tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml diff --git a/tests/performance/tests/environments/dev_env1/inference_multiple_worker.yaml b/tests/performance/tests/inference_multiple_worker/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/inference_multiple_worker.yaml rename to tests/performance/tests/inference_multiple_worker/environments/dev_env1.yaml diff --git a/tests/performance/tests/inference_multiple_worker.jmx b/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.jmx similarity index 100% rename from tests/performance/tests/inference_multiple_worker.jmx rename to tests/performance/tests/inference_multiple_worker/inference_multiple_worker.jmx diff --git a/tests/performance/tests/inference_multiple_worker.yaml b/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml similarity index 100% rename from tests/performance/tests/inference_multiple_worker.yaml rename to tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml diff --git a/tests/performance/tests/inference_server_monitoring.yaml b/tests/performance/tests/inference_server_monitoring/inference_server_monitoring.yaml similarity index 74% rename from tests/performance/tests/inference_server_monitoring.yaml rename to tests/performance/tests/inference_server_monitoring/inference_server_monitoring.yaml index 020a1ce2a..1146848cd 100644 --- a/tests/performance/tests/inference_server_monitoring.yaml +++ b/tests/performance/tests/inference_server_monitoring/inference_server_monitoring.yaml @@ -12,6 +12,14 @@ scenarios: services: + - module: shellexec + prepare: + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 10s" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" - module: monitoring server-agent: - address: localhost:9009 # metric monitoring service address diff --git a/tests/performance/tests/inference_server_monitoring/register_and_inference.jmx b/tests/performance/tests/inference_server_monitoring/register_and_inference.jmx new file mode 100644 index 000000000..0d60d304c --- /dev/null +++ b/tests/performance/tests/inference_server_monitoring/register_and_inference.jmx @@ -0,0 +1,189 @@ + + + + + + false + true + false + + + + + + + + + + cnn_url + https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + = + The url from where to fetch noop model from + + + scale_up_workers + ${__P(min_workers,1)} + = + The workers to scale No op model to + + + scale_down_workers + 0 + Offload the No-Op Model + = + + + model + squeezenet_v1.1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${cnn_url} + POST + true + false + true + false + + + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_up_workers} + PUT + true + false + true + false + + + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_down_workers} + DELETE + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/environments/dev_env1/inference_server_monitoring_criteria.yaml b/tests/performance/tests/inference_server_monitoring_criteria/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/inference_server_monitoring_criteria.yaml rename to tests/performance/tests/inference_server_monitoring_criteria/environments/dev_env1.yaml diff --git a/tests/performance/tests/inference_server_monitoring_criteria.yaml b/tests/performance/tests/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml similarity index 81% rename from tests/performance/tests/inference_server_monitoring_criteria.yaml rename to tests/performance/tests/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml index afa450feb..c71941570 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml @@ -12,6 +12,14 @@ scenarios: services: + - module: shellexec + prepare: + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 10s" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" - module: monitoring server-agent: - address: localhost:9009 # metric monitoring service address diff --git a/tests/performance/tests/inference_server_monitoring_criteria/register_and_inference.jmx b/tests/performance/tests/inference_server_monitoring_criteria/register_and_inference.jmx new file mode 100644 index 000000000..0d60d304c --- /dev/null +++ b/tests/performance/tests/inference_server_monitoring_criteria/register_and_inference.jmx @@ -0,0 +1,189 @@ + + + + + + false + true + false + + + + + + + + + + cnn_url + https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + = + The url from where to fetch noop model from + + + scale_up_workers + ${__P(min_workers,1)} + = + The workers to scale No op model to + + + scale_down_workers + 0 + Offload the No-Op Model + = + + + model + squeezenet_v1.1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${cnn_url} + POST + true + false + true + false + + + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_up_workers} + PUT + true + false + true + false + + + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_down_workers} + DELETE + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/environments/dev_env1/inference_single_worker.yaml b/tests/performance/tests/inference_single_worker/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/inference_single_worker.yaml rename to tests/performance/tests/inference_single_worker/environments/dev_env1.yaml diff --git a/tests/performance/tests/inference_single_worker.jmx b/tests/performance/tests/inference_single_worker/inference_single_worker.jmx similarity index 100% rename from tests/performance/tests/inference_single_worker.jmx rename to tests/performance/tests/inference_single_worker/inference_single_worker.jmx diff --git a/tests/performance/tests/inference_single_worker.yaml b/tests/performance/tests/inference_single_worker/inference_single_worker.yaml similarity index 100% rename from tests/performance/tests/inference_single_worker.yaml rename to tests/performance/tests/inference_single_worker/inference_single_worker.yaml diff --git a/tests/performance/tests/inference_taurus_local_monitoring.yaml b/tests/performance/tests/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml similarity index 68% rename from tests/performance/tests/inference_taurus_local_monitoring.yaml rename to tests/performance/tests/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml index 0369b101c..29c558f73 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml @@ -14,6 +14,14 @@ modules: class : metrics_monitoring_taurus.Monitor # monitoring class. services: + - module: shellexec + prepare: + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 10s" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - interval: 1s diff --git a/tests/performance/tests/inference_taurus_local_monitoring/register_and_inference.jmx b/tests/performance/tests/inference_taurus_local_monitoring/register_and_inference.jmx new file mode 100644 index 000000000..0d60d304c --- /dev/null +++ b/tests/performance/tests/inference_taurus_local_monitoring/register_and_inference.jmx @@ -0,0 +1,189 @@ + + + + + + false + true + false + + + + + + + + + + cnn_url + https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + = + The url from where to fetch noop model from + + + scale_up_workers + ${__P(min_workers,1)} + = + The workers to scale No op model to + + + scale_down_workers + 0 + Offload the No-Op Model + = + + + model + squeezenet_v1.1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${cnn_url} + POST + true + false + true + false + + + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_up_workers} + PUT + true + false + true + false + + + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_down_workers} + DELETE + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/environments/dev_env1/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/inference_taurus_local_monitoring_criteria.yaml rename to tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/dev_env1.yaml diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml similarity index 79% rename from tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml rename to tests/performance/tests/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml index 728a88e84..3597e75c3 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml @@ -15,6 +15,14 @@ modules: class : metrics_monitoring_taurus.Monitor # monitoring class. services: + - module: shellexec + prepare: + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 10s" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor - interval: 1s diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria/register_and_inference.jmx b/tests/performance/tests/inference_taurus_local_monitoring_criteria/register_and_inference.jmx new file mode 100644 index 000000000..0d60d304c --- /dev/null +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria/register_and_inference.jmx @@ -0,0 +1,189 @@ + + + + + + false + true + false + + + + + + + + + + cnn_url + https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar + = + The url from where to fetch noop model from + + + scale_up_workers + ${__P(min_workers,1)} + = + The workers to scale No op model to + + + scale_down_workers + 0 + Offload the No-Op Model + = + + + model + squeezenet_v1.1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models?url=${cnn_url} + POST + true + false + true + false + + + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_up_workers} + PUT + true + false + true + false + + + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + /models/${model}?min_worker=${scale_down_workers} + DELETE + true + false + true + false + + + + + + + + + diff --git a/tests/performance/tests/environments/dev_env1/list_models.yaml b/tests/performance/tests/list_models/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/list_models.yaml rename to tests/performance/tests/list_models/environments/dev_env1.yaml diff --git a/tests/performance/tests/list_models.jmx b/tests/performance/tests/list_models/list_models.jmx similarity index 100% rename from tests/performance/tests/list_models.jmx rename to tests/performance/tests/list_models/list_models.jmx diff --git a/tests/performance/tests/list_models.yaml b/tests/performance/tests/list_models/list_models.yaml similarity index 100% rename from tests/performance/tests/list_models.yaml rename to tests/performance/tests/list_models/list_models.yaml diff --git a/tests/performance/tests/environments/dev_env1/model_description.yaml b/tests/performance/tests/model_description/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/model_description.yaml rename to tests/performance/tests/model_description/environments/dev_env1.yaml diff --git a/tests/performance/tests/model_description.jmx b/tests/performance/tests/model_description/model_description.jmx similarity index 100% rename from tests/performance/tests/model_description.jmx rename to tests/performance/tests/model_description/model_description.jmx diff --git a/tests/performance/tests/model_description.yaml b/tests/performance/tests/model_description/model_description.yaml similarity index 100% rename from tests/performance/tests/model_description.yaml rename to tests/performance/tests/model_description/model_description.yaml diff --git a/tests/performance/tests/environments/dev_env1/register_unregister.yaml b/tests/performance/tests/register_unregister/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/register_unregister.yaml rename to tests/performance/tests/register_unregister/environments/dev_env1.yaml diff --git a/tests/performance/tests/register_unregister.jmx b/tests/performance/tests/register_unregister/register_unregister.jmx similarity index 100% rename from tests/performance/tests/register_unregister.jmx rename to tests/performance/tests/register_unregister/register_unregister.jmx diff --git a/tests/performance/tests/register_unregister.yaml b/tests/performance/tests/register_unregister/register_unregister.yaml similarity index 100% rename from tests/performance/tests/register_unregister.yaml rename to tests/performance/tests/register_unregister/register_unregister.yaml diff --git a/tests/performance/tests/environments/dev_env1/register_unregister_multiple.yaml b/tests/performance/tests/register_unregister_multiple/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/register_unregister_multiple.yaml rename to tests/performance/tests/register_unregister_multiple/environments/dev_env1.yaml diff --git a/tests/performance/tests/register_unregister_multiple.jmx b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.jmx similarity index 100% rename from tests/performance/tests/register_unregister_multiple.jmx rename to tests/performance/tests/register_unregister_multiple/register_unregister_multiple.jmx diff --git a/tests/performance/tests/register_unregister_multiple.yaml b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml similarity index 100% rename from tests/performance/tests/register_unregister_multiple.yaml rename to tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml diff --git a/tests/performance/tests/environments/dev_env1/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/scale_down_workers.yaml rename to tests/performance/tests/scale_down_workers/environments/dev_env1.yaml diff --git a/tests/performance/tests/scale_down_workers.jmx b/tests/performance/tests/scale_down_workers/scale_down_workers.jmx similarity index 100% rename from tests/performance/tests/scale_down_workers.jmx rename to tests/performance/tests/scale_down_workers/scale_down_workers.jmx diff --git a/tests/performance/tests/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers/scale_down_workers.yaml similarity index 100% rename from tests/performance/tests/scale_down_workers.yaml rename to tests/performance/tests/scale_down_workers/scale_down_workers.yaml diff --git a/tests/performance/tests/environments/dev_env1/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/environments/dev_env1/scale_up_workers.yaml rename to tests/performance/tests/scale_up_workers/environments/dev_env1.yaml diff --git a/tests/performance/tests/scale_up_workers.jmx b/tests/performance/tests/scale_up_workers/scale_up_workers.jmx similarity index 100% rename from tests/performance/tests/scale_up_workers.jmx rename to tests/performance/tests/scale_up_workers/scale_up_workers.jmx diff --git a/tests/performance/tests/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers/scale_up_workers.yaml similarity index 100% rename from tests/performance/tests/scale_up_workers.yaml rename to tests/performance/tests/scale_up_workers/scale_up_workers.yaml From 6230213503f99dbf1eccf0d7bae20616219b35f0 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 17:49:50 +0530 Subject: [PATCH 196/329] added missing api_desc.jmx --- .../tests/api_description/api_description.jmx | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 tests/performance/tests/api_description/api_description.jmx diff --git a/tests/performance/tests/api_description/api_description.jmx b/tests/performance/tests/api_description/api_description.jmx new file mode 100644 index 000000000..a026cb312 --- /dev/null +++ b/tests/performance/tests/api_description/api_description.jmx @@ -0,0 +1,88 @@ + + + + + + false + true + false + + + + + + + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + + ${__P(management_port,8444)} + + + + OPTIONS + true + false + true + false + + + + + + + + + + + ${__P(port,8443)} + + + + OPTIONS + true + false + true + false + + + + + + + + + From f9087f680fad2883224b9aebc245dd38c4297b24 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 17:50:09 +0530 Subject: [PATCH 197/329] updated batch inference response time --- .../tests/batch_inference/environments/dev_env1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/tests/batch_inference/environments/dev_env1.yaml b/tests/performance/tests/batch_inference/environments/dev_env1.yaml index f294d535d..c9de1ab34 100644 --- a/tests/performance/tests/batch_inference/environments/dev_env1.yaml +++ b/tests/performance/tests/batch_inference/environments/dev_env1.yaml @@ -1,7 +1,7 @@ --- settings: env: - Inference : 1s + Inference : 1.5s sum_workers_file_descriptors : 38 total_workers : 4 orphans : 0 From fc4d5cd92e4372a4410bb8093fb8a4d46145b870 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 17:50:31 +0530 Subject: [PATCH 198/329] corrected health check variable values --- .../tests/health_check/environments/dev_env1.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/performance/tests/health_check/environments/dev_env1.yaml b/tests/performance/tests/health_check/environments/dev_env1.yaml index 477387117..f1d6cd14e 100644 --- a/tests/performance/tests/health_check/environments/dev_env1.yaml +++ b/tests/performance/tests/health_check/environments/dev_env1.yaml @@ -2,8 +2,8 @@ --- settings: env: - success_HealthCheck : 14s - avg_rt_HealthCheck : 100% + success_HealthCheck : 100% + avg_rt_HealthCheck : 14ms + total_processes : 1 sum_all_file_descriptors : 67 - sum_all_memory_rss : 750000000 - total_processes : 1 \ No newline at end of file + sum_all_memory_rss : 750000000 #750MB \ No newline at end of file From d15fe77e0405462aba5cd8478812e6c6cc994355 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 17:50:48 +0530 Subject: [PATCH 199/329] tweaked workers memory for scale up --- .../tests/scale_up_workers/environments/dev_env1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/tests/scale_up_workers/environments/dev_env1.yaml b/tests/performance/tests/scale_up_workers/environments/dev_env1.yaml index 148231f8c..dd6932853 100644 --- a/tests/performance/tests/scale_up_workers/environments/dev_env1.yaml +++ b/tests/performance/tests/scale_up_workers/environments/dev_env1.yaml @@ -6,7 +6,7 @@ settings: min_sum_workers_file_descriptors : 11 max_sum_workers_file_descriptors : 38 frontend_file_descriptors : 88 - min_sum_workers_memory_rss : 122000000 + min_sum_workers_memory_rss : 115000000 max_sum_workers_memory_rss : 450000000 frontend_memory_rss : 290000000 min_total_workers : 1 From d305e967eb718a9d0b47a5a960a2d62960c69383 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 24 Jun 2020 17:55:25 +0530 Subject: [PATCH 200/329] comparison html and xml report --- tests/performance/run_performance_suite.py | 196 +++++++++++++-------- 1 file changed, 126 insertions(+), 70 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 76380de52..ffdb4319f 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -97,12 +97,13 @@ def __exit__(self, type, value, traceback): class Suite(object): - def __init__(self, name, artifacts_dir, junit_xml, timer): + def __init__(self, name, artifacts_dir, junit_xml, timer, env_name): self.ts = TestSuite(name) self.name = name self.junit_xml = junit_xml self.timer = timer self.artifacts_dir = artifacts_dir + self.env_name = env_name def __enter__(self): return self @@ -136,7 +137,7 @@ def __exit__(self, type, value, traceback): tc.result = Skipped() self.ts.add_testcase(tc) - self.ts.hostname = socket.gethostname() + self.ts.hostname = self.env_name self.ts.timestamp = self.timer.start self.ts.time = self.timer.diff() self.ts.tests = tests @@ -159,7 +160,7 @@ def get_latest(names, env_id, exclude_run_name): max_ts = 0 latest_run = '' for run_name in names: - run_name_list = run_name.split('_') + run_name_list = run_name.split('__') if env_id == run_name_list[0] and run_name != exclude_run_name: if int(run_name_list[2]) > max_ts: max_ts = int(run_name_list[2]) @@ -170,6 +171,11 @@ def get_latest(names, env_id, exclude_run_name): def get_dir_to_compare(self): parent_dir = pathlib.Path(self.artifacts_dir).parent names = [di for di in os.listdir(parent_dir) if os.path.isdir(os.path.join(parent_dir, di))] + print("-----------") + print(names) + print(parent_dir) + print(self.env_name) + print("-----------") latest_run = self.get_latest(names, self.env_name, self.current_run_name) return os.path.join(parent_dir, latest_run), latest_run @@ -178,9 +184,11 @@ def store_results(self): def compare(self): compare_dir, compare_run_name = self.get_dir_to_compare() - if compare_dir: + if compare_run_name: self.result = compare_artifacts(self.artifacts_dir, compare_dir, self.artifacts_dir, self.current_run_name, compare_run_name) + else: + logger.warning("The latest run not found for env {}".format(self.env_name)) self.store_results() return self.result @@ -265,7 +273,7 @@ def get_mon_metrics_list(test_yaml): def get_compare_metric_list(dir, sub_dir): diff_percents = [] metrics = [] - test_yaml = "{}/{}/{}.yaml".format(dir, sub_dir, sub_dir) + test_yaml = "{}/{}/{}".format(dir, sub_dir, "effective.yml") with open(test_yaml) as test_yaml: test_yaml = yaml.safe_load(test_yaml) for rep_section in test_yaml.get('reporting', []): @@ -294,72 +302,116 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): aggregates = ["mean", "max", "min"] header = ["run_name1", "run_name2", "test_suite", "metric", "run1", "run2", "percentage_diff", "result"] rows = [header] + + junit_xml = JUnitXml() for sub_dir1 in sub_dirs_1: - if sub_dir1 in sub_dirs_2: - metrics_file1 = glob.glob("{}/{}/SAlogs_*".format(dir1, sub_dir1)) - metrics_file2 = glob.glob("{}/{}/SAlogs_*".format(dir2, sub_dir1)) - if not (metrics_file1 and metrics_file2): - metrics_file1 = glob.glob("{}/{}/local_*".format(dir1, sub_dir1)) - metrics_file2 = glob.glob("{}/{}/local_*".format(dir2, sub_dir1)) + with Timer("Comparison test suite {} execution time".format(sub_dir1)) as t: + ts = TestSuite(sub_dir1) + errors,failures, skipped, tests = 0, 0, 0, 0 + if sub_dir1 in sub_dirs_2: + metrics_file1 = glob.glob("{}/{}/SAlogs_*".format(dir1, sub_dir1)) + metrics_file2 = glob.glob("{}/{}/SAlogs_*".format(dir2, sub_dir1)) if not (metrics_file1 and metrics_file2): - logger.info("Metrics monitoring logs are not captured for {} in either of the runs.".format(sub_dir1)) - rows.append([run_name1, run_name2, sub_dir1, "log_file", sub_dir1, sub_dir1, "metrics are not captured for either of the runs", "pass"]) - continue + metrics_file1 = glob.glob("{}/{}/local_*".format(dir1, sub_dir1)) + metrics_file2 = glob.glob("{}/{}/local_*".format(dir2, sub_dir1)) + if not (metrics_file1 and metrics_file2): + logger.info("Metrics monitoring logs are not captured for {} in either of the runs.".format(sub_dir1)) + rows.append([run_name1, run_name2, sub_dir1, "log_file", sub_dir1, sub_dir1, "metrics are not captured for either of the runs", "pass"]) + tc = TestCase("metrics_file_present") + tc.result = Skipped("Metrics are not captured for either of the runs") + ts.add_testcase(tc) + skipped += 1 + continue - metrics_from_file1 = pd.read_csv(metrics_file1[0]) - metrics_from_file2 = pd.read_csv(metrics_file2[0]) + metrics_from_file1 = pd.read_csv(metrics_file1[0]) + metrics_from_file2 = pd.read_csv(metrics_file2[0]) - metrics, diff_percents = get_compare_metric_list(dir1, sub_dir1) - for col, diff_percent in zip(metrics, diff_percents): - for agg_func in aggregates: - name = "{}_{}".format(agg_func, str(col)) - try: - val1 = getattr(metrics_from_file1[str(col)], agg_func)() - except TypeError: - val1 = "NULL" - if str(col) in metrics_from_file2: + metrics, diff_percents = get_compare_metric_list(dir1, sub_dir1) + for col, diff_percent in zip(metrics, diff_percents): + for agg_func in aggregates: + name = "{}_{}".format(agg_func, str(col)) try: - val2 = getattr(metrics_from_file2[str(col)], agg_func)() + val1 = getattr(metrics_from_file1[str(col)], agg_func)() except TypeError: - val2 = "NULL" - else: - val2 = "NA" - - if val1 == val2 and val1 == "NULL": - diff = "NULL" - pass_fail = "pass" - elif val1 == "NULL": - diff = val2 - pass_fail = "fail" - elif val2 == "NULL": - diff = val1 - pass_fail = "fail" - elif val2 == "NA": - diff = val2 - pass_fail = "pass" - else: - try: - if val2 != val1: - diff = (abs(val2 - val1) / ((val2 + val1)/2)) * 100 - pass_fail = "pass" if diff < diff_percent else "fail" - else: # special case of 0 - pass_fail = "pass" - diff = 0 - - except Exception as e: - logger.info("error while calculating the diff {}".format(str(e))) - diff = str(e) + val1 = "NULL" + + if str(col) in metrics_from_file2: + try: + val2 = getattr(metrics_from_file2[str(col)], agg_func)() + except TypeError: + val2 = "NULL" + else: + val2 = "NA" + + if val1 == val2 and val1 == "NULL": + diff = "NULL" + pass_fail = "pass" + elif val1 == "NULL": + diff = val2 pass_fail = "fail" - - if over_all_pass: - over_all_pass = pass_fail == "pass" - rows.append([run_name1, run_name2, sub_dir1, name, val1, val2, diff, pass_fail]) - else: - rows.append([run_name1, run_name2, sub_dir1, "log_file", "log file available", "log file not available", "NA", "pass"]) - - out_path = "{}/comparison.csv".format(out_dir) + elif val2 == "NULL": + diff = val1 + pass_fail = "fail" + elif val2 == "NA": + diff = val2 + pass_fail = "pass" + else: + try: + if float(val2) != float(val1): + val1 = float(val1) + val2 = float(val2) + diff = (abs(val2 - val1) / ((val2 + val1)/2)) * 100 + pass_fail = "pass" if diff < float(diff_percent) else "fail" + else: # special case of 0 + pass_fail = "pass" + diff = 0 + + except Exception as e: + logger.info("error while calculating the diff for val1={} and val2={}." + "Error is: {}".format(val1, val2, str(e))) + diff = str(e) + pass_fail = "fail" + + if over_all_pass: + over_all_pass = pass_fail == "pass" + + result_row = [run_name1, run_name2, sub_dir1, name, val1, val2, diff, pass_fail] + rows.append(result_row) + tc = TestCase(name) + tc.result = None if pass_fail == 'pass' else Failure(dict(zip(header, result_row)) ) + ts.add_testcase(tc) + if pass_fail == 'fail': + failures += 1 + else: + tests += 1 + + else: + rows.append([run_name1, run_name2, sub_dir1, "log_file", "log file available", "log file not available", "NA", "pass"]) + + tc = TestCase("metrics_file_present") + tc.result = Skipped("Metrics log file not available for {}".format(run_name2)) + ts.add_testcase(tc) + skipped += 1 + + ts.hostname = run_name1 + " and " + run_name1 + ts.timestamp = t.start + ts.time = t.diff() + ts.tests = tests + ts.failures = failures + ts.skipped = skipped + ts.errors = errors + ts.update_statistics() + junit_xml.add_testsuite(ts) + + junit_xml.update_statistics() + junit_xml_path = '{}/comparison_results.xml'.format(out_dir) + junit_html_path = '{}/comparison_results.html'.format(out_dir) + junit_xml.write(junit_xml_path) + run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) + + out_path = "{}/comparison_results.csv".format(out_dir) logger.info("Writing comparison report to log file {}".format(out_path)) with open(out_path, 'w') as csvfile: csv_writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) @@ -391,8 +443,11 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam if test_dir is None: test_dir = "{}/tests".format(base_file_path) + if '__' in env_name: + raise Exception("Environment name should not have double underscores in it.") + commit_id = subprocess.check_output('git rev-parse --short HEAD'.split()).decode("utf-8")[:-1] - artifacts_folder_name = "{}_{}_{}".format(env_name, commit_id, int(time.time())) + artifacts_folder_name = "{}__{}__{}".format(env_name, commit_id, int(time.time())) if artifacts_dir is None: artifacts_dir = "{}/{}".format(run_artifacts_path, artifacts_folder_name) else: @@ -410,11 +465,11 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam suite_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) options_str = get_options(suite_artifacts_dir, jmeter_path) env_yaml_path = "{}/{}/environments/{}.yaml".format(test_dir, suite_name, env_name) - env_yaml_path = "" if not os.path.exists(env_yaml_path) else env_yaml_path + test_file = "{}/{}/{}.yaml".format(test_dir, suite_name, suite_name) - with Suite(suite_name, suite_artifacts_dir, junit_xml, t) as s: + with Suite(suite_name, suite_artifacts_dir, junit_xml, t, env_name) as s: s.code, s.err = run_process("{} bzt {} {} {} {}".format(pre_command, options_str, - test_file, env_yaml_path, GLOBAL_CONFIG_PATH)) + test_file, env_yaml_path, GLOBAL_CONFIG_PATH)) metrics_log_file = glob.glob("{}/SAlogs_*".format(suite_artifacts_dir)) if metrics_log_file: @@ -427,8 +482,8 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam shutil.copyfileobj(from_file, to_file) junit_xml.update_statistics() - junit_xml_path = '{}/junit.xml'.format(artifacts_dir) - junit_html_path = '{}/junit.html'.format(artifacts_dir) + junit_xml_path = '{}/performance_results.xml'.format(artifacts_dir) + junit_html_path = '{}/performance_results.html'.format(artifacts_dir) junit_xml.write(junit_xml_path) run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) @@ -444,14 +499,15 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam else: compare_status = 'passed' logger.info("Comparison with monitoring metrics of previous run has {}.".format(compare_status)) - + logger.info("Comparison test suite report - {}/comparison_results.html".format(artifacts_dir)) + if junit_xml.errors or junit_xml.failures or junit_xml.skipped: suite_status = 'failed' exit_code = 3 else: suite_status = 'passed' logger.info("Test suite run has {}.".format(suite_status)) - logger.info("Test suite report - {}/junit.html".format(artifacts_dir)) + logger.info("Test suite report - {}/performance_results.html".format(artifacts_dir)) sys.exit(exit_code) From 2f1bc44c86eb068a1f47a359a7af5120f28b4178 Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Wed, 24 Jun 2020 18:01:42 +0530 Subject: [PATCH 201/329] updates to readme, examples, pull out worker name and change help of environment Signed-off-by: Dhruva Ray --- tests/performance/README.md | 104 +++++++++++------- tests/performance/TESTS.md | 16 +-- tests/performance/agents/metrics/__init__.py | 4 +- tests/performance/run_performance_suite.py | 4 +- .../environments/dev_env1.yaml | 0 .../examples_local_criterion.jmx} | 0 .../examples_local_criterion.yaml} | 2 +- .../examples_local_monitoring.jmx} | 0 .../examples_local_monitoring.yaml} | 2 +- .../environments/dev_env1.yaml | 0 .../examples_remote_criteria.jmx} | 0 .../examples_remote_criteria.yaml} | 4 +- .../examples_remote_monitoring.jmx} | 0 .../examples_remote_monitoring.yaml} | 2 +- .../examples_starter.jmx} | 0 .../examples_starter.yaml} | 2 +- 16 files changed, 83 insertions(+), 57 deletions(-) rename tests/performance/tests/{inference_taurus_local_monitoring_criteria => examples_local_criterion}/environments/dev_env1.yaml (100%) rename tests/performance/tests/{call_jmx/register_and_inference.jmx => examples_local_criterion/examples_local_criterion.jmx} (100%) rename tests/performance/tests/{inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml => examples_local_criterion/examples_local_criterion.yaml} (96%) rename tests/performance/tests/{inference_server_monitoring/register_and_inference.jmx => examples_local_monitoring/examples_local_monitoring.jmx} (100%) rename tests/performance/tests/{inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml => examples_local_monitoring/examples_local_monitoring.yaml} (95%) rename tests/performance/tests/{inference_server_monitoring_criteria => examples_remote_criteria}/environments/dev_env1.yaml (100%) rename tests/performance/tests/{inference_server_monitoring_criteria/register_and_inference.jmx => examples_remote_criteria/examples_remote_criteria.jmx} (100%) rename tests/performance/tests/{inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml => examples_remote_criteria/examples_remote_criteria.yaml} (97%) rename tests/performance/tests/{inference_taurus_local_monitoring/register_and_inference.jmx => examples_remote_monitoring/examples_remote_monitoring.jmx} (100%) rename tests/performance/tests/{inference_server_monitoring/inference_server_monitoring.yaml => examples_remote_monitoring/examples_remote_monitoring.yaml} (96%) rename tests/performance/tests/{inference_taurus_local_monitoring_criteria/register_and_inference.jmx => examples_starter/examples_starter.jmx} (100%) rename tests/performance/tests/{call_jmx/call_jmx.yaml => examples_starter/examples_starter.yaml} (91%) diff --git a/tests/performance/README.md b/tests/performance/README.md index 3c6b75c6f..7529f35da 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -1,21 +1,36 @@ # Performance Regression Suite -This test suite helps in running the load tests and monitoring the process and system wide metrics. It allows to specify the pass/fail criteria for metrics in the test case. +This test suite helps in running load tests and monitoring the process and system wide metrics. +It allows to specify the pass/fail criteria for metrics in the test case. We use Taurus with JMeter as a test automation framework to run the test cases and metrics monitoring. ## How to run the test suite -To run the test suite you need to execute the [run_performance_suite.py](run_performance_suite.py). You will have to provide the artifacts-dir path to store the test case results. -You can specify test cases to be run by providing 'test-dir' with default as '$MMS_HOME/tests/performance/tests' and 'pattern' with default as '*.yaml'. For other options use '--help' option. +To run the test suite you need to execute [run_performance_suite.py](run_performance_suite.py) with the following +parameters + +* `--artifacts-dir` or `-a` is a directory where the test case results will be stored. The default value is +`$MMS_HOME/tests/performance/run_artifacts`. + +* `--test-dir` or `-t` is a directory containing the test cases. The default value is +`$MMS_HOME/tests/performance/tests`. + +* `--pattern` or `-p` picks up certain test cases for execution within the `test-dir`. The default value is to pick up +all test cases. + +* `--env-name` or `-e` specifies the environment name to use while running the test cases. The environment name is the name of +the file (minus the extension) found inside the environments folder in each test case. They encapsulate parameter +values which are specific to the execution environment. This is a mandatory parameter. Script does the following: -1. Optionally but by default starts the metrics monitoring server -2. Collects all the test yamls from test-dir satisfying the pattern -3. Executes test yamls +1. Starts the metrics monitoring server. +2. Collects all the tests from test-dir satisfying the pattern +3. Executes the tests 4. Generates Junit XML and HTML report in artifacts-dir. ### A. Installation Prerequisites -1. Install Taurus. The Taurus needs Python3 but since your tests and MMS instance can run in different virtual environement or machine, -you can configure system such that tests are running on Python3 and MMS instance can run on Python 2 or 3. +1. Install Taurus. Taurus needs Python3 but since your tests and MMS instance can run in different virtual environment +or machine, you can configure the system such that tests are running on Python3 and MMS instance can run on Python 2 +or 3. Refer the [link](https://gettaurus.org/docs/Installation/) for more details on installation. ```bash pip install bzt # Needs python3.6+ @@ -25,49 +40,57 @@ Refer the [link](https://gettaurus.org/docs/Installation/) for more details on i export MMS_HOME= pip install -r $MMS_HOME/tests/performance/requirements.txt ``` -3. Make sure that `git` is installed and the test suites are run from the MMS working directory. This is used to compare performance regresssions across runs and the run artifacts are stored in a folder which have the commit SHA. +3. Make sure that `git` is installed and the test suites are run from the MMS working directory. This is used to +compare performance regressions across runs and the run artifacts are stored in a folder which have the commit SHA. ### B. Running the test suite 1. Make sure parameters set in the [tests/common/global_config.yaml](tests/performance/tests/global_config.yaml) are correct. -2. Run the test suite runner script +2. Run the test suite runner script with the --env-name parameter. This parameter contains the threshold values +for a particular environment and is found in the environments folder of every test case 3. Check the console logs, $artifacts-dir$//junit.html report, comparison.csv and other artifacts. **Steps are provided below** ```bash export MMS_HOME= cd $MMS_HOME/tests/performance - # Note that MMS server started and stopped by the individual test suite. - # check variables + # check variables such as MMS server PORT etc # vi tests/common/global_config.yaml - - python -m run_performance_suite --pattern='*' + python -m run_performance_suite --env-name dev-env1 ``` ### C. Understanding the test suite artifacts and reports -1. The $artifacts-dir$//junit.html contains the summary report of the test run. Note that each test yaml is treated as a -test suite. Different criteria in the yaml are treated as test cases. If criteria is not specified in the yaml, test suite is marked as skipped with 0 test cases. +1. The $artifacts-dir$//junit.html contains the summary report of the test run. Note that each test yaml is +treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not specified in +the yaml, test suite is marked as skipped with 0 test cases. 2. For each test suite a sub-directory is created with artifacts for it. -3. The comparison.csv contains diff for monitoring metrics between an ongoing run and a previous run which was ran for same MMS server. +3. The comparison.csv contains diff for monitoring metrics between an ongoing run and a previous run +which was ran for same MMS server. ## How to add test case to test suite. -To add test case follow steps below. +Follow these three steps to add a new test case to the test suite. + 1. Add scenario 2. Add metrics to monitor 3. Add pass/fail criteria #### 1. Add scenario -You can specify the test scenarios, in the scenario section of the yaml. -To get you started quickly, we have provided a sample JMeter script and a Taurus yaml file [here](tests/call_jmx/register_and_inference.jmx) and [here](tests/call_jmx/call_jmx.yaml) . +Create a folder for your test under `test_dir` location. A test generally comprises on a jmeter file - containing the +load scenario and a yaml file which contains test scenarios specifying the conditions for failure or success. The +filenames should be identical to the folder name with their respective extensions. + +To get you started quickly, we have provided an example [jmeter script](tests/examples_starter/examples_starter.jmx) +and a [scenario](tests/examples_starter/examples_starter.yaml). -Here is how the sample call_jmx.yaml looks like. Note variables used by jmx script are specified in [tests/global_config.yaml](tests/performance/tests/global_config.yaml) file. +Note variables used by examples_starter.jmx script are specified in +[tests/global_config.yaml](tests/performance/tests/global_config.yaml) file. - ```yaml + ```tests/examples_starter/examples_starter.yaml execution: - concurrency: 1 ramp-up: 1s @@ -76,34 +99,37 @@ Here is how the sample call_jmx.yaml looks like. Note variables used by jmx scri scenarios: Inference: - script: register_and_inference.jmx + script: examples_starter.jmx ``` -To run this individual test using Taurus(bzt) run commands below: +To run this individual test, run the following command ```bash export MMS_HOME= cd $MMS_HOME/tests/performance - python -m run_performance_suite -p call_jmx + python -m run_performance_suite -p examples_starter -e local ``` **Note**: -Taurus provides support for different executors such as JMeter. You can use test script written in those frameworks as it is. -Details about executor types are provided [here](https://gettaurus.org/docs/ExecutionSettings/). +Taurus provides support for different executors such as JMeter. You can use test script written in those +frameworks as it is. Details about executor types are provided [here](https://gettaurus.org/docs/ExecutionSettings/). Details about how to run an existing JMeter script are provided [here](https://gettaurus.org/docs/JMeter/). #### 2. Add metrics to monitor You can specify different metrics in services/monitoring section of the yaml. Metrics can be monitored in two ways: + 1. Standalone monitoring server If your MMS server is hosted on different machine, you will be using this method. Before running the test case - you have to start a [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will be communicating with Taurus test client over sockets. - The address and port(default=9009) of the monitoring script should be specified in test case yaml. + you have to start a [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will be communicating + with Taurus test client over sockets. The address and port(default=9009) of the monitoring script should be + specified in test case yaml. - **Note**: While running Test suite runner script, no need to manually start the monitoring server. The scripts optionally but by default starts and stops it in setup and teardown. + **Note**: While running Test suite runner script, no need to manually start the monitoring server. + The scripts optionally but by default starts and stops it in setup and teardown. To start monitoring server run commands below: ```bash @@ -112,7 +138,7 @@ Metrics can be monitored in two ways: python $MMS_HOME/tests/performance/metrics_monitoring_server.py --start ``` - Sample yaml with monitoring section config. Complete yaml can be found [here](tests/inference_server_monitoring/inference_server_monitoring.yaml) + Sample monitoring section config. ```yaml services: @@ -128,17 +154,18 @@ Metrics can be monitored in two ways: - sum_num_handles - server_workers # no of mms workers ``` + Complete yaml can be found [here](tests/examples_remote_monitoring/examples_remote_monitoring.yaml) Use the command below to run the test yaml. ```bash export MMS_HOME= cd $MMS_HOME/tests/performance - python -m run_performance_suite -p inference_server_monitoring + python -m run_performance_suite -p examples_remote_monitoring -e 2xlarge ``` -2. Taurus local monitoring plugin +2. Local monitoring plugin If your test client is running on the server itself, you may want to use this method. We have provided a custom Taurus plugin as [metrics_monitoring_taurus.py](metrics_monitoring_taurus.py). @@ -153,7 +180,7 @@ Metrics can be monitored in two ways: export PYTHONPATH=$MMS_HOME/tests/performance:$PYTHONPATH ``` - Relevant test yaml sections. Test yaml can be found [here](tests/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml) + Sample monitoring section config. ```yaml modules: @@ -172,13 +199,14 @@ Metrics can be monitored in two ways: - sum_memory_percent ``` - + Complete yaml can be found [here](tests/examples_local_monitoring/examples_local_monitoring.yaml) + Use command below to run the test yaml ```bash export MMS_HOME= cd $MMS_HOME/tests/performance - python -m run_performance_suite -p inference_taurus_local_monitoring + python -m run_performance_suite -p examples_local_monitoring -e 2xlarge ``` #### 3.1 Add pass/fail criteria @@ -209,9 +237,7 @@ Below are different options used by run_performance_suite script for coparison. 1. **artifacts-dir**: This is an optional parameter. The default is './run_artifacts' directory. A sub directory with '{env_name}_{git_commit_id}_{timestamp}' gets created in the artifacts dir. -2. **env-name**: -This is an optional parameter. The default is current hostname. This should be unique it is used while doing comparison between runs. -Comparison should happen between the runs of same environment. + 3. **compare-local/no-compare-local**: This is an optional parameter. The default is compare-local. If `compare-local` is set, previous run results from the local `artifacts-dir` folder will be used used. `experimental` If no-compare-local is set, previous run results from the public S3 bucket will be used. `no-compare-local is currently experimental` diff --git a/tests/performance/TESTS.md b/tests/performance/TESTS.md index 285cf9da3..bb473ff20 100644 --- a/tests/performance/TESTS.md +++ b/tests/performance/TESTS.md @@ -13,13 +13,13 @@ |API|CODE|YAML| |---|----|---| -|Register Model|A,B,F,J|[WIP] [register_unregister.yaml](tests/register_unregister.yaml)| -|Inference|A,B,C|[WIP] [inference_single_worker.yaml](tests/inference_single_worker.yaml), [inference_multiple_worker.yaml](tests/inference_multiple_worker.yaml)| +|Register Model|A,B,F,J|[register_unregister.yaml](tests/register_unregister.yaml)| +|Inference|A,B,C|[inference_single_worker.yaml](tests/inference_single_worker.yaml), [inference_multiple_worker.yaml](tests/inference_multiple_worker.yaml)| |Batch Inference|A,B,C|[batch_inference.yaml](tests/batch_inference.yaml)| |Custom Model Handlers|C|[batch_and_single_inference.yaml](tests/batch_and_single_inference.yaml)| -|Scale Workers - UP/DOWN|D,G,I,F,J|[WIP] [scale_up_workers.yaml](tests/scale_up_workers.yaml), [scale_down_workers.yaml](tests/scale_down_workers.yaml)| -|Unregister Models|D,G,I,J|[WIP] [register_unregister.yaml](tests/register_unregister.yaml)| -|Health Check|A,B,E|[WIP] [health_check.yaml](tests/health_check.yaml)| -|API Description|A,B,E|[WIP] [api_description.yaml](tests/api_description.yaml)| -|Model Describe|A,B,E|[WIP] [model_description.yaml](tests/model_description.yaml)| -|List Models|A,B,E|[WIP] [list_models.yaml](tests/list_models.yaml)| +|Scale Workers - UP/DOWN|D,G,I,F,J|[scale_up_workers.yaml](tests/scale_up_workers.yaml), [scale_down_workers.yaml](tests/scale_down_workers.yaml)| +|Unregister Models|D,G,I,J|[register_unregister.yaml](tests/register_unregister.yaml)| +|Health Check|A,B,E|[health_check.yaml](tests/health_check.yaml)| +|API Description|A,B,E|[api_description.yaml](tests/api_description.yaml)| +|Model Describe|A,B,E|[model_description.yaml](tests/model_description.yaml)| +|List Models|A,B,E|[list_models.yaml](tests/list_models.yaml)| diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index 893db8394..119a7ca7f 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -68,6 +68,7 @@ class ProcessType(Enum): } AVAILABLE_METRICS = list(system_metrics) + list(misc_metrics) +WORKER_NAME = 'model_service_worker.py' for metric in list(process_metrics): for ptype in list(ProcessType): @@ -115,7 +116,7 @@ def update_metric(metric_name, proc_type, stats): processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) for child in children: try: - if psutil.pid_exists(child.pid) and 'model_service_worker.py' in child.cmdline()[1]: + if psutil.pid_exists(child.pid) and WORKER_NAME in child.cmdline()[1]: processes_stats.append({'type': ProcessType.WORKER, 'stats' : child.as_dict()}) else: reclaimed_pids.append(child) @@ -153,5 +154,4 @@ def update_metric(metric_name, proc_type, stats): result['system_read_bytes'] = system_disk_io_counters.read_bytes result['system_write_bytes'] = system_disk_io_counters.write_bytes - return result diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 76380de52..a38786232 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -384,7 +384,9 @@ def get_options(artifacts_dir, jmeter_path=None): @click.option('-p', '--pattern', help='Test case folder name pattern', default="*") @click.option('-j', '--jmeter-path', help='JMeter executable path.') @click.option('--monit/--no-monit', help='Start Monitoring server', default=True) -@click.option('--env-name', help='Unique machine id on which MMS server is running', required=True) +@click.option('-e', '--env-name', help='environment name which defines threshold values used by the test cases. ' + 'This is the name of a file found in the tests environment folder without ' + 'the extension', required=True) @click.option('--compare-local/--no-compare-local', help='Compare with previous run with files stored' ' in artifacts directory', default=True) def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_name, compare_local): diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/dev_env1.yaml b/tests/performance/tests/examples_local_criterion/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/dev_env1.yaml rename to tests/performance/tests/examples_local_criterion/environments/dev_env1.yaml diff --git a/tests/performance/tests/call_jmx/register_and_inference.jmx b/tests/performance/tests/examples_local_criterion/examples_local_criterion.jmx similarity index 100% rename from tests/performance/tests/call_jmx/register_and_inference.jmx rename to tests/performance/tests/examples_local_criterion/examples_local_criterion.jmx diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/examples_local_criterion/examples_local_criterion.yaml similarity index 96% rename from tests/performance/tests/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml rename to tests/performance/tests/examples_local_criterion/examples_local_criterion.yaml index 3597e75c3..91738d350 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml +++ b/tests/performance/tests/examples_local_criterion/examples_local_criterion.yaml @@ -7,7 +7,7 @@ execution: scenarios: Inference: - script: register_and_inference.jmx + script: examples_local_criterion.jmx modules: server_local_monitoring: diff --git a/tests/performance/tests/inference_server_monitoring/register_and_inference.jmx b/tests/performance/tests/examples_local_monitoring/examples_local_monitoring.jmx similarity index 100% rename from tests/performance/tests/inference_server_monitoring/register_and_inference.jmx rename to tests/performance/tests/examples_local_monitoring/examples_local_monitoring.jmx diff --git a/tests/performance/tests/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml b/tests/performance/tests/examples_local_monitoring/examples_local_monitoring.yaml similarity index 95% rename from tests/performance/tests/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml rename to tests/performance/tests/examples_local_monitoring/examples_local_monitoring.yaml index 29c558f73..26fa9e81a 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring/inference_taurus_local_monitoring.yaml +++ b/tests/performance/tests/examples_local_monitoring/examples_local_monitoring.yaml @@ -6,7 +6,7 @@ execution: scenario: Inference scenarios: Inference: - script: register_and_inference.jmx + script: examples_local_monitoring.jmx modules: server_local_monitoring: diff --git a/tests/performance/tests/inference_server_monitoring_criteria/environments/dev_env1.yaml b/tests/performance/tests/examples_remote_criteria/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/inference_server_monitoring_criteria/environments/dev_env1.yaml rename to tests/performance/tests/examples_remote_criteria/environments/dev_env1.yaml diff --git a/tests/performance/tests/inference_server_monitoring_criteria/register_and_inference.jmx b/tests/performance/tests/examples_remote_criteria/examples_remote_criteria.jmx similarity index 100% rename from tests/performance/tests/inference_server_monitoring_criteria/register_and_inference.jmx rename to tests/performance/tests/examples_remote_criteria/examples_remote_criteria.jmx diff --git a/tests/performance/tests/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml b/tests/performance/tests/examples_remote_criteria/examples_remote_criteria.yaml similarity index 97% rename from tests/performance/tests/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml rename to tests/performance/tests/examples_remote_criteria/examples_remote_criteria.yaml index c71941570..541df9a0b 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml +++ b/tests/performance/tests/examples_remote_criteria/examples_remote_criteria.yaml @@ -7,9 +7,7 @@ execution: scenarios: Inference: - script: register_and_inference.jmx - - + script: examples_remote_criteria.jmx services: - module: shellexec diff --git a/tests/performance/tests/inference_taurus_local_monitoring/register_and_inference.jmx b/tests/performance/tests/examples_remote_monitoring/examples_remote_monitoring.jmx similarity index 100% rename from tests/performance/tests/inference_taurus_local_monitoring/register_and_inference.jmx rename to tests/performance/tests/examples_remote_monitoring/examples_remote_monitoring.jmx diff --git a/tests/performance/tests/inference_server_monitoring/inference_server_monitoring.yaml b/tests/performance/tests/examples_remote_monitoring/examples_remote_monitoring.yaml similarity index 96% rename from tests/performance/tests/inference_server_monitoring/inference_server_monitoring.yaml rename to tests/performance/tests/examples_remote_monitoring/examples_remote_monitoring.yaml index 1146848cd..235c3b803 100644 --- a/tests/performance/tests/inference_server_monitoring/inference_server_monitoring.yaml +++ b/tests/performance/tests/examples_remote_monitoring/examples_remote_monitoring.yaml @@ -7,7 +7,7 @@ execution: scenarios: Inference: - script: register_and_inference.jmx + script: examples_remote_monitoring.jmx diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria/register_and_inference.jmx b/tests/performance/tests/examples_starter/examples_starter.jmx similarity index 100% rename from tests/performance/tests/inference_taurus_local_monitoring_criteria/register_and_inference.jmx rename to tests/performance/tests/examples_starter/examples_starter.jmx diff --git a/tests/performance/tests/call_jmx/call_jmx.yaml b/tests/performance/tests/examples_starter/examples_starter.yaml similarity index 91% rename from tests/performance/tests/call_jmx/call_jmx.yaml rename to tests/performance/tests/examples_starter/examples_starter.yaml index 2cd4f348d..ac6aaa50b 100644 --- a/tests/performance/tests/call_jmx/call_jmx.yaml +++ b/tests/performance/tests/examples_starter/examples_starter.yaml @@ -6,7 +6,7 @@ execution: scenario: Inference scenarios: Inference: - script: register_and_inference.jmx + script: examples_starter.jmx services: - module: shellexec From 8f7e69641092297530f8e8acc008fac36dc1aa95 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 24 Jun 2020 18:03:54 +0530 Subject: [PATCH 202/329] test suite movement --- tests/performance/run_performance_suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index ffdb4319f..faa37816c 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -433,7 +433,7 @@ def get_options(artifacts_dir, jmeter_path=None): @click.command() @click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True)) @click.option('-t', '--test-dir', help='Directory containing tests.', type=click.Path(exists=True), default=None) -@click.option('-p', '--pattern', help='Test case folder name pattern', default="*") +@click.option('-p', '--pattern', help='Test case folder name pattern', default="[!example]*") @click.option('-j', '--jmeter-path', help='JMeter executable path.') @click.option('--monit/--no-monit', help='Start Monitoring server', default=True) @click.option('--env-name', help='Unique machine id on which MMS server is running', required=True) From 6ab270c7fbfbc2ed87b007dc24806742c2df0107 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 24 Jun 2020 18:05:10 +0530 Subject: [PATCH 203/329] test suite movement --- .../environments/dev_env1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/dev_env1.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/dev_env1.yaml index 149a251a1..05e0c3197 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/dev_env1.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/dev_env1.yaml @@ -5,5 +5,5 @@ settings: fail : 100% p90 : 290ms avg_rt : 1s - sum_workers_memory_rss : 132000000 + sum_workers_memory_rss : 140000000 sum_workers_memory_rss_diff_percent : 30 From 5261b8b38eabe3df331fea7b1c4f75ae71a5973e Mon Sep 17 00:00:00 2001 From: maheshambule Date: Wed, 24 Jun 2020 18:13:04 +0530 Subject: [PATCH 204/329] handle env path --- tests/performance/run_performance_suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index caf132e94..502d640c7 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -467,7 +467,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam suite_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) options_str = get_options(suite_artifacts_dir, jmeter_path) env_yaml_path = "{}/{}/environments/{}.yaml".format(test_dir, suite_name, env_name) - + env_yaml_path = "" if not os.path.exists(env_yaml_path) else env_yaml_path test_file = "{}/{}/{}.yaml".format(test_dir, suite_name, suite_name) with Suite(suite_name, suite_artifacts_dir, junit_xml, t, env_name) as s: s.code, s.err = run_process("{} bzt {} {} {} {}".format(pre_command, options_str, From 4512f9dd3acaef7992c5b66a950b1bdee343da0d Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 18:18:18 +0530 Subject: [PATCH 205/329] renamed all dev_env1 to xlarge --- .../tests/api_description/api_description.yaml | 16 ++++++++-------- .../api_description/environments/dev_env1.yaml | 8 -------- .../api_description/environments/xlarge.yaml | 10 ++++++++++ .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 .../environments/{dev_env1.yaml => xlarge.yaml} | 0 17 files changed, 18 insertions(+), 16 deletions(-) delete mode 100644 tests/performance/tests/api_description/environments/dev_env1.yaml create mode 100644 tests/performance/tests/api_description/environments/xlarge.yaml rename tests/performance/tests/batch_and_single_inference/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/batch_inference/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/health_check/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/inference_multiple_models/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/inference_multiple_worker/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/inference_server_monitoring_criteria/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/inference_single_worker/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/list_models/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/model_description/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/register_unregister/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/register_unregister_multiple/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/scale_down_workers/environments/{dev_env1.yaml => xlarge.yaml} (100%) rename tests/performance/tests/scale_up_workers/environments/{dev_env1.yaml => xlarge.yaml} (100%) diff --git a/tests/performance/tests/api_description/api_description.yaml b/tests/performance/tests/api_description/api_description.yaml index a67ae8b05..50369442b 100644 --- a/tests/performance/tests/api_description/api_description.yaml +++ b/tests/performance/tests/api_description/api_description.yaml @@ -33,36 +33,36 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of ManagementAPIDescription<100%, stop as failed - - success of InferenceAPIDescription<100%, stop as failed - - avg-rt of ManagementAPIDescription>${AVG_RT_ManagementAPIDescription}, stop as failed - - avg-rt of InferenceAPIDescription>${AVG_RT_InferenceAPIDescription}, stop as failed + - success of ManagementAPIDescription<${SUCCESS_MGMT_API}, stop as failed + - success of InferenceAPIDescription<${SUCCESS_INFR_API}, stop as failed + - avg-rt of ManagementAPIDescription>${MGMT_API_RT}, stop as failed + - avg-rt of InferenceAPIDescription>${INFR_API_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: ${sum_all_file_descriptors} + threshold: ${TOTAL_FDS} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: ${sum_all_memory_rss} +# threshold: ${TOTAL_MEM} # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/api_description/environments/dev_env1.yaml b/tests/performance/tests/api_description/environments/dev_env1.yaml deleted file mode 100644 index b81945759..000000000 --- a/tests/performance/tests/api_description/environments/dev_env1.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -settings: - env: - AVG_RT_ManagementAPIDescription : 10ms - AVG_RT_InferenceAPIDescription : 10ms - total_processes : 1 - sum_all_file_descriptors : 73 - sum_all_memory_rss : 100000000 #100MB diff --git a/tests/performance/tests/api_description/environments/xlarge.yaml b/tests/performance/tests/api_description/environments/xlarge.yaml new file mode 100644 index 000000000..edb288f2b --- /dev/null +++ b/tests/performance/tests/api_description/environments/xlarge.yaml @@ -0,0 +1,10 @@ +--- +settings: + env: + SUCCESS_MGMT_API: 100% + SUCCESS_INFR_API: 100% + MGMT_API_RT : 10ms + INFR_API_RT : 10ms + TOTAL_PROCS : 1 + TOTAL_FDS : 73 + 2xlarge: 100000000 #100MB diff --git a/tests/performance/tests/batch_and_single_inference/environments/dev_env1.yaml b/tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/batch_and_single_inference/environments/dev_env1.yaml rename to tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml diff --git a/tests/performance/tests/batch_inference/environments/dev_env1.yaml b/tests/performance/tests/batch_inference/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/batch_inference/environments/dev_env1.yaml rename to tests/performance/tests/batch_inference/environments/xlarge.yaml diff --git a/tests/performance/tests/health_check/environments/dev_env1.yaml b/tests/performance/tests/health_check/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/health_check/environments/dev_env1.yaml rename to tests/performance/tests/health_check/environments/xlarge.yaml diff --git a/tests/performance/tests/inference_multiple_models/environments/dev_env1.yaml b/tests/performance/tests/inference_multiple_models/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_multiple_models/environments/dev_env1.yaml rename to tests/performance/tests/inference_multiple_models/environments/xlarge.yaml diff --git a/tests/performance/tests/inference_multiple_worker/environments/dev_env1.yaml b/tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_multiple_worker/environments/dev_env1.yaml rename to tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml diff --git a/tests/performance/tests/inference_server_monitoring_criteria/environments/dev_env1.yaml b/tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_server_monitoring_criteria/environments/dev_env1.yaml rename to tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml diff --git a/tests/performance/tests/inference_single_worker/environments/dev_env1.yaml b/tests/performance/tests/inference_single_worker/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_single_worker/environments/dev_env1.yaml rename to tests/performance/tests/inference_single_worker/environments/xlarge.yaml diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/dev_env1.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/dev_env1.yaml rename to tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/xlarge.yaml diff --git a/tests/performance/tests/list_models/environments/dev_env1.yaml b/tests/performance/tests/list_models/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/list_models/environments/dev_env1.yaml rename to tests/performance/tests/list_models/environments/xlarge.yaml diff --git a/tests/performance/tests/model_description/environments/dev_env1.yaml b/tests/performance/tests/model_description/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/model_description/environments/dev_env1.yaml rename to tests/performance/tests/model_description/environments/xlarge.yaml diff --git a/tests/performance/tests/register_unregister/environments/dev_env1.yaml b/tests/performance/tests/register_unregister/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/register_unregister/environments/dev_env1.yaml rename to tests/performance/tests/register_unregister/environments/xlarge.yaml diff --git a/tests/performance/tests/register_unregister_multiple/environments/dev_env1.yaml b/tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/register_unregister_multiple/environments/dev_env1.yaml rename to tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml diff --git a/tests/performance/tests/scale_down_workers/environments/dev_env1.yaml b/tests/performance/tests/scale_down_workers/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/scale_down_workers/environments/dev_env1.yaml rename to tests/performance/tests/scale_down_workers/environments/xlarge.yaml diff --git a/tests/performance/tests/scale_up_workers/environments/dev_env1.yaml b/tests/performance/tests/scale_up_workers/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/scale_up_workers/environments/dev_env1.yaml rename to tests/performance/tests/scale_up_workers/environments/xlarge.yaml From e89083f8f968a6cb011fd49750b70bbdccb6abf3 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 18:25:42 +0530 Subject: [PATCH 206/329] api desc renamed vairables --- tests/performance/tests/api_description/api_description.yaml | 4 ++-- .../tests/api_description/environments/xlarge.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/performance/tests/api_description/api_description.yaml b/tests/performance/tests/api_description/api_description.yaml index 50369442b..3c4365a32 100644 --- a/tests/performance/tests/api_description/api_description.yaml +++ b/tests/performance/tests/api_description/api_description.yaml @@ -33,8 +33,8 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of ManagementAPIDescription<${SUCCESS_MGMT_API}, stop as failed - - success of InferenceAPIDescription<${SUCCESS_INFR_API}, stop as failed + - success of ManagementAPIDescription<${MGMT_API_SUCC}, stop as failed + - success of InferenceAPIDescription<${INFR_API_SUCC}, stop as failed - avg-rt of ManagementAPIDescription>${MGMT_API_RT}, stop as failed - avg-rt of InferenceAPIDescription>${INFR_API_RT}, stop as failed # Custom Criteria diff --git a/tests/performance/tests/api_description/environments/xlarge.yaml b/tests/performance/tests/api_description/environments/xlarge.yaml index edb288f2b..c3de8e7bc 100644 --- a/tests/performance/tests/api_description/environments/xlarge.yaml +++ b/tests/performance/tests/api_description/environments/xlarge.yaml @@ -1,8 +1,8 @@ --- settings: env: - SUCCESS_MGMT_API: 100% - SUCCESS_INFR_API: 100% + MGMT_API_SUCC: 100% + INFR_API_SUCC: 100% MGMT_API_RT : 10ms INFR_API_RT : 10ms TOTAL_PROCS : 1 From 407054151df9d0e41169a231500c96509309823b Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 18:32:29 +0530 Subject: [PATCH 207/329] corrected variable name --- .../performance/tests/api_description/environments/xlarge.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/tests/api_description/environments/xlarge.yaml b/tests/performance/tests/api_description/environments/xlarge.yaml index c3de8e7bc..93ca5f819 100644 --- a/tests/performance/tests/api_description/environments/xlarge.yaml +++ b/tests/performance/tests/api_description/environments/xlarge.yaml @@ -7,4 +7,4 @@ settings: INFR_API_RT : 10ms TOTAL_PROCS : 1 TOTAL_FDS : 73 - 2xlarge: 100000000 #100MB + TOTAL_MEM: 100000000 #100MB From 37525e5862c2674b198322838a066ac7e830437b Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Wed, 24 Jun 2020 18:40:30 +0530 Subject: [PATCH 208/329] change example name to be consistent... update readme for new report names Signed-off-by: Dhruva Ray --- tests/performance/README.md | 17 +++++++++-------- .../environments/dev_env1.yaml | 0 .../examples_local_criteria.jmx} | 0 .../examples_local_criteria.yaml} | 0 4 files changed, 9 insertions(+), 8 deletions(-) rename tests/performance/tests/{examples_local_criterion => examples_local_criteria}/environments/dev_env1.yaml (100%) rename tests/performance/tests/{examples_local_criterion/examples_local_criterion.jmx => examples_local_criteria/examples_local_criteria.jmx} (100%) rename tests/performance/tests/{examples_local_criterion/examples_local_criterion.yaml => examples_local_criteria/examples_local_criteria.yaml} (100%) diff --git a/tests/performance/README.md b/tests/performance/README.md index 7529f35da..5e4740c3a 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -25,7 +25,7 @@ Script does the following: 1. Starts the metrics monitoring server. 2. Collects all the tests from test-dir satisfying the pattern 3. Executes the tests -4. Generates Junit XML and HTML report in artifacts-dir. +4. Generates performance XML and HTML report in artifacts-dir. ### A. Installation Prerequisites 1. Install Taurus. Taurus needs Python3 but since your tests and MMS instance can run in different virtual environment @@ -47,7 +47,8 @@ compare performance regressions across runs and the run artifacts are stored in 1. Make sure parameters set in the [tests/common/global_config.yaml](tests/performance/tests/global_config.yaml) are correct. 2. Run the test suite runner script with the --env-name parameter. This parameter contains the threshold values for a particular environment and is found in the environments folder of every test case -3. Check the console logs, $artifacts-dir$//junit.html report, comparison.csv and other artifacts. +3. Check the console logs, $artifacts-dir$//performance_results.html report, comparison.csv, comparison.html +and other artifacts. **Steps are provided below** ```bash @@ -62,12 +63,12 @@ for a particular environment and is found in the environments folder of every te ``` ### C. Understanding the test suite artifacts and reports -1. The $artifacts-dir$//junit.html contains the summary report of the test run. Note that each test yaml is -treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not specified in -the yaml, test suite is marked as skipped with 0 test cases. +1. The $artifacts-dir$//performance_results.html contains the summary report of the test run. Note that each +test yaml is treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not + specified in the yaml, test suite is marked as skipped with 0 test cases. 2. For each test suite a sub-directory is created with artifacts for it. -3. The comparison.csv contains diff for monitoring metrics between an ongoing run and a previous run -which was ran for same MMS server. +3. The comparison_results.csv contains diff for monitoring metrics between an ongoing run and a previous run +which was ran for same MMS server. Comparison.html shows the comparision results. ## How to add test case to test suite. @@ -108,7 +109,7 @@ To run this individual test, run the following command ```bash export MMS_HOME= cd $MMS_HOME/tests/performance - python -m run_performance_suite -p examples_starter -e local + python -m run_performance_suite -p examples_starter -e 2xlarge ``` **Note**: diff --git a/tests/performance/tests/examples_local_criterion/environments/dev_env1.yaml b/tests/performance/tests/examples_local_criteria/environments/dev_env1.yaml similarity index 100% rename from tests/performance/tests/examples_local_criterion/environments/dev_env1.yaml rename to tests/performance/tests/examples_local_criteria/environments/dev_env1.yaml diff --git a/tests/performance/tests/examples_local_criterion/examples_local_criterion.jmx b/tests/performance/tests/examples_local_criteria/examples_local_criteria.jmx similarity index 100% rename from tests/performance/tests/examples_local_criterion/examples_local_criterion.jmx rename to tests/performance/tests/examples_local_criteria/examples_local_criteria.jmx diff --git a/tests/performance/tests/examples_local_criterion/examples_local_criterion.yaml b/tests/performance/tests/examples_local_criteria/examples_local_criteria.yaml similarity index 100% rename from tests/performance/tests/examples_local_criterion/examples_local_criterion.yaml rename to tests/performance/tests/examples_local_criteria/examples_local_criteria.yaml From 5bf63968b3d1f05c1ecfe27435cab564ca7ea654 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 18:45:27 +0530 Subject: [PATCH 209/329] updated variables in batch single infr scenario --- .../batch_and_single_inference.yaml | 12 ++++++------ .../environments/xlarge.yaml | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml b/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml index b4d92bca9..8aaadd663 100644 --- a/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml +++ b/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml @@ -45,7 +45,7 @@ reporting: - subject: avg-rt # required label: 'Inference1' # optional, default is '' condition: '>' # required - threshold: ${Inference1} # required + threshold: ${INFR1_RT} # required logic: for # optional, logic to aggregate values within timeframe. # Default 'for' means take latest, # 'within' and 'over' means take sum/avg of all values within interval @@ -55,7 +55,7 @@ reporting: - subject: avg-rt # required label: 'Inference2' # optional, default is '' condition: '>' # required - threshold: ${Inference2} # required + threshold: ${INFR2_RT} # required logic: for # optional, logic to aggregate values within timeframe. # Default 'for' means take latest, # 'within' and 'over' means take sum/avg of all values within interval @@ -65,7 +65,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: ${sum_workers_memory_rss} + threshold: ${TOTAL_WRKR_MEM} timeframe: 1s stop : true fail : true @@ -73,7 +73,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/orphans condition: '>' - threshold: ${orphans} + threshold: ${TOTAL_ORPHANS} timeframe: 1s stop : true fail : true @@ -81,7 +81,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '>' - threshold: ${total_workers} + threshold: ${TOTAL_WORKERS} timeframe: 1s stop: true fail: true @@ -89,7 +89,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '>' - threshold: ${sum_workers_file_descriptors} + threshold: ${TOTAL_WORKERS_FDS} timeframe: 1s stop: true fail: true diff --git a/tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml b/tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml index 38930777b..97307b690 100644 --- a/tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml +++ b/tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml @@ -1,9 +1,9 @@ --- settings: env: - Inference1 : 6s - Inference2 : 0.08s - sum_workers_file_descriptors : 78 - total_workers : 9 - orphans : 0 - sum_workers_memory_rss : 4000000000 + INFR1_RT : 6s + INFR2_RT : 0.08s + TOTAL_WORKERS_MEM : 4000000000 #4GB + TOTAL_WORKERS : 9 + TOTAL_ORPHANS : 0 + TOTAL_WORKERS_FDS : 78 From eb6e087d21dd06e0f0ab6cc35dcbf8bfa5a93e4f Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Wed, 24 Jun 2020 18:56:51 +0530 Subject: [PATCH 210/329] move section to improve clarity Signed-off-by: Dhruva Ray --- tests/performance/README.md | 50 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index f7e4775e7..884130569 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -4,29 +4,6 @@ This test suite helps in running load tests and monitoring the process and syste It allows to specify the pass/fail criteria for metrics in the test case. We use Taurus with JMeter as a test automation framework to run the test cases and metrics monitoring. -## How to run the test suite -To run the test suite you need to execute [run_performance_suite.py](run_performance_suite.py) with the following -parameters - -* `--artifacts-dir` or `-a` is a directory where the test case results will be stored. The default value is -`$MMS_HOME/tests/performance/run_artifacts`. - -* `--test-dir` or `-t` is a directory containing the test cases. The default value is -`$MMS_HOME/tests/performance/tests`. - -* `--pattern` or `-p` picks up certain test cases for execution within the `test-dir`. The default value is to pick up -all test cases. - -* `--env-name` or `-e` specifies the environment name to use while running the test cases. The environment name is the name of -the file (minus the extension) found inside the environments folder in each test case. They encapsulate parameter -values which are specific to the execution environment. This is a mandatory parameter. - -Script does the following: -1. Starts the metrics monitoring server. -2. Collects all the tests from test-dir satisfying the pattern -3. Executes the tests -4. Generates performance XML and HTML report in artifacts-dir. - ### A. Installation Prerequisites 1. Install Taurus. Taurus needs Python3 but since your tests and MMS instance can run in different virtual environment or machine, you can configure the system such that tests are running on Python3 and MMS instance can run on Python 2 @@ -45,8 +22,28 @@ compare performance regressions across runs and the run artifacts are stored in ### B. Running the test suite 1. Make sure parameters set in the [tests/common/global_config.yaml](tests/performance/tests/global_config.yaml) are correct. -2. Run the test suite runner script with the --env-name parameter. This parameter contains the threshold values -for a particular environment and is found in the environments folder of every test case +2. To run the test suite you need to execute [run_performance_suite.py](run_performance_suite.py) with the following +parameters + + * `--artifacts-dir` or `-a` is a directory where the test case results will be stored. The default value is +`$MMS_HOME/tests/performance/run_artifacts`. + + * `--test-dir` or `-t` is a directory containing the test cases. The default value is +`$MMS_HOME/tests/performance/tests`. + + * `--pattern` or `-p` picks up certain test cases for execution within the `test-dir`. The default value is to pick up +all test cases. + + * `--env-name` or `-e` specifies the environment name to use while running the test cases. The environment name is the name of +the file (minus the extension) found inside the environments folder in each test case. They encapsulate parameter +values which are specific to the execution environment. This is a mandatory parameter. + + Script does the following: + 1. Starts the metrics monitoring server. + 2. Collects all the tests from test-dir satisfying the pattern + 3. Executes the tests + 4. Generates performance XML and HTML report in artifacts-dir. + 3. Check the console logs, $artifacts-dir$//performance_results.html report, comparison.csv, comparison.html and other artifacts. @@ -59,9 +56,10 @@ and other artifacts. # check variables such as MMS server PORT etc # vi tests/common/global_config.yaml - python -m run_performance_suite --env-name dev-env1 + python -m run_performance_suite --env-name xlarge ``` + ### C. Understanding the test suite artifacts and reports 1. The $artifacts-dir$//performance_results.html contains the summary report of the test run. Note that each test yaml is treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not From 3a089a3450cce415574ed4bdd00699da4414c4ac Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 18:57:08 +0530 Subject: [PATCH 211/329] updating and correcting var name and values batch_inference --- .../tests/batch_inference/batch_inference.yaml | 10 +++++----- .../tests/batch_inference/environments/xlarge.yaml | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/performance/tests/batch_inference/batch_inference.yaml b/tests/performance/tests/batch_inference/batch_inference.yaml index 043d72129..80a9b7509 100644 --- a/tests/performance/tests/batch_inference/batch_inference.yaml +++ b/tests/performance/tests/batch_inference/batch_inference.yaml @@ -43,7 +43,7 @@ reporting: - subject: avg-rt # required label: 'Inference' # optional, default is '' condition: '>' # required - threshold: ${Inference} # required + threshold: ${INFR_RT} # required logic: for # optional, logic to aggregate values within timeframe. # Default 'for' means take latest, # 'within' and 'over' means take sum/avg of all values within interval @@ -53,7 +53,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: ${sum_workers_memory_rss} + threshold: ${TOTAL_WORKERS_MEM} timeframe: 1s stop : true fail : true @@ -61,7 +61,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/orphans condition: '>' - threshold: ${orphans} + threshold: ${TOTAL_ORPHANS} timeframe: 1s stop : true fail : true @@ -69,7 +69,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '>' - threshold: ${total_workers} + threshold: ${TOTAL_WORKERS} timeframe: 1s stop: true fail: true @@ -77,7 +77,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '>' - threshold: ${sum_workers_file_descriptors} + threshold: ${TOTAL_WORKERS_FDS} timeframe: 1s stop: true fail: true diff --git a/tests/performance/tests/batch_inference/environments/xlarge.yaml b/tests/performance/tests/batch_inference/environments/xlarge.yaml index c9de1ab34..23a443aaf 100644 --- a/tests/performance/tests/batch_inference/environments/xlarge.yaml +++ b/tests/performance/tests/batch_inference/environments/xlarge.yaml @@ -1,8 +1,8 @@ --- settings: env: - Inference : 1.5s - sum_workers_file_descriptors : 38 - total_workers : 4 - orphans : 0 - sum_workers_memory_rss : 3000000000 \ No newline at end of file + INFR_RT : 1.5s + TOTAL_WORKERS_MEM : 3000000000 #3GB + TOTAL_WORKERS : 4 + TOTAL_ORPHANS : 0 + TOTAL_WORKERS_FDS : 38 \ No newline at end of file From 4f0631d4368184b4e34edf7d43a8f695c7662c82 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 19:02:52 +0530 Subject: [PATCH 212/329] updated var names in health check scenario --- .../tests/health_check/environments/xlarge.yaml | 11 +++++------ .../performance/tests/health_check/health_check.yaml | 10 +++++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/performance/tests/health_check/environments/xlarge.yaml b/tests/performance/tests/health_check/environments/xlarge.yaml index f1d6cd14e..689a5d66b 100644 --- a/tests/performance/tests/health_check/environments/xlarge.yaml +++ b/tests/performance/tests/health_check/environments/xlarge.yaml @@ -1,9 +1,8 @@ - --- settings: env: - success_HealthCheck : 100% - avg_rt_HealthCheck : 14ms - total_processes : 1 - sum_all_file_descriptors : 67 - sum_all_memory_rss : 750000000 #750MB \ No newline at end of file + HLTH_CHK_SUCC : 100% + HLTH_CHK_RT : 14ms + TOTAL_PROCS : 1 + TOTAL_FDS : 67 + TOTAL_MEM : 750000000 #750MB \ No newline at end of file diff --git a/tests/performance/tests/health_check/health_check.yaml b/tests/performance/tests/health_check/health_check.yaml index c06c95465..080fa0610 100644 --- a/tests/performance/tests/health_check/health_check.yaml +++ b/tests/performance/tests/health_check/health_check.yaml @@ -33,8 +33,8 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of HealthCheck<${success_HealthCheck}, stop as failed - - avg-rt of HealthCheck>${avg_rt_HealthCheck}, stop as failed + - success of HealthCheck<${HLTH_CHK_SUCC}, stop as failed + - avg-rt of HealthCheck>${HLTH_CHK_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes @@ -46,21 +46,21 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: ${sum_all_file_descriptors} + threshold: ${TOTAL_FDS} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: ${sum_all_memory_rss} +# threshold: ${TOTAL_MEM} # timeframe: 5s # stop : true # fail : true \ No newline at end of file From 80f52d5f10791f61b32a84667a76321d0e29b782 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 19:29:50 +0530 Subject: [PATCH 213/329] updated var names --- .../api_description/api_description.yaml | 8 ++-- .../api_description/environments/xlarge.yaml | 8 ++-- .../tests/health_check/health_check.yaml | 2 +- .../health_check/modified_health_check.jmx | 38 +++++++++++++++++++ .../environments/xlarge.yaml | 14 +++---- .../inference_multiple_models.yaml | 16 ++++---- 6 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 tests/performance/tests/health_check/modified_health_check.jmx diff --git a/tests/performance/tests/api_description/api_description.yaml b/tests/performance/tests/api_description/api_description.yaml index 3c4365a32..ee23dbee2 100644 --- a/tests/performance/tests/api_description/api_description.yaml +++ b/tests/performance/tests/api_description/api_description.yaml @@ -33,10 +33,10 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of ManagementAPIDescription<${MGMT_API_SUCC}, stop as failed - - success of InferenceAPIDescription<${INFR_API_SUCC}, stop as failed - - avg-rt of ManagementAPIDescription>${MGMT_API_RT}, stop as failed - - avg-rt of InferenceAPIDescription>${INFR_API_RT}, stop as failed + - success of ManagementAPIDescription<${MGMT_DESC_SUCC}, stop as failed + - success of InferenceAPIDescription<${INFR_DESC_SUCC}, stop as failed + - avg-rt of ManagementAPIDescription>${MGMT_DESC_RT}, stop as failed + - avg-rt of InferenceAPIDescription>${INFR_DESC_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes diff --git a/tests/performance/tests/api_description/environments/xlarge.yaml b/tests/performance/tests/api_description/environments/xlarge.yaml index 93ca5f819..f7bc5561d 100644 --- a/tests/performance/tests/api_description/environments/xlarge.yaml +++ b/tests/performance/tests/api_description/environments/xlarge.yaml @@ -1,10 +1,10 @@ --- settings: env: - MGMT_API_SUCC: 100% - INFR_API_SUCC: 100% - MGMT_API_RT : 10ms - INFR_API_RT : 10ms + MGMT_DESC_SUCC: 100% + INFR_DESC_SUCC: 100% + MGMT_DESC_RT : 10ms + INFR_DESC_RT : 10ms TOTAL_PROCS : 1 TOTAL_FDS : 73 TOTAL_MEM: 100000000 #100MB diff --git a/tests/performance/tests/health_check/health_check.yaml b/tests/performance/tests/health_check/health_check.yaml index 080fa0610..979af2526 100644 --- a/tests/performance/tests/health_check/health_check.yaml +++ b/tests/performance/tests/health_check/health_check.yaml @@ -39,7 +39,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true diff --git a/tests/performance/tests/health_check/modified_health_check.jmx b/tests/performance/tests/health_check/modified_health_check.jmx new file mode 100644 index 000000000..47437318b --- /dev/null +++ b/tests/performance/tests/health_check/modified_health_check.jmx @@ -0,0 +1,38 @@ + + + + + + false + true + false + + + + + + + continue101030S + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + /ping + GET + true + false + true + false + + + + + + + /Users/prashant_sail/sagemaker-container/multi-model-server/tests/performance/run_artifacts/xlarge_4f0631d_1593007036/health_check/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/Users/prashant_sail/sagemaker-container/multi-model-server/tests/performance/run_artifacts/xlarge_4f0631d_1593007036/health_check/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue + + diff --git a/tests/performance/tests/inference_multiple_models/environments/xlarge.yaml b/tests/performance/tests/inference_multiple_models/environments/xlarge.yaml index 018c6c453..36b7dc0ad 100644 --- a/tests/performance/tests/inference_multiple_models/environments/xlarge.yaml +++ b/tests/performance/tests/inference_multiple_models/environments/xlarge.yaml @@ -1,10 +1,10 @@ --- settings: env: - success_Inference1 : 100% - avg_rt_Inference1 : 290ms - success_Inference2: 100% - avg_rt_Inference2: 450ms - sum_all_file_descriptors : 107 - sum_all_memory_rss : 600000000 - total_processes : 5 \ No newline at end of file + INFR1_SUCC : 100% + INFR2_SUCC: 100% + INFR1_RT : 290ms + INFR2_RT: 450ms + TOTAL_PROCS : 5 + TOTAL_FDS : 107 + TOTAL_MEM : 600000000 #600MB \ No newline at end of file diff --git a/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml b/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml index c8f72faa3..d76183b8f 100644 --- a/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml +++ b/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml @@ -39,36 +39,36 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of Inference1<${success_Inference1}, stop as failed - - success of Inference2<${success_Inference2}, stop as failed - - avg-rt of Inference1>${avg_rt_Inference1}, stop as failed - - avg-rt of Inference2>${avg_rt_Inference2}, stop as failed + - success of Inference1<${INFR1_SUCC}, stop as failed + - success of Inference2<${INFR2_SUCC}, stop as failed + - avg-rt of Inference1>${INFR1_RT}, stop as failed + - avg-rt of Inference2>${INFR2_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: ${sum_all_file_descriptors} + threshold: ${TOTAL_FDS} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: ${sum_all_memory_rss} #600MB +# threshold: ${TOTAL_MEM} # timeframe: 5s # stop : true # fail : true From 6d9d1e39db7fe9c6750dbb0e0b3b0b55c4b60115 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 19:39:02 +0530 Subject: [PATCH 214/329] updated vars --- .../health_check/modified_health_check.jmx | 38 ------------------- .../environments/xlarge.yaml | 11 +++--- .../inference_multiple_worker.yaml | 12 +++--- 3 files changed, 11 insertions(+), 50 deletions(-) delete mode 100644 tests/performance/tests/health_check/modified_health_check.jmx diff --git a/tests/performance/tests/health_check/modified_health_check.jmx b/tests/performance/tests/health_check/modified_health_check.jmx deleted file mode 100644 index 47437318b..000000000 --- a/tests/performance/tests/health_check/modified_health_check.jmx +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - false - true - false - - - - - - - continue101030S - - - - - ${__P(hostname,127.0.0.1)} - ${__P(port,8443)} - ${__P(protocol,https)} - - /ping - GET - true - false - true - false - - - - - - - /Users/prashant_sail/sagemaker-container/multi-model-server/tests/performance/run_artifacts/xlarge_4f0631d_1593007036/health_check/kpi.jtlsaveConfigfalsetruetruetruetruetruetruetruetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruefalse/Users/prashant_sail/sagemaker-container/multi-model-server/tests/performance/run_artifacts/xlarge_4f0631d_1593007036/health_check/error.jtlsaveConfigtruetruetruetruetruetruetruetruetruetruetruetruefalsefalsetruetruetruetruetruetruetruetrue - - diff --git a/tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml b/tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml index 2391dd514..5b9fd6c0a 100644 --- a/tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml +++ b/tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml @@ -1,9 +1,8 @@ - --- settings: env: - success_Inference : 100% - avg_rt_Inference : 140ms - sum_all_file_descriptors : 126 - sum_all_memory_rss : 750000000 - total_processes : 6 \ No newline at end of file + INFR_SUCC : 100% + INFR_RT : 140ms + TOTAL_PROCS : 6 + TOTAL_FDS : 126 + TOTAL_MEM : 750000000 #750MB \ No newline at end of file diff --git a/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml b/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml index 42cf5a8f7..aa1f313c2 100644 --- a/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml +++ b/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml @@ -38,34 +38,34 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of Inference<${success_Inference}, stop as failed - - avg-rt of Inference>${avg_rt_Inference}, stop as failed + - success of Inference<${INFR_SUCC}, stop as failed + - avg-rt of Inference>${INFR_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: ${sum_all_file_descriptors} + threshold: ${TOTAL_FDS} timeframe: 1s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: ${sum_all_memory_rss} +# threshold: ${TOTAL_MEM} # timeframe: 5s # stop : true # fail : true \ No newline at end of file From cc369272d7481cb8ec54fb26d84cef67e2b9c775 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 19:42:01 +0530 Subject: [PATCH 215/329] updated vars --- .../environments/xlarge.yaml | 8 ++++---- .../inference_server_monitoring_criteria.yaml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml b/tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml index 320f9372f..434bae265 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml +++ b/tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml @@ -2,7 +2,7 @@ --- settings: env: - fail : 50% - p90 : 250ms - avg_rt : 1s - sum_workers_file_descriptors : 80 + FAIL : 50% + P90 : 250ms + AVG_RT : 1s + TOTAL_WORKERS_FDS : 80 diff --git a/tests/performance/tests/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml b/tests/performance/tests/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml index c71941570..121802e17 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_server_monitoring_criteria/inference_server_monitoring_criteria.yaml @@ -36,13 +36,13 @@ services: reporting: - module: passfail criteria: - - fail >${fail}, stop as failed - - p90 >${p90} , stop as failed - - avg-rt >${avg_rt} , stop as failed + - fail >${FAIL}, stop as failed + - p90 >${P90} , stop as failed + - avg-rt >${AVG_RT} , stop as failed - class: bzt.modules.monitoring.MonitoringCriteria subject: mms-inference-server/sum_workers_file_descriptors condition: '>' - threshold: ${sum_workers_file_descriptors} + threshold: ${TOTAL_WORKERS_FDS} timeframe: 1s fail: true stop: true From 4107a386094ae1ea228e00d54230ee3b5bc60f62 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 19:57:48 +0530 Subject: [PATCH 216/329] Updated vars --- .../inference_single_worker/environments/xlarge.yaml | 10 +++++----- .../inference_single_worker.yaml | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/performance/tests/inference_single_worker/environments/xlarge.yaml b/tests/performance/tests/inference_single_worker/environments/xlarge.yaml index 656ed23f5..c945e1f91 100644 --- a/tests/performance/tests/inference_single_worker/environments/xlarge.yaml +++ b/tests/performance/tests/inference_single_worker/environments/xlarge.yaml @@ -1,8 +1,8 @@ --- settings: env: - success_Inference : 100% - avg_rt_Inference : 290ms - sum_all_file_descriptors : 90 - sum_all_memory_rss : 290000000 - total_processes : 3 \ No newline at end of file + INFR_SUCC : 100% + INFR_RT : 290ms + TOTAL_PROCS : 3 + TOTAL_FDS : 90 + TOTAL_MEM : 290000000 #290MB \ No newline at end of file diff --git a/tests/performance/tests/inference_single_worker/inference_single_worker.yaml b/tests/performance/tests/inference_single_worker/inference_single_worker.yaml index 6a60e37ad..f04bce5e7 100644 --- a/tests/performance/tests/inference_single_worker/inference_single_worker.yaml +++ b/tests/performance/tests/inference_single_worker/inference_single_worker.yaml @@ -38,34 +38,34 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of Inference<${success_Inference}, stop as failed - - avg-rt of Inference>${avg_rt_Inference}, stop as failed + - success of Inference<${INFR_SUCC}, stop as failed + - avg-rt of Inference>${INFR_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: ${sum_all_file_descriptors} + threshold: ${TOTAL_FDS} timeframe: 1s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: ${sum_all_memory_rss} #290MB +# threshold: ${TOTAL_MEM} # timeframe: 5s # stop : true # fail : true From 9857375e16add827fd4eecc9aa2feed24518bb89 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 20:37:55 +0530 Subject: [PATCH 217/329] updated vars --- .../environments/xlarge.yaml | 1 - .../environments/xlarge.yaml | 11 ++++---- ...ence_taurus_local_monitoring_criteria.yaml | 10 ++++---- .../list_models/environments/xlarge.yaml | 10 ++++---- .../tests/list_models/list_models.yaml | 12 ++++----- .../environments/xlarge.yaml | 10 ++++---- .../model_description/model_description.yaml | 12 ++++----- .../environments/xlarge.yaml | 15 ++++++----- .../register_unregister.yaml | 14 +++++------ .../environments/xlarge.yaml | 19 +++++++------- .../register_unregister_multiple.yaml | 18 ++++++------- .../environments/xlarge.yaml | 25 +++++++++---------- .../scale_down_workers.yaml | 24 +++++++++--------- .../scale_up_workers/environments/xlarge.yaml | 24 +++++++++--------- .../scale_up_workers/scale_up_workers.yaml | 24 +++++++++--------- 15 files changed, 112 insertions(+), 117 deletions(-) diff --git a/tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml b/tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml index 434bae265..674a6c1ff 100644 --- a/tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml +++ b/tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml @@ -1,4 +1,3 @@ - --- settings: env: diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/xlarge.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/xlarge.yaml index 149a251a1..14287339a 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/xlarge.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/xlarge.yaml @@ -1,9 +1,8 @@ - --- settings: env: - fail : 100% - p90 : 290ms - avg_rt : 1s - sum_workers_memory_rss : 132000000 - sum_workers_memory_rss_diff_percent : 30 + FAIL : 100% + P90 : 290ms + AVG_RT : 1s + TOTAL_WORKERS_MEM : 132000000 + PERCENT_DIFF_TOTAL_WORKERS_MEM : 30 diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml b/tests/performance/tests/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml index 3597e75c3..5ab08b048 100644 --- a/tests/performance/tests/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml +++ b/tests/performance/tests/inference_taurus_local_monitoring_criteria/inference_taurus_local_monitoring_criteria.yaml @@ -36,14 +36,14 @@ services: reporting: - module: passfail criteria: - - fail >${fail}, stop as failed - - p90 >${p90} , stop as failed - - avg-rt >${avg_rt} , stop as failed + - fail >${FAIL}, stop as failed + - p90 >${P90} , stop as failed + - avg-rt >${AVG_RT} , stop as failed - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: ${sum_workers_memory_rss} + threshold: ${TOTAL_WORKERS_MEM} timeframe: 1s stop : true fail : true - diff_percent : ${sum_workers_memory_rss_diff_percent} + diff_percent : ${PERCENT_DIFF_TOTAL_WORKERS_MEM} diff --git a/tests/performance/tests/list_models/environments/xlarge.yaml b/tests/performance/tests/list_models/environments/xlarge.yaml index fa5d2f77e..611624824 100644 --- a/tests/performance/tests/list_models/environments/xlarge.yaml +++ b/tests/performance/tests/list_models/environments/xlarge.yaml @@ -1,8 +1,8 @@ --- settings: env: - success_ListModels : 100% - avg_rt_ListModels : 14ms - sum_all_file_descriptors : 86 - sum_all_memory_rss : 185000000 - total_processes : 3 \ No newline at end of file + LST_MODLS_SUCC : 100% + LST_MODLS_RT : 14ms + TOTAL_PROCS : 3 + TOTAL_FDS : 86 + TOTAL_MEM : 185000000 #185MB \ No newline at end of file diff --git a/tests/performance/tests/list_models/list_models.yaml b/tests/performance/tests/list_models/list_models.yaml index ca7270af8..1cda84771 100644 --- a/tests/performance/tests/list_models/list_models.yaml +++ b/tests/performance/tests/list_models/list_models.yaml @@ -35,34 +35,34 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of ListModels<${success_ListModels}, stop as failed - - avg-rt of ListModels>${avg_rt_ListModels}, stop as failed + - success of ListModels<${LST_MODLS_SUCC}, stop as failed + - avg-rt of ListModels>${LST_MODLS_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: ${sum_all_file_descriptors} + threshold: ${TOTAL_FDS} timeframe: 1s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: ${sum_all_memory_rss} #185MB +# threshold: ${TOTAL_MEM} # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/model_description/environments/xlarge.yaml b/tests/performance/tests/model_description/environments/xlarge.yaml index c04a741c1..00e0aac87 100644 --- a/tests/performance/tests/model_description/environments/xlarge.yaml +++ b/tests/performance/tests/model_description/environments/xlarge.yaml @@ -1,8 +1,8 @@ --- settings: env: - success_ModelDescription : 100% - avg_rt_ModelDescription : 14ms - sum_all_file_descriptors : 90 - sum_all_memory_rss : 300000000 - total_processes : 3 \ No newline at end of file + MODL_DESC_SUCC : 100% + MODL_DESC_RT : 14ms + TOTAL_PROCS : 3 + TOTAL_FDS : 90 + TOTAL_MEM : 300000000 #300MB \ No newline at end of file diff --git a/tests/performance/tests/model_description/model_description.yaml b/tests/performance/tests/model_description/model_description.yaml index 35dd9a7f0..4d36ffd77 100644 --- a/tests/performance/tests/model_description/model_description.yaml +++ b/tests/performance/tests/model_description/model_description.yaml @@ -35,34 +35,34 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of ModelDescription<${success_ModelDescription}, stop as failed - - avg-rt of ModelDescription>${avg_rt_ModelDescription}, stop as failed + - success of ModelDescription<${MODL_DESC_SUCC}, stop as failed + - avg-rt of ModelDescription>${MODL_DESC_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: ${sum_all_file_descriptors} + threshold: ${TOTAL_FDS} timeframe: 1s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/sum_all_memory_rss # condition: '>' -# threshold: ${sum_all_memory_rss} +# threshold: ${TOTAL_MEM} # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/register_unregister/environments/xlarge.yaml b/tests/performance/tests/register_unregister/environments/xlarge.yaml index c5ff1754a..a5d1cee32 100644 --- a/tests/performance/tests/register_unregister/environments/xlarge.yaml +++ b/tests/performance/tests/register_unregister/environments/xlarge.yaml @@ -1,11 +1,10 @@ - --- settings: env: - success_RegisterModel : 100% - avg_rt_RegisterModel : 15s - success_UnRegisterModel: 100% - avg_rt_UnRegisterModel: 10ms - sum_all_file_descriptors : 66 - frontend_memory_rss : 75000000 - total_processes : 1 \ No newline at end of file + REG_SUCC : 100% + UNREG_SUCC: 100% + REG_RT : 15s + UNREG_RT: 10ms + TOTAL_PROCS : 1 + TOTAL_FDS : 66 + FRNTEND_MEM : 75000000 #75MB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister/register_unregister.yaml b/tests/performance/tests/register_unregister/register_unregister.yaml index 108487403..ca8a616f6 100644 --- a/tests/performance/tests/register_unregister/register_unregister.yaml +++ b/tests/performance/tests/register_unregister/register_unregister.yaml @@ -35,29 +35,29 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of RegisterModel<${success_RegisterModel}, stop as failed - - success of UnregisterModel<${success_UnRegisterModel}, stop as failed - - avg-rt of RegisterModel>${avg_rt_RegisterModel}, stop as failed - - avg-rt of UnregisterModel>${avg_rt_UnRegisterModel}, stop as failed + - success of RegisterModel<${REG_SUCC}, stop as failed + - success of UnregisterModel<${UNREG_SUCC}, stop as failed + - avg-rt of RegisterModel>${REG_RT}, stop as failed + - avg-rt of UnregisterModel>${UNREG_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: ${sum_all_file_descriptors} + threshold: ${TOTAL_FDS} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/frontend_memory_rss # condition: '>' -# threshold: ${frontend_memory_rss} #75MB +# threshold: ${FRNTEND_MEM} # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml b/tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml index e98d3589f..24c07f5cf 100644 --- a/tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml +++ b/tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml @@ -1,13 +1,12 @@ - --- settings: env: - success_RegisterModel : 100% - avg_rt_RegisterModel : 15s - success_UnRegisterModel: 100% - avg_rt_UnRegisterModel: 18ms - success_ScaleUp: 100% - avg_rt_ScaleUp: 1.5s - sum_all_file_descriptors : 73 - frontend_memory_rss : 120000000 - total_processes : 2 \ No newline at end of file + REG_SUCC : 100% + SCL_UP_SUCC: 100% + UNREG_SUCC: 100% + REG_RT : 15s + SCL_UP_RT: 1.5s + UNREG_RT: 18ms + TOTAL_PROCS : 2 + TOTAL_FDS : 73 + FRNTEND_MEM : 120000000 #120MB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml index d99294b99..062553357 100644 --- a/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml +++ b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml @@ -36,31 +36,31 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of RegisterModel<${success_RegisterModel}, stop as failed - - success of ScaleUp<${success_ScaleUp}, stop as failed - - success of UnregisterModel<${success_UnRegisterModel}, stop as failed - - avg-rt of RegisterModel>${avg_rt_RegisterModel}, stop as failed - - avg-rt of ScaleUp>${avg_rt_ScaleUp}, stop as failed - - avg-rt of UnregisterModel>${avg_rt_UnRegisterModel}, stop as failed + - success of RegisterModel<${REG_SUCC}, stop as failed + - success of ScaleUp<${SCL_UP_SUCC}, stop as failed + - success of UnregisterModel<${UNREG_SUCC}, stop as failed + - avg-rt of RegisterModel>${REG_RT}, stop as failed + - avg-rt of ScaleUp>${SCL_UP_RT}, stop as failed + - avg-rt of UnregisterModel>${UNREG_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${total_processes} + threshold: ${TOTAL_PROCS} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' - threshold: ${sum_all_file_descriptors} + threshold: ${TOTAL_FDS} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/frontend_memory_rss # condition: '>' -# threshold: ${frontend_memory_rss} #120MB +# threshold: ${FRNTEND_MEM} # timeframe: 5s # stop : true # fail : true \ No newline at end of file diff --git a/tests/performance/tests/scale_down_workers/environments/xlarge.yaml b/tests/performance/tests/scale_down_workers/environments/xlarge.yaml index 10bcb3d7e..6d43899b3 100644 --- a/tests/performance/tests/scale_down_workers/environments/xlarge.yaml +++ b/tests/performance/tests/scale_down_workers/environments/xlarge.yaml @@ -1,16 +1,15 @@ --- settings: env: - success_ScaleDown : 100% - avg_rt_ScaleDown : 10ms - min_sum_workers_file_descriptors : 23 - max_sum_workers_file_descriptors : 38 - frontend_file_descriptors : 78 - min_sum_workers_memory_rss : 210000000 - max_sum_workers_memory_rss : 450000000 - frontend_memory_rss : 290000000 - min_total_workers : 2 - max_total_workers : 4 - max_total_processes : 6 - min_total_processes : 4 - + SCL_DWN_SUCC : 100% + SCL_DWN_RT : 10ms + TOTAL_PROCS_B4_SCL_DWN : 6 + TOTAL_PROCS_AFTR_SCL_DWN : 4 + TOTAL_WRKRS_B4_SCL_DWN : 4 + TOTAL_WRKRS_AFTR_SCL_DWN : 2 + FRNTEND_FDS : 78 + TOTAL_WRKRS_FDS_B4_SCL_DWN: 38 + TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 23 + FRNTEND_MEM : 290000000 #290MB + TOTAL_WRKRS_MEM_B4_SCL_DWN : 450000000 #450MB + TOTAL_WRKRS_MEM_AFTR_SCL_DWN : 210000000 #210MB \ No newline at end of file diff --git a/tests/performance/tests/scale_down_workers/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers/scale_down_workers.yaml index ed071bed1..54787b21b 100644 --- a/tests/performance/tests/scale_down_workers/scale_down_workers.yaml +++ b/tests/performance/tests/scale_down_workers/scale_down_workers.yaml @@ -38,76 +38,76 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of ScaleDown<${success_ScaleDown}, stop as failed - - avg-rt of ScaleDown>${avg_rt_ScaleDown}, stop as failed + - success of ScaleDown<${SCL_DWN_SUCC}, stop as failed + - avg-rt of ScaleDown>${SCL_DWN_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${max_total_processes} + threshold: ${TOTAL_PROCS_B4_SCL_DWN} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: ${min_total_processes} + threshold: ${TOTAL_PROCS_AFTR_SCL_DWN} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '>' - threshold: ${max_total_workers} + threshold: ${TOTAL_WRKRS_B4_SCL_DWN} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '<' - threshold: ${min_total_workers} + threshold: ${TOTAL_WRKRS_AFTR_SCL_DWN} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/frontend_file_descriptors condition: '>' - threshold: ${frontend_file_descriptors} + threshold: ${FRNTEND_FDS} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '>' - threshold: ${max_sum_workers_file_descriptors} + threshold: ${TOTAL_WRKRS_FDS_B4_SCL_DWN} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '<' - threshold: ${min_sum_workers_file_descriptors} + threshold: ${TOTAL_WRKRS_FDS_AFTR_SCL_DWN} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/frontend_memory_rss # condition: '>' -# threshold: ${frontend_memory_rss} #290MB +# threshold: ${FRNTEND_MEM} # timeframe: 5s # stop : true # fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: ${max_sum_workers_memory_rss} #450MB + threshold: ${TOTAL_WRKRS_MEM_B4_SCL_DWN} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '<' - threshold: ${min_sum_workers_memory_rss} #230MB + threshold: ${TOTAL_WRKRS_MEM_AFTR_SCL_DWN} timeframe: 5s stop : true fail : true \ No newline at end of file diff --git a/tests/performance/tests/scale_up_workers/environments/xlarge.yaml b/tests/performance/tests/scale_up_workers/environments/xlarge.yaml index dd6932853..9e3182c3e 100644 --- a/tests/performance/tests/scale_up_workers/environments/xlarge.yaml +++ b/tests/performance/tests/scale_up_workers/environments/xlarge.yaml @@ -1,15 +1,15 @@ --- settings: env: - success_ScaleUp : 100% - avg_rt_ScaleUp : 10ms - min_sum_workers_file_descriptors : 11 - max_sum_workers_file_descriptors : 38 - frontend_file_descriptors : 88 - min_sum_workers_memory_rss : 115000000 - max_sum_workers_memory_rss : 450000000 - frontend_memory_rss : 290000000 - min_total_workers : 1 - max_total_workers : 4 - max_total_processes : 6 - min_total_processes : 3 \ No newline at end of file + SCL_UP_SUCC : 100% + SCL_UP_RT : 10ms + TOTAL_PROCS_AFTR_SCL_UP : 6 + TOTAL_PROCS_B4_SCL_UP : 3 + TOTAL_WRKRS_AFTR_SCL_UP : 4 + TOTAL_WRKRS_B4_SCL_UP : 1 + FRNTEND_FDS : 88 + TOTAL_WRKRS_FDS_AFTR_SCL_UP : 38 + TOTAL_WRKRS_FDS_B4_SCL_UP : 11 + FRNTEND_MEM : 290000000 #290MB + TOTAL_WRKRS_MEM_AFTR_SCL_UP : 450000000 #450MB + TOTAL_WRKRS_MEM_B4_SCL_UP : 115000000 #115MB \ No newline at end of file diff --git a/tests/performance/tests/scale_up_workers/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers/scale_up_workers.yaml index 0d584fc1a..ee4ea5d89 100644 --- a/tests/performance/tests/scale_up_workers/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers/scale_up_workers.yaml @@ -38,76 +38,76 @@ reporting: - module: passfail criteria: # Inbuilt Criteria - - success of ScaleUp<${success_ScaleUp}, stop as failed - - avg-rt of ScaleUp>${avg_rt_ScaleUp}, stop as failed + - success of ScaleUp<${SCL_UP_SUCC}, stop as failed + - avg-rt of ScaleUp>${SCL_UP_RT}, stop as failed # Custom Criteria - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${max_total_processes} + threshold: ${TOTAL_PROCS_AFTR_SCL_UP} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '<' - threshold: ${min_total_processes} + threshold: ${TOTAL_PROCS_B4_SCL_UP} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '>' - threshold: ${max_total_workers} + threshold: ${TOTAL_WRKRS_AFTR_SCL_UP} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_workers condition: '<' - threshold: ${min_total_workers} + threshold: ${TOTAL_WRKRS_B4_SCL_UP} timeframe: 1s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/frontend_file_descriptors condition: '>' - threshold: ${frontend_file_descriptors} + threshold: ${FRNTEND_FDS} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '>' - threshold: ${max_sum_workers_file_descriptors} + threshold: ${TOTAL_WRKRS_FDS_AFTR_SCL_UP} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_file_descriptors condition: '<' - threshold: ${min_sum_workers_file_descriptors} + threshold: ${TOTAL_WRKRS_FDS_B4_SCL_UP} timeframe: 5s stop : true fail : true # - class: bzt.modules.monitoring.MonitoringCriteria # subject: ServerLocalClient/frontend_memory_rss # condition: '>' -# threshold: ${frontend_memory_rss} #290MB +# threshold: ${FRNTEND_MEM} # timeframe: 5s # stop : true # fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: ${max_sum_workers_memory_rss} #450MB + threshold: ${TOTAL_WRKRS_MEM_AFTR_SCL_UP} timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '<' - threshold: ${min_sum_workers_memory_rss} #122MB + threshold: ${TOTAL_WRKRS_MEM_B4_SCL_UP} timeframe: 5s stop : true fail : true \ No newline at end of file From c4ca7d865409013faf475adf14304ee4be9e334a Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 21:33:02 +0530 Subject: [PATCH 218/329] replace dev_env1 with xlarge --- .../examples_local_criteria/environments/dev_env1.yaml | 9 --------- .../environments/xlarge.yaml | 0 .../examples_remote_criteria/environments/dev_env1.yaml | 8 -------- .../environments/xlarge.yaml | 0 4 files changed, 17 deletions(-) delete mode 100644 tests/performance/tests/examples_local_criteria/environments/dev_env1.yaml rename tests/performance/tests/{inference_taurus_local_monitoring_criteria => examples_local_criteria}/environments/xlarge.yaml (100%) delete mode 100644 tests/performance/tests/examples_remote_criteria/environments/dev_env1.yaml rename tests/performance/tests/{inference_server_monitoring_criteria => examples_remote_criteria}/environments/xlarge.yaml (100%) diff --git a/tests/performance/tests/examples_local_criteria/environments/dev_env1.yaml b/tests/performance/tests/examples_local_criteria/environments/dev_env1.yaml deleted file mode 100644 index 05e0c3197..000000000 --- a/tests/performance/tests/examples_local_criteria/environments/dev_env1.yaml +++ /dev/null @@ -1,9 +0,0 @@ - ---- -settings: - env: - fail : 100% - p90 : 290ms - avg_rt : 1s - sum_workers_memory_rss : 140000000 - sum_workers_memory_rss_diff_percent : 30 diff --git a/tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/xlarge.yaml b/tests/performance/tests/examples_local_criteria/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_taurus_local_monitoring_criteria/environments/xlarge.yaml rename to tests/performance/tests/examples_local_criteria/environments/xlarge.yaml diff --git a/tests/performance/tests/examples_remote_criteria/environments/dev_env1.yaml b/tests/performance/tests/examples_remote_criteria/environments/dev_env1.yaml deleted file mode 100644 index 320f9372f..000000000 --- a/tests/performance/tests/examples_remote_criteria/environments/dev_env1.yaml +++ /dev/null @@ -1,8 +0,0 @@ - ---- -settings: - env: - fail : 50% - p90 : 250ms - avg_rt : 1s - sum_workers_file_descriptors : 80 diff --git a/tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml b/tests/performance/tests/examples_remote_criteria/environments/xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_server_monitoring_criteria/environments/xlarge.yaml rename to tests/performance/tests/examples_remote_criteria/environments/xlarge.yaml From fd3efaac9763a8d3d215aaae18668f98b995f52d Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 22:05:29 +0530 Subject: [PATCH 219/329] TOTAL_WORKERS_MEM to batch infer --- .../batch_and_single_inference/batch_and_single_inference.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml b/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml index 8aaadd663..a1fea699c 100644 --- a/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml +++ b/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml @@ -65,7 +65,7 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_workers_memory_rss condition: '>' - threshold: ${TOTAL_WRKR_MEM} + threshold: ${TOTAL_WORKERS_MEM} timeframe: 1s stop : true fail : true From 21e964e8204deb4c8273f9b58ad267edc97d8add Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 24 Jun 2020 23:05:46 +0530 Subject: [PATCH 220/329] hacky glob to exclude examples by default --- tests/performance/run_performance_suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 502d640c7..1e7fafaaf 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -433,7 +433,7 @@ def get_options(artifacts_dir, jmeter_path=None): @click.command() @click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True)) @click.option('-t', '--test-dir', help='Directory containing tests.', type=click.Path(exists=True), default=None) -@click.option('-p', '--pattern', help='Test case folder name pattern', default="[!example]*") +@click.option('-p', '--pattern', help='Test case folder name pattern', default="[!e][!x]*") @click.option('-j', '--jmeter-path', help='JMeter executable path.') @click.option('--monit/--no-monit', help='Start Monitoring server', default=True) @click.option('-e', '--env-name', help='environment name which defines threshold values used by the test cases. ' From 08731370e38206521be93cfc95505c035c080eb2 Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Thu, 25 Jun 2020 09:56:48 +0530 Subject: [PATCH 221/329] fix links to reflect new structure... Signed-off-by: Dhruva Ray --- tests/performance/TESTS.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/performance/TESTS.md b/tests/performance/TESTS.md index bb473ff20..6108a7d91 100644 --- a/tests/performance/TESTS.md +++ b/tests/performance/TESTS.md @@ -13,13 +13,13 @@ |API|CODE|YAML| |---|----|---| -|Register Model|A,B,F,J|[register_unregister.yaml](tests/register_unregister.yaml)| -|Inference|A,B,C|[inference_single_worker.yaml](tests/inference_single_worker.yaml), [inference_multiple_worker.yaml](tests/inference_multiple_worker.yaml)| -|Batch Inference|A,B,C|[batch_inference.yaml](tests/batch_inference.yaml)| -|Custom Model Handlers|C|[batch_and_single_inference.yaml](tests/batch_and_single_inference.yaml)| -|Scale Workers - UP/DOWN|D,G,I,F,J|[scale_up_workers.yaml](tests/scale_up_workers.yaml), [scale_down_workers.yaml](tests/scale_down_workers.yaml)| -|Unregister Models|D,G,I,J|[register_unregister.yaml](tests/register_unregister.yaml)| -|Health Check|A,B,E|[health_check.yaml](tests/health_check.yaml)| -|API Description|A,B,E|[api_description.yaml](tests/api_description.yaml)| -|Model Describe|A,B,E|[model_description.yaml](tests/model_description.yaml)| -|List Models|A,B,E|[list_models.yaml](tests/list_models.yaml)| +|Register Model|A,B,F,J|[register_unregister](tests/register_unregister)| +|Inference|A,B,C|[inference_single_worker](tests/inference_single_worker), [inference_multiple_worker](tests/inference_multiple_worker)| +|Batch Inference|A,B,C|[batch_inference](tests/batch_inference)| +|Custom Model Handlers|C|[batch_and_single_inference](tests/batch_and_single_inference)| +|Scale Workers - UP/DOWN|D,G,I,F,J|[scale_up_workers](tests/scale_up_workers), [scale_down_workers](tests/scale_down_workers)| +|Unregister Models|D,G,I,J|[register_unregister](tests/register_unregister)| +|Health Check|A,B,E|[health_check](tests/health_check)| +|API Description|A,B,E|[api_description](tests/api_description)| +|Model Describe|A,B,E|[model_description](tests/model_description)| +|List Models|A,B,E|[list_models](tests/list_models)| From 746bb0e8db4ee8fe62bfb23f1dc7b8be9b01ba1b Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Thu, 25 Jun 2020 17:17:44 +0530 Subject: [PATCH 222/329] update README to put motivation section Signed-off-by: Dhruva Ray --- tests/performance/README.md | 48 ++++++++++++++++++++++------ tests/performance/assets/blocks.png | Bin 0 -> 50131 bytes 2 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 tests/performance/assets/blocks.png diff --git a/tests/performance/README.md b/tests/performance/README.md index 884130569..86baa61a8 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -1,8 +1,38 @@ # Performance Regression Suite -This test suite helps in running load tests and monitoring the process and system wide metrics. -It allows to specify the pass/fail criteria for metrics in the test case. -We use Taurus with JMeter as a test automation framework to run the test cases and metrics monitoring. +## Motivation +The goal of this test suite is to ensure that performance regressions are detected early on. Ideally, with every commit +made into the source control system. + +The salient features of the performance regression suite are + +* Non-intrusive - Does not need any code-changes or instrumentation on the server being monitored. +* It can be used to monitor a wide variety of server metrics - memory, cpu, io - in addition to +traditional API level metrics such as latency, throughput etc. +* It is easy to add custom metrics. For example, in MMS server, `the number of workers spawned` would be an interesting +metric to track. The platform allows for easy addition of these metrics. +* The list of available server metrics can be found [here](#Metrics-that-you-can-use-for-passfail-criteria) +* Test cases are specified in human readable yaml files. Every test case has a pass or fail status. This is determined +by evaluating expressions specified in the test case. Every expression checks metrics against threshold values. For +example, `memory consumed by all workers < 500M`, `number of worker processes < 3` +* Test cases execute against compute environments. The threshold values are specific to the compute environment. It is +possible to specify multiple compute environments against which the test cases will run. It follows that each compute +environment, will have its own threshold values. +* This suite leverages the open source [Taurus framework](https://gettaurus.org/). +* This suite extends the Taurus framework in the following ways + * Adds resource monitoring service. This allows us to add MMS specific metrics. + * Environments as described earlier + * Specification of pass/fail criterion between two commits. For example, memory consumed by workers should not + increase by more than 10% between two commits for the given test case. + * Custom reporting of results + +The lay of the land is captured in the following drawing +![](assets/blocks.png) + + + + + ### A. Installation Prerequisites 1. Install Taurus. Taurus needs Python3 but since your tests and MMS instance can run in different virtual environment @@ -42,7 +72,7 @@ values which are specific to the execution environment. This is a mandatory para 1. Starts the metrics monitoring server. 2. Collects all the tests from test-dir satisfying the pattern 3. Executes the tests - 4. Generates performance XML and HTML report in artifacts-dir. + 4. Generates artifacts in the artifacts-dir against each test case. 3. Check the console logs, $artifacts-dir$//performance_results.html report, comparison.csv, comparison.html and other artifacts. @@ -56,7 +86,7 @@ and other artifacts. # check variables such as MMS server PORT etc # vi tests/common/global_config.yaml - python -m run_performance_suite --env-name xlarge + python -m run_performance_suite -e xlarge ``` @@ -66,7 +96,7 @@ test yaml is treated as a test suite. Different criteria in the yaml are treated specified in the yaml, test suite is marked as skipped with 0 test cases. 2. For each test suite a sub-directory is created with artifacts for it. 3. The comparison_results.csv contains diff for monitoring metrics between an ongoing run and a previous run -which was ran for same MMS server. Comparison.html shows the comparision results. +which was ran for same MMS server. Comparison.html shows the comparison results. ## How to add test case to test suite. @@ -231,8 +261,8 @@ reporting: #### 3.2 Add pass/fail criteria with previous run On completion, the test suite runner script compares the monitoring metrics with values from a previous run which was executed on same environment. Note that at least one test suite run on the same environment should have happened in order to do the comparison. The run results are stored in either a local folder or a S3 bucket based on the `compare-local` option -Metrics which have 'diff_percent' value specified in the pass/fail criterion are used for comparision with the previous run. See pass/fail criteria [section](#3-add-passfail-criteria) -Below are different options used by run_performance_suite script for coparison. +Metrics which have 'diff_percent' value specified in the pass/fail criterion are used for comparison with the previous run. See pass/fail criteria [section](#3-add-passfail-criteria) +Below are different options used by run_performance_suite script for comparison. 1. **artifacts-dir**: This is an optional parameter. The default is './run_artifacts' directory. A sub directory with '{env_name}_{git_commit_id}_{timestamp}' gets created in the artifacts dir. @@ -300,5 +330,3 @@ reporting: ## Test Strategy More details about our testing strategy and test cases can be found [here](TESTS.md) -## TODOs -1. Auto threshold calculation diff --git a/tests/performance/assets/blocks.png b/tests/performance/assets/blocks.png new file mode 100644 index 0000000000000000000000000000000000000000..44e8cdbefed89db083a28e26ac3d03d8c4ef9c39 GIT binary patch literal 50131 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�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 JLc%EU{{j9^niK#4 literal 0 HcmV?d00001 From b37ed3bbbd0ceb596748c7add11ed31542eec885 Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Thu, 25 Jun 2020 19:07:11 +0530 Subject: [PATCH 223/329] refactor README.md Signed-off-by: Dhruva Ray --- tests/performance/README.md | 311 +++++++++++++++++------------------- 1 file changed, 146 insertions(+), 165 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 86baa61a8..6f02a8897 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -11,48 +11,41 @@ The salient features of the performance regression suite are traditional API level metrics such as latency, throughput etc. * It is easy to add custom metrics. For example, in MMS server, `the number of workers spawned` would be an interesting metric to track. The platform allows for easy addition of these metrics. -* The list of available server metrics can be found [here](#Metrics-that-you-can-use-for-passfail-criteria) * Test cases are specified in human readable yaml files. Every test case has a pass or fail status. This is determined by evaluating expressions specified in the test case. Every expression checks metrics against threshold values. For -example, `memory consumed by all workers < 500M`, `number of worker processes < 3` +example, `memory consumed by all workers < 500M`, `number of worker processes < 3`. * Test cases execute against compute environments. The threshold values are specific to the compute environment. It is possible to specify multiple compute environments against which the test cases will run. It follows that each compute environment, will have its own threshold values. * This suite leverages the open source [Taurus framework](https://gettaurus.org/). * This suite extends the Taurus framework in the following ways - * Adds resource monitoring service. This allows us to add MMS specific metrics. - * Environments as described earlier + * Adds resource monitoring service. This allows MMS specific metrics to be added. + * Environments as described earlier. * Specification of pass/fail criterion between two commits. For example, memory consumed by workers should not increase by more than 10% between two commits for the given test case. - * Custom reporting of results + * Custom reporting of results. -The lay of the land is captured in the following drawing -![](assets/blocks.png) - - - +The building blocks of the performance regression suite and flow is captured in the following drawing +![](assets/blocks.png) +## Quickstart -### A. Installation Prerequisites -1. Install Taurus. Taurus needs Python3 but since your tests and MMS instance can run in different virtual environment -or machine, you can configure the system such that tests are running on Python3 and MMS instance can run on Python 2 -or 3. -Refer the [link](https://gettaurus.org/docs/Installation/) for more details on installation. +### A. Installation +1. Install Taurus. Refer the [link](https://gettaurus.org/docs/Installation/) for more details on installation. ```bash pip install bzt # Needs python3.6+ ``` -2. Install other dependencies. +2. Install performance regression suite dependencies. ```bash export MMS_HOME= pip install -r $MMS_HOME/tests/performance/requirements.txt ``` -3. Make sure that `git` is installed and the test suites are run from the MMS working directory. This is used to -compare performance regressions across runs and the run artifacts are stored in a folder which have the commit SHA. +3. Make sure that `git` is installed and the test suites are run from the MMS working directory. ### B. Running the test suite -1. Make sure parameters set in the [tests/common/global_config.yaml](tests/performance/tests/global_config.yaml) are correct. -2. To run the test suite you need to execute [run_performance_suite.py](run_performance_suite.py) with the following +1. Make sure parameters set in [tests/common/global_config.yaml](tests/performance/tests/global_config.yaml) are correct. +2. To run the test suite execute [run_performance_suite.py](run_performance_suite.py) with the following parameters * `--artifacts-dir` or `-a` is a directory where the test case results will be stored. The default value is @@ -61,62 +54,61 @@ parameters * `--test-dir` or `-t` is a directory containing the test cases. The default value is `$MMS_HOME/tests/performance/tests`. - * `--pattern` or `-p` picks up certain test cases for execution within the `test-dir`. The default value is to pick up + * `--pattern` or `-p` picks up certain test cases for execution within the `test-dir`. The default value picks up all test cases. * `--env-name` or `-e` specifies the environment name to use while running the test cases. The environment name is the name of the file (minus the extension) found inside the environments folder in each test case. They encapsulate parameter values which are specific to the execution environment. This is a mandatory parameter. - Script does the following: - 1. Starts the metrics monitoring server. - 2. Collects all the tests from test-dir satisfying the pattern - 3. Executes the tests - 4. Generates artifacts in the artifacts-dir against each test case. + The script does the following: + 1. Starts the metrics monitoring server. + 2. Collects all the tests from test-dir satisfying the pattern + 3. Executes the tests + 4. Generates artifacts in the artifacts-dir against each test case. 3. Check the console logs, $artifacts-dir$//performance_results.html report, comparison.csv, comparison.html and other artifacts. - **Steps are provided below** - ```bash - export MMS_HOME= - cd $MMS_HOME/tests/performance - - # Note that MMS server started and stopped by the individual test suite. - # check variables such as MMS server PORT etc - # vi tests/common/global_config.yaml - - python -m run_performance_suite -e xlarge - ``` +**Steps are provided below** +```bash +export MMS_HOME= +cd $MMS_HOME/tests/performance + +# Note that MMS server started and stopped by the individual test suite. +# check variables such as MMS server PORT etc +# vi tests/common/global_config.yaml -### C. Understanding the test suite artifacts and reports -1. The $artifacts-dir$//performance_results.html contains the summary report of the test run. Note that each -test yaml is treated as a test suite. Different criteria in the yaml are treated as test cases. If criteria is not - specified in the yaml, test suite is marked as skipped with 0 test cases. -2. For each test suite a sub-directory is created with artifacts for it. -3. The comparison_results.csv contains diff for monitoring metrics between an ongoing run and a previous run -which was ran for same MMS server. Comparison.html shows the comparison results. +python -m run_performance_suite -e xlarge +``` +### C. Understanding the test suite artifacts and reports +1. The $artifacts-dir$//performance_results.html is a summary report of the test run. +2. Each test yaml is treated as a test suite. Each criteria in the test suite is treated as a test case. +If the test suite does not specify any criteria, then the test suite is reported as skipped with 0 test cases. +3. For each test suite, a sub-directory is created containing relevant run artifacts. +4. The $artifacts-dir$//comparison_results.html is a summary report which shows performance difference between +the last two commits. -## How to add test case to test suite. +## Add a new test Follow these three steps to add a new test case to the test suite. -1. Add scenario +1. Add scenario (a.k.a test suite) 2. Add metrics to monitor -3. Add pass/fail criteria +3. Add pass/fail criteria (a.k.a test case) -#### 1. Add scenario -Create a folder for your test under `test_dir` location. A test generally comprises on a jmeter file - containing the +#### 1. Add scenario (a.k.a test suite) +Create a folder for the test under `test_dir` location. A test generally comprises of a jmeter file - containing the load scenario and a yaml file which contains test scenarios specifying the conditions for failure or success. The -filenames should be identical to the folder name with their respective extensions. +file-names should be identical to the folder name with their respective extensions. -To get you started quickly, we have provided an example [jmeter script](tests/examples_starter/examples_starter.jmx) -and a [scenario](tests/examples_starter/examples_starter.yaml). +An example [jmeter script](tests/examples_starter/examples_starter.jmx) +and a [scenario](tests/examples_starter/examples_starter.yaml) is provided as a template to get started. -Note variables used by examples_starter.jmx script are specified in +Please note that various global configuration settings used by examples_starter.jmx script are specified in [tests/global_config.yaml](tests/performance/tests/global_config.yaml) file. ```tests/examples_starter/examples_starter.yaml @@ -132,7 +124,7 @@ Note variables used by examples_starter.jmx script are specified in ``` -To run this individual test, run the following command +To execute this test suite, run the following command ```bash export MMS_HOME= @@ -141,40 +133,34 @@ To run this individual test, run the following command ``` **Note**: -Taurus provides support for different executors such as JMeter. You can use test script written in those -frameworks as it is. Details about executor types are provided [here](https://gettaurus.org/docs/ExecutionSettings/). -Details about how to run an existing JMeter script are provided [here](https://gettaurus.org/docs/JMeter/). +Taurus provides support for different executors such as JMeter. Supported executor types can be found [here](https://gettaurus.org/docs/ExecutionSettings/). +Details about how to use an existing JMeter script are provided [here](https://gettaurus.org/docs/JMeter/). #### 2. Add metrics to monitor -You can specify different metrics in services/monitoring section of the yaml. -Metrics can be monitored in two ways: +Specify the metrics of interest in the services/monitoring section of the yaml. 1. Standalone monitoring server - If your MMS server is hosted on different machine, you will be using this method. Before running the test case - you have to start a [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will be communicating - with Taurus test client over sockets. The address and port(default=9009) of the monitoring script should be - specified in test case yaml. - - **Note**: While running Test suite runner script, no need to manually start the monitoring server. - The scripts optionally but by default starts and stops it in setup and teardown. + Use this technique if MMS and the tests execute on different machines. Before running the test cases, + please start the [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will + communicate server metric data with the test client over sockets. The monitoring server runs on port 9009 by default. - To start monitoring server run commands below: + To start the monitoring server, run the following commands on the MMS host: ```bash export MMS_HOME= pip install -r $MMS_HOME/tests/performance/requirements.txt python $MMS_HOME/tests/performance/metrics_monitoring_server.py --start ``` - - Sample monitoring section config. + + The monitoring section configuration is shown below. ```yaml services: - module: monitoring server-agent: - - address: localhost:9009 # metric monitoring service address - label: mms-inference-server # if you specify label, it will be used in reports instead of ip:port + - address: :9009 # metric monitoring service address + label: mms-inference-server # Specified label will be used in reports instead of ip:port interval: 1s # polling interval logging: True # those logs will be saved to "SAlogs_192.168.0.1_9009.csv" in the artifacts dir metrics: # metrics should be supported by monitoring service @@ -183,9 +169,9 @@ Metrics can be monitored in two ways: - sum_num_handles - server_workers # no of mms workers ``` - Complete yaml can be found [here](tests/examples_remote_monitoring/examples_remote_monitoring.yaml) + The complete yaml can be found [here](tests/examples_remote_monitoring/examples_remote_monitoring.yaml) - Use the command below to run the test yaml. + Use the command below to run the test suite. ```bash export MMS_HOME= @@ -193,23 +179,10 @@ Metrics can be monitored in two ways: python -m run_performance_suite -p examples_remote_monitoring -e xlarge ``` - 2. Local monitoring plugin - If your test client is running on the server itself, you may want to use this method. - We have provided a custom Taurus plugin as [metrics_monitoring_taurus.py](metrics_monitoring_taurus.py). - - **Note**: To know the list of supported/available metrics check [here](metrics_monitoring_taurus.py) - **Note**: While running Test suite runner script, no need to manually update the PYTHONPATH. The scripts updates it. - - Use commands below to update PYTHONPATH so that plugin gets picked up by Taurus. - - ```bash - export MMS_HOME= - export PYTHONPATH=$MMS_HOME/tests/performance:$PYTHONPATH - ``` - - Sample monitoring section config. + Use this technique if both MMS and the tests run on the same host. + The monitoring section configuration is shown below. ```yaml modules: @@ -228,9 +201,9 @@ Metrics can be monitored in two ways: - sum_memory_percent ``` - Complete yaml can be found [here](tests/examples_local_monitoring/examples_local_monitoring.yaml) + The complete yaml can be found [here](tests/examples_local_monitoring/examples_local_monitoring.yaml). - Use command below to run the test yaml + Use the command below to run the test suite. ```bash export MMS_HOME= @@ -238,95 +211,103 @@ Metrics can be monitored in two ways: python -m run_performance_suite -p examples_local_monitoring -e xlarge ``` -#### 3.1 Add pass/fail criteria -You can specify the pass/fail criteria for the test cases. -Read more about it [here](https://gettaurus.org/docs/PassFail/) - -Relevant test yaml section: -```yaml -reporting: -- module: passfail - criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - subject: mms-inference-server/sum_num_handles - condition: '>' - threshold: 180 - timeframe: 1s - fail: true - stop: true - diff_percent : 30 +#### 3. Add pass/fail criteria (a.k.a test case) -``` +1. **Specify the pass/fail criteria**. Each pass-fail criterion maps to a test case in the generated report. We leverage the +pass-fail module from Taurus to achieve this functionality. More details can be found [here](https://gettaurus.org/docs/PassFail/). -#### 3.2 Add pass/fail criteria with previous run -On completion, the test suite runner script compares the monitoring metrics with values from a previous run which was executed on same environment. -Note that at least one test suite run on the same environment should have happened in order to do the comparison. The run results are stored in either a local folder or a S3 bucket based on the `compare-local` option -Metrics which have 'diff_percent' value specified in the pass/fail criterion are used for comparison with the previous run. See pass/fail criteria [section](#3-add-passfail-criteria) -Below are different options used by run_performance_suite script for comparison. -1. **artifacts-dir**: -This is an optional parameter. The default is './run_artifacts' directory. -A sub directory with '{env_name}_{git_commit_id}_{timestamp}' gets created in the artifacts dir. - -3. **compare-local/no-compare-local**: -This is an optional parameter. The default is compare-local. If `compare-local` is set, previous run results from the local `artifacts-dir` folder will be used used. -`experimental` If no-compare-local is set, previous run results from the public S3 bucket will be used. `no-compare-local is currently experimental` - -```yaml -reporting: -- module: passfail - criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - subject: mms-inference-server/sum_num_handles - condition: '>' - threshold: 180 - timeframe: 1s - fail: true - stop: true - diff_percent : 30 + A sample criterion is shown below -``` + ```yaml + reporting: + - module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: mms-inference-server/sum_num_handles + condition: '>' + threshold: 180 + timeframe: 1s + fail: true + stop: true + + ``` -#### 3.3 Metrics that you can use for passfail criteria - **System Metrics** - > disk_used, memory_percent, read_count, write_count, read_bytes, write_byte +2. Specify the pass/fail criterion vis-a-vis a prior run. On completion, the test suite runner script compares the +monitoring metrics with values from a previous run which was executed on same environment. The run results are stored +in either a local folder or a S3 bucket based on the `compare-local` option. Metrics which have 'diff_percent' value +specified in the pass/fail criterion are used for comparison with the previous run. - | Syntax | Examples | - | ------ | -------- | - | system_{metricname} | system_disk_used, system_memory_percent, system_write_count | + A sample criterion is shown below + ```yaml + reporting: + - module: passfail + criteria: + - class: bzt.modules.monitoring.MonitoringCriteria + subject: mms-inference-server/sum_num_handles + condition: '>' + threshold: 180 + timeframe: 1s + fail: true + stop: true + diff_percent : 30 + + ``` + Note that + 1. At least one test suite run on the same environment should have happened in order to do the comparison. + 2. The $artifacts-dir$//comparison_results.html is a summary report which shows performance difference + between the last two commits. + 3. The test case fails if the diff_percent is greater than the specified value across runs. +3. Metrics available for pass-fail criteria + + **System Metrics** + > disk_used, memory_percent, read_count, write_count, read_bytes, write_byte - **Process Metrics** - > cpu_percent, memory_percent, cpu_user_time, cpu_system_time, cpu_iowait_time, memory_rss, memory_vms, io_read_count, io_write_count, io_read_bytes, io_write_bytes, file_descriptors, threads + | Syntax | Examples | + | ------ | -------- | + | system_{metricname} | system_disk_used, system_memory_percent, system_write_count | - - Frontend - It is a single java process + **Process Metrics** + > cpu_percent, memory_percent, cpu_user_time, cpu_system_time, cpu_iowait_time, memory_rss, memory_vms, io_read_count, io_write_count, io_read_bytes, io_write_bytes, file_descriptors, threads - | Syntax | Examples | - | ------ | -------- | - | frontend_{metricname} | frontend_cpu_percent, frontend_memory_percent, frontend_cpu_iowait_time, frontend_memory_rss, frontend_io_write_bytes, frontend_threads | + - Frontend. Represents the Java process hosting the REST APIs - - Workers - These are python processes. MMS can have more than one worker - Metrics for worker(s) are always available with an aggregate - > Aggregates - > sum, avg, min, max + | Syntax | Examples | + | ------ | -------- | + | frontend_{metricname} | frontend_cpu_percent, frontend_memory_percent, frontend_cpu_iowait_time, frontend_memory_rss, frontend_io_write_bytes, frontend_threads | - | Syntax | Examples | - | ------ | -------- | - | {aggregate}\_workers\_{metricname} | total_workers, sum_workers_memory_percent, avg_workers_iowait_time, min_workers_io_write_bytes, max_workers_threads | + - Workers. Represents the python worker processes. Metrics for worker(s) are always available with an aggregate + > Aggregates + > sum, avg, min, max - - All (Frontend + Workers) - We can also aggregate metrics for both frontend and worker processes together + | Syntax | Examples | + | ------ | -------- | + | {aggregate}\_workers\_{metricname} | total_workers, sum_workers_memory_percent, avg_workers_iowait_time, min_workers_io_write_bytes, max_workers_threads | + + - All (Frontend + Workers). Represents aggregate metrics for both frontend and worker processes. - | Syntax | Examples | - | ------ | -------- | - | {aggregate}\_all\_{metricname} | sum_all_memory_percent, avg_all_iowait_time, min_all_io_write_bytes, max_all_threads | + | Syntax | Examples | + | ------ | -------- | + | {aggregate}\_all\_{metricname} | sum_all_memory_percent, avg_all_iowait_time, min_all_io_write_bytes, max_all_threads | - - Miscellaneous - * total_processes - Total number of processes spawned for frontend & workers - * total_workers - Total number of workers spawned - * orphans - Total number of orphan processes + - Miscellaneous + * total_processes - Total number of processes spawned for frontend & workers + * total_workers - Total number of workers spawned + * orphans - Total number of orphan processes -## Test Strategy +## Test Strategy & Cases More details about our testing strategy and test cases can be found [here](TESTS.md) +## FAQ + +Q1. Is it possible to use the performance regression framework to test MMS on Python2.7? + +Yes. Even though, the performance regression framework needs Python 3.7+ (as Taurus requires Python 3.7+), there are two +possible ways to achieve this +* Please create a Python 2.7 virtual env which runs MMS and a Python 3.7 virtual env which runs + the test framework and test cases. +* Alternatively, deploy the standalone monitoring agent on the MMS instance and run the test cases against the remote +server. Note that the standalone monitoring agent works on both Python 2/3. + + + From dfa80a888c6f3e4991989989361bc3b67553f91b Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Thu, 25 Jun 2020 19:10:22 +0530 Subject: [PATCH 224/329] fix hard carriage return Signed-off-by: Dhruva Ray --- tests/performance/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index 6f02a8897..e5c6b8e55 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -143,8 +143,8 @@ Specify the metrics of interest in the services/monitoring section of the yaml. 1. Standalone monitoring server Use this technique if MMS and the tests execute on different machines. Before running the test cases, - please start the [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will - communicate server metric data with the test client over sockets. The monitoring server runs on port 9009 by default. + please start the [metrics_monitoring_server.py](metrics_monitoring_server.py) script. It will communicate server + metric data with the test client over sockets. The monitoring server runs on port 9009 by default. To start the monitoring server, run the following commands on the MMS host: ```bash From 4b5ef339cf18660f8a2551b3cc3ae2cea4885d76 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 25 Jun 2020 19:52:04 +0530 Subject: [PATCH 225/329] trigger build --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4e63cd9f4..19450916e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -198,4 +198,4 @@ workflows: - performance_regression: executor: docker-python-2 requires: - - build + - build \ No newline at end of file From 42215ca38f653c66b999c9d8b82aacb6a39b1710 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 26 Jun 2020 00:09:22 +0530 Subject: [PATCH 226/329] refactoring code --- tests/performance/agents/configuration.py | 3 +- tests/performance/agents/metrics/__init__.py | 9 +- tests/performance/agents/metrics_collector.py | 32 +- ...taurus.py => metrics_monitoring_inproc.py} | 37 +- .../agents/metrics_monitoring_server.py | 40 +- tests/performance/pylintrc | 425 +++++++++++++++ tests/performance/run_performance_suite.py | 496 ++---------------- tests/performance/runs/__init__.py | 0 tests/performance/runs/compare.py | 188 +++++++ tests/performance/runs/junit.py | 26 + tests/performance/runs/monitoring_driver.py | 42 ++ tests/performance/runs/storage.py | 114 ++++ tests/performance/runs/taurus/__init__.py | 49 ++ tests/performance/runs/taurus/reader.py | 55 ++ tests/performance/runs/taurus/x2junit.py | 75 +++ .../api_description/api_description.yaml | 2 +- .../batch_and_single_inference.yaml | 6 +- .../batch_inference/batch_inference.yaml | 6 +- .../environments/xlarge.yaml | 2 +- .../examples_local_criteria.yaml | 8 +- .../examples_local_monitoring.yaml | 6 +- .../tests/health_check/health_check.yaml | 2 +- .../inference_multiple_models.yaml | 2 +- .../inference_multiple_worker.yaml | 2 +- .../inference_single_worker.yaml | 2 +- .../tests/list_models/list_models.yaml | 2 +- .../model_description/model_description.yaml | 2 +- .../register_unregister.yaml | 2 +- .../register_unregister_multiple.yaml | 2 +- .../scale_down_workers.yaml | 2 +- .../scale_up_workers/scale_up_workers.yaml | 2 +- tests/performance/utils/__init__.py | 20 + tests/performance/utils/fs.py | 39 ++ tests/performance/utils/pyshell.py | 46 ++ tests/performance/utils/timer.py | 41 ++ 35 files changed, 1258 insertions(+), 529 deletions(-) rename tests/performance/agents/{metrics_monitoring_taurus.py => metrics_monitoring_inproc.py} (79%) create mode 100644 tests/performance/pylintrc create mode 100644 tests/performance/runs/__init__.py create mode 100644 tests/performance/runs/compare.py create mode 100644 tests/performance/runs/junit.py create mode 100644 tests/performance/runs/monitoring_driver.py create mode 100644 tests/performance/runs/storage.py create mode 100644 tests/performance/runs/taurus/__init__.py create mode 100644 tests/performance/runs/taurus/reader.py create mode 100644 tests/performance/runs/taurus/x2junit.py create mode 100644 tests/performance/utils/__init__.py create mode 100644 tests/performance/utils/fs.py create mode 100644 tests/performance/utils/pyshell.py create mode 100644 tests/performance/utils/timer.py diff --git a/tests/performance/agents/configuration.py b/tests/performance/agents/configuration.py index fc5d75864..a59f70f3d 100644 --- a/tests/performance/agents/configuration.py +++ b/tests/performance/agents/configuration.py @@ -13,7 +13,7 @@ """ Read configuration file """ -# pylint: disable=redefined-builtin +# pylint: disable=redefined-builtin, bare-except import configparser import pathlib @@ -21,6 +21,7 @@ path = pathlib.Path(__file__).parent.absolute() config.read('{}/config.ini'.format(path)) + def get(section, key, default=''): try: return config[section][key] diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index 119a7ca7f..c0e3b5a90 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -86,6 +86,7 @@ class ProcessType(Enum): children = set() + def get_metrics(server_process, child_processes, logger): """ Get Server processes specific metrics """ @@ -95,13 +96,13 @@ def get_metrics(server_process, child_processes, logger): def update_metric(metric_name, proc_type, stats): stats = stats if stats else [0] - stats = list(filter(lambda x: isinstance(x, int) or isinstance(x, float), stats)) + stats = list(filter(lambda x: isinstance(x, (float, int)), stats)) if proc_type == ProcessType.WORKER: proc_name = 'workers' elif proc_type == ProcessType.FRONTEND: proc_name = 'frontend' - result[proc_name+ '_' + metric_name] = stats[0] + result[proc_name + '_' + metric_name] = stats[0] return else: proc_name = 'all' @@ -117,7 +118,7 @@ def update_metric(metric_name, proc_type, stats): for child in children: try: if psutil.pid_exists(child.pid) and WORKER_NAME in child.cmdline()[1]: - processes_stats.append({'type': ProcessType.WORKER, 'stats' : child.as_dict()}) + processes_stats.append({'type': ProcessType.WORKER, 'stats': child.as_dict()}) else: reclaimed_pids.append(child) logger.debug('child {0} no longer available'.format(child.pid)) @@ -143,7 +144,7 @@ def update_metric(metric_name, proc_type, stats): # Total processes result['total_processes'] = len(worker_stats) + 1 result['total_workers'] = max(len(worker_stats) - 1, 0) - result['orphans'] = len(list(filter(lambda p : p['ppid'] == 1, worker_stats))) + result['orphans'] = len(list(filter(lambda p: p['ppid'] == 1, worker_stats))) ### SYSTEM METRICS ### result['system_disk_used'] = psutil.disk_usage('/').used diff --git a/tests/performance/agents/metrics_collector.py b/tests/performance/agents/metrics_collector.py index 23704886d..c900c606c 100755 --- a/tests/performance/agents/metrics_collector.py +++ b/tests/performance/agents/metrics_collector.py @@ -13,7 +13,7 @@ """ Server metrics collector """ -# pylint: disable=redefined-builtin +# pylint: disable=redefined-builtin, broad-except, unused-variable import argparse import logging @@ -21,18 +21,15 @@ import sys import tempfile import time - import gevent import psutil -from tabulate import tabulate - -logger = logging.getLogger(__name__) from utils.process import get_process_pid_from_file, get_child_processes, \ get_server_processes, get_server_pidfile from metrics import AVAILABLE_METRICS, get_metrics import configuration +logger = logging.getLogger(__name__) TMP_DIR = tempfile.gettempdir() METRICS_LOG_FILE = "{}/server_metrics_{}.log".format(TMP_DIR, int(time.time())) METRICS_COLLECTOR_PID_FILE = "{}/.metrics_collector.pid".format(TMP_DIR) @@ -58,18 +55,19 @@ def stop_process(pid_file): try: process = psutil.Process(pid) if process.is_running(): - logger.info("Process with pid {} is running. Killing it.".format(process.pid)) + logger.info("Process with pid %s is running. Killing it.", process.pid) process.kill() except Exception as e: pass else: - logger.info("Dead process with pid {} found in '{}'.".format(process.pid, pid_file)) + logger.info("Dead process with pid %s found in '%s'.", process.pid, pid_file) - logger.info("Removing pid file '{}'.".format(pid_file)) + logger.info("Removing pid file '%s'.", pid_file) os.remove(pid_file) def check_is_running(pid_file): + """check if pid is running""" pid = get_process_pid_from_file(pid_file) if pid: try: @@ -79,7 +77,7 @@ def check_is_running(pid_file): else: if perf_mon_process.is_running(): logger.error("Performance monitoring script already running. " - "Stop it using stop option.") + "Stop it using stop option.") sys.exit() @@ -103,12 +101,12 @@ def monitor_processes(server_process, metrics, interval, socket): collected_metrics = get_metrics(server_process, get_child_processes(server_process), logger) metrics_msg = [] for metric in metrics: - message.append(str(collected_metrics.get(metric, 0))) - if collected_metrics.get(metric) is not None: - metrics_msg.append("{0} : {1}".format(metric, collected_metrics.get(metric, 0))) + message.append(str(collected_metrics.get(metric, 0))) + if collected_metrics.get(metric) is not None: + metrics_msg.append("{0} : {1}".format(metric, collected_metrics.get(metric, 0))) - message = "\t".join(message)+"\t\n" - logger.info("{}".format("{0}".format(" -- ".join(metrics_msg)))) + message = "\t".join(message) + "\t\n" + logger.info("%s", " -- ".join(metrics_msg)) if socket: try: @@ -143,10 +141,10 @@ def start_metric_collector_process(): if __name__ == "__main__": logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) - parser = argparse.ArgumentParser(prog='perf-mon-script', description='System Performance Monitoring') + parser = argparse.ArgumentParser(prog='metric-collector', description='System Performance Metrics collector') sub_parse = parser.add_mutually_exclusive_group(required=True) - sub_parse.add_argument('--start', action='store_true', help='Start the perf-mon-script') - sub_parse.add_argument('--stop', action='store_true', help='Stop the perf-mon-script') + sub_parse.add_argument('--start', action='store_true', help='Start the metric-collector') + sub_parse.add_argument('--stop', action='store_true', help='Stop the metric-collector') args = parser.parse_args() diff --git a/tests/performance/agents/metrics_monitoring_taurus.py b/tests/performance/agents/metrics_monitoring_inproc.py similarity index 79% rename from tests/performance/agents/metrics_monitoring_taurus.py rename to tests/performance/agents/metrics_monitoring_inproc.py index 277b0f5b2..ed5788187 100644 --- a/tests/performance/agents/metrics_monitoring_taurus.py +++ b/tests/performance/agents/metrics_monitoring_inproc.py @@ -15,19 +15,20 @@ Should be used when server and Taurus are running on same machine. This file should be placed in Python Path along with monitoring package. """ -# pylint: disable=redefined-builtin +# pylint: disable=redefined-builtin, unnecessary-comprehension import csv import sys -import configuration from bzt import TaurusConfigError from bzt.modules import monitoring from bzt.utils import dehumanize_time + +import configuration from metrics import get_metrics, AVAILABLE_METRICS as AVAILABLE_SERVER_METRICS from utils.process import get_process_pid_from_file, get_server_processes, \ get_child_processes, get_server_pidfile -from tabulate import tabulate + PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 @@ -35,12 +36,17 @@ class Monitor(monitoring.Monitoring): + """Add ServerLocalClient to Monitoring by patching to monitoring.Monitoring + """ + def __init__(self): super(Monitor, self).__init__() self.client_classes.update({'ServerLocalClient': ServerLocalClient}) class ServerLocalClient(monitoring.LocalClient): + """Custom server local client """ + AVAILABLE_METRICS = monitoring.LocalClient.AVAILABLE_METRICS + \ AVAILABLE_SERVER_METRICS @@ -81,19 +87,20 @@ def connect(self): class ServerLocalMonitor(monitoring.LocalMonitor): + """Custom server local monitor""" - def _calc_resource_stats(self, interval): - result = super()._calc_resource_stats(interval) - server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) - server_process = get_server_processes(server_pid) - result.update(get_metrics(server_process, get_child_processes(server_process), self.log)) + def _calc_resource_stats(self, interval): + result = super()._calc_resource_stats(interval) + server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) + server_process = get_server_processes(server_pid) + result.update(get_metrics(server_process, get_child_processes(server_process), self.log)) - metrics_msg = [] + metrics_msg = [] - updated_result = {} - for key in self.metrics: - if result.get(key) is not None: + updated_result = {} + for key in self.metrics: + if result.get(key) is not None: metrics_msg.append("{0} : {1}".format(key, result[key])) - updated_result[key] = result.get(key) - self.log.info("{0}".format(" -- ".join(metrics_msg))) - return updated_result + updated_result[key] = result.get(key) + self.log.info("{0}".format(" -- ".join(metrics_msg))) + return updated_result diff --git a/tests/performance/agents/metrics_monitoring_server.py b/tests/performance/agents/metrics_monitoring_server.py index 55bbcca02..8a393e1c6 100644 --- a/tests/performance/agents/metrics_monitoring_server.py +++ b/tests/performance/agents/metrics_monitoring_server.py @@ -13,7 +13,7 @@ """ Remote server monitoring script """ -# pylint: disable=redefined-builtin +# pylint: disable=redefined-builtin, wrong-import-position, too-many-nested-blocks, broad-except import argparse import logging @@ -27,12 +27,13 @@ monkey.patch_select() monkey.patch_socket() -logger = logging.getLogger(__name__) from metrics_collector import start_metric_collection, stop_process, store_pid, check_is_running from utils.process import get_process_pid_from_file, \ get_server_processes, get_server_pidfile import configuration + +logger = logging.getLogger(__name__) TMP_DIR = tempfile.gettempdir() METRICS_MON_SERVER_PID_FILE = "{}/.metrics_monitoring_server.pid".format(TMP_DIR) PID_FILE = configuration.get('server', 'pid_file', 'model_server.pid') @@ -40,30 +41,29 @@ HOST = str(configuration.get('monitoring', 'HOST')) PORT = int(configuration.get('monitoring', 'PORT', 9009)) - SOCKET_LIST = [] RECV_BUFFER = 4096 - interval = 1 def perf_server(): + """ start performance moniting server on a socket """ server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((HOST, PORT)) server_socket.listen(10) SOCKET_LIST.append(server_socket) - logger.info("Started metrics monitoring server on port {}".format(PORT)) + logger.info("Started metrics monitoring server on port %s", PORT) while True: - ready_to_read, ready_to_write, in_error = select.select(SOCKET_LIST, [], [], 0) + ready_to_read, _, _ = select.select(SOCKET_LIST, [], [], 0) for sock in ready_to_read: # a new connection request recieved if sock == server_socket: sockfd, addr = server_socket.accept() SOCKET_LIST.append(sockfd) - logger.info("client ({}, {}) connected".format(addr[0], addr[1])) + logger.info("client (%s, %s) connected", addr[0], addr[1]) # a message from a client, not a new connection else: @@ -77,8 +77,8 @@ def perf_server(): close_socket(sock) elif data.startswith('interval'): try: - global interval - interval = int(data.split(":")[1][:-1]) + global interval + interval = int(data.split(":")[1][:-1]) except Exception: send_message(sock, "In-correct interval data") elif data.startswith('metrics'): @@ -95,24 +95,24 @@ def perf_server(): if sock in SOCKET_LIST: SOCKET_LIST.remove(sock) except Exception as e: - logger.warning("Error {}".format(str(e))) + logger.warning("Error %s", str(e)) continue server_socket.close() -def send_message(socket, message): +def send_message(socket_, message): try: - socket.send(message.encode("latin-1")) + socket_.send(message.encode("latin-1")) except Exception as e: - logger.warning("Error while sending the message {}. Closing the socket.".format(str(e))) - close_socket(socket) + logger.warning("Error while sending the message %s. Closing the socket.", str(e)) + close_socket(socket_) -def close_socket(socket): - socket.close() - if socket in SOCKET_LIST: - SOCKET_LIST.remove(socket) +def close_socket(socket_): + socket_.close() + if socket_ in SOCKET_LIST: + SOCKET_LIST.remove(socket_) if __name__ == "__main__": @@ -130,7 +130,3 @@ def close_socket(socket): perf_server() elif args.stop: stop_process(METRICS_MON_SERVER_PID_FILE) - - - - diff --git a/tests/performance/pylintrc b/tests/performance/pylintrc new file mode 100644 index 000000000..f6dcb394b --- /dev/null +++ b/tests/performance/pylintrc @@ -0,0 +1,425 @@ +[MASTER] + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=8 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist=numpy,opencv + +# Allow optimization of some AST trees. This will activate a peephole AST +# optimizer, which will apply various small optimizations. For instance, it can +# be used to obtain the result of joining multiple strings with the addition +# operator. Joining a lot of strings can lead to a maximum recursion error in +# Pylint and this flag can prevent that. It has one side effect, the resulting +# AST will be different than the one from reality. This option is deprecated +# and it will be removed in Pylint 2.0. +optimize-ast=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=indexing-exception,old-raise-syntax + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=design,similarities,no-self-use,attribute-defined-outside-init,locally-disabled,star-args,pointless-except,bad-option-value,global-statement,fixme,suppressed-message,useless-suppression,locally-enabled,no-member,no-name-in-module,import-error,unsubscriptable-object,unbalanced-tuple-unpacking,undefined-variable,protected-access,superfluous-parens,invalid-name,no-else-return,useless-super-delegation,len-as-condition,invalid-unary-operand-type,useless-object-inheritance +# disable=unicode-builtin,delslice-method,using-cmp-argument,setslice-method,dict-view-method,parameter-unpacking,range-builtin-not-iterating,print-statement,file-builtin,old-raise-syntax,basestring-builtin,execfile-builtin,indexing-exception,import-star-module-level,coerce-method,long-builtin,old-ne-operator,old-division,no-absolute-import,raw_input-builtin,old-octal-literal,oct-method,xrange-builtin,hex-method,unpacking-in-except,nonzero-method,raising-string,intern-builtin,reload-builtin,metaclass-assignment,cmp-method,filter-builtin-not-iterating,apply-builtin,map-builtin-not-iterating,next-method-called,unichr-builtin,buffer-builtin,dict-iter-method,input-builtin,coerce-builtin,getslice-method,useless-suppression,standarderror-builtin,zip-builtin-not-iterating,suppressed-message,cmp-builtin,backtick,long-suffix,reduce-builtin,round-builtin + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". This option is deprecated +# and it will be removed in Pylint 2.0. +files-output=no + +# Tells whether to display a full report or only the messages +reports=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=120 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,future.builtins,builtins + + +[BASIC] + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,_,a,b,op,x,y,wd,lr,kv,k,v,s,p,h,c,m,n,X,t,g,f + +# Bad variable names which should always be refused, separated by a comma +bad-names= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=10 + + +[ELIF] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=optparse + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 1e7fafaaf..2edb57cda 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -13,435 +13,57 @@ """ Run Tarus test cases and generate the Junit XML report """ -# pylint: disable=redefined-builtin +# pylint: disable=redefined-builtin, no-value-for-parameter -import glob import logging import os import pathlib -import socket import subprocess -import shutil -from agents import configuration -import csv -import pandas as pd -import boto3 import sys import time -import click -from subprocess import PIPE, STDOUT +import webbrowser -import requests -import yaml -from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure +import click +from junitparser import JUnitXml from tqdm import tqdm +from utils import run_process, Timer, get_sub_dirs +from runs.storage import LocalStorage, S3Storage +from runs.monitoring_driver import Monitoring +from runs.junit import genrate_junit_report +from runs.taurus import get_taurus_options, x2junit, update_metric_log_header +from runs.compare import compare + logger = logging.getLogger(__name__) logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) -metrics_monitoring_server = "agents/metrics_monitoring_server.py" base_file_path = pathlib.Path(__file__).parent.absolute() run_artifacts_path = "{}/run_artifacts/".format(base_file_path) GLOBAL_CONFIG_PATH = "{}/tests/global_config.yaml".format(base_file_path) -S3_BUCKET = configuration.get('suite', 's3_bucket') - - -class Timer(object): - """ - Helper context manager class to capture time diff - """ - def __init__(self, description): - self.description = description - - def __enter__(self): - self.start = int(time.time()) - return self - - def __exit__(self, type, value, traceback): - logger.info(f"{self.description}: {self.diff()}s") - - def diff(self): - return int(time.time()) - self.start - - -class Monitoring(object): - def __init__(self, path, use=True): - self.path = "{}/{}".format(path, "agents/metrics_monitoring_server.py") - self.use = use - - def __enter__(self): - # Commented as - - # START \ STOP of MMS server is handled in individual scenario's prepare \ post-process stages - # - # with open(GLOBAL_CONFIG_PATH) as conf_file: - # global_config = yaml.safe_load(conf_file) - # server_props = global_config["modules"]["jmeter"]["properties"] - # server_ping_url = "{}://{}:{}/ping".format(server_props["protocol"], server_props["hostname"], - # server_props["port"]) - # try: - # requests.get(server_ping_url) - # except requests.exceptions.ConnectionError: - # raise Exception("Server is not running. Pinged url {}. Exiting...".format(server_ping_url)) - - if self.use: - start_monitoring_server = "{} {} --start".format(sys.executable, self.path) - run_process(start_monitoring_server, wait=False) - time.sleep(2) - return self - - def __exit__(self, type, value, traceback): - if self.use: - stop_monitoring_server = "{} {} --stop".format(sys.executable, self.path) - run_process(stop_monitoring_server) - - -class Suite(object): - def __init__(self, name, artifacts_dir, junit_xml, timer, env_name): - self.ts = TestSuite(name) - self.name = name - self.junit_xml = junit_xml - self.timer = timer - self.artifacts_dir = artifacts_dir - self.env_name = env_name - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - xunit_file = "{}/xunit.xml".format(self.artifacts_dir) - tests, failures, skipped, errors = 0, 0, 0, 0 - err_txt = "" - if os.path.exists(xunit_file): - xml = JUnitXml.fromfile(xunit_file) - for i, suite in enumerate(xml): - for case in suite: - name = "scenario_{}: {}".format(i, case.name) - result = case.result - if isinstance(result, Error): - errors += 1 - err_txt = self.err - elif isinstance(result, Failure): - failures += 1 - err_txt = self.err - elif isinstance(result, Skipped): - skipped += 1 - else: - tests += 1 - - tc = TestCase(name) - tc.result = result - self.ts.add_testcase(tc) - else: - tc = TestCase(self.name) - tc.result = Skipped() - self.ts.add_testcase(tc) - - self.ts.hostname = self.env_name - self.ts.timestamp = self.timer.start - self.ts.time = self.timer.diff() - self.ts.tests = tests - self.ts.failures = failures - self.ts.skipped = skipped - self.ts.errors = errors - self.ts.update_statistics() - self.junit_xml.add_testsuite(self.ts) - -class Compare(): - def __init__(self, artifacts_dir, current_run_name, env_name): - self.artifacts_dir = artifacts_dir - self.current_run_name = current_run_name - self.env_name = env_name - self.result = True - - @staticmethod - def get_latest(names, env_id, exclude_run_name): - max_ts = 0 - latest_run = '' - for run_name in names: - run_name_list = run_name.split('__') - if env_id == run_name_list[0] and run_name != exclude_run_name: - if int(run_name_list[2]) > max_ts: - max_ts = int(run_name_list[2]) - latest_run = run_name - - return latest_run - - def get_dir_to_compare(self): - parent_dir = pathlib.Path(self.artifacts_dir).parent - names = [di for di in os.listdir(parent_dir) if os.path.isdir(os.path.join(parent_dir, di))] - print("-----------") - print(names) - print(parent_dir) - print(self.env_name) - print("-----------") - latest_run = self.get_latest(names, self.env_name, self.current_run_name) - return os.path.join(parent_dir, latest_run), latest_run - - def store_results(self): - pass - - def compare(self): - compare_dir, compare_run_name = self.get_dir_to_compare() - if compare_run_name: - self.result = compare_artifacts(self.artifacts_dir, compare_dir, self.artifacts_dir, - self.current_run_name, compare_run_name) - else: - logger.warning("The latest run not found for env {}".format(self.env_name)) - self.store_results() - return self.result - - -class LocalCompare(Compare): - pass - - -class S3Compare(Compare): - def get_dir_to_compare(self): - tgt_path = "{}/comp_data".format(self.artifacts_dir) - s3 = boto3.resource('s3') - bucket = s3.Bucket(S3_BUCKET) - result = bucket.meta.client.list_objects(Bucket=bucket.name, - Delimiter='/') - run_names = [] - for o in result.get('CommonPrefixes'): - run_names.append(o.get('Prefix')[:-1]) - - latest_run = self.get_latest(run_names, self.env_name, self.current_run_name) - if not latest_run: - logger.info("No run found for env_id {}".format(self.env_name)) - return '', '' - - if not os.path.exists(tgt_path): - os.makedirs(tgt_path) - - tgt_path = "{}/{}".format(tgt_path, latest_run) - run_process("aws s3 cp s3://{}/{} {} --recursive".format(bucket.name, latest_run, tgt_path)) - - return tgt_path, latest_run - - def store_results(self): - run_process("aws s3 cp {} s3://{}/{} --recursive".format(self.artifacts_dir, S3_BUCKET, - self.current_run_name)) - - -def run_process(cmd, wait=True): - print("running command : {}".format(cmd)) - - if wait: - os.environ["PYTHONUNBUFFERED"] = "1" - p = subprocess.Popen(cmd, stdout=PIPE, stderr=STDOUT, - shell=True) - lines =[] - while True: - line = p.stdout.readline().decode('utf-8').rstrip() - if not line: break - lines.append(line) - print(line) - - return p.returncode, '\n'.join(lines) - else: - p = subprocess.Popen(cmd, shell=True) - return p.returncode, '' +def open_report(file_path): + if os.path.exists(file_path): + return webbrowser.open_new_tab('file://' + os.path.realpath(file_path)) + return False -def get_sub_dirs(dir, exclude_list=['comp_data'], include_pattern='*'): - dir = dir.strip() - if not os.path.exists(dir): - msg = "The path {} does not exit".format(dir) - logger.error("The path {} does not exit".format(dir)) - raise Exception(msg) - - pattern_list = glob.glob(dir + "/" + include_pattern) - return list([x for x in os.listdir(dir) if os.path.isdir(dir + "/" + x) and x not in exclude_list - and dir + "/" + x in pattern_list]) - - -def get_mon_metrics_list(test_yaml): - metrics = [] - with open(test_yaml) as test_yaml: - test_yaml = yaml.safe_load(test_yaml) - for rep_section in test_yaml.get('services', []): - if rep_section.get('module', None) == 'monitoring' and "server-agent" in rep_section: - for mon_section in rep_section.get('server-agent', []): - if isinstance(mon_section, dict): - metrics.extend(mon_section.get('metrics', [])) - - return metrics - -def get_compare_metric_list(dir, sub_dir): - diff_percents = [] - metrics = [] - test_yaml = "{}/{}/{}".format(dir, sub_dir, "effective.yml") - with open(test_yaml) as test_yaml: - test_yaml = yaml.safe_load(test_yaml) - for rep_section in test_yaml.get('reporting', []): - if rep_section.get('module', None) == 'passfail': - for criterion in rep_section.get('criteria', []): - if isinstance(criterion, dict) and 'monitoring' in criterion.get('class', ''): - subject = criterion["subject"] - metric = subject.rsplit('/', 1) - metric = metric[1] if len(metric) == 2 else metric[0] - diff_percent = criterion.get("diff_percent", None) - - if diff_percent: - metrics.append(metric) - diff_percents.append(diff_percent) - - return metrics, diff_percents - - -def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): - logger.info("Comparing artifacts from {} with {}".format(dir1, dir2)) - sub_dirs_1 = get_sub_dirs(dir1) - sub_dirs_2 = get_sub_dirs(dir2) - - over_all_pass = True - - aggregates = ["mean", "max", "min"] - header = ["run_name1", "run_name2", "test_suite", "metric", "run1", "run2", "percentage_diff", "result"] - rows = [header] - - junit_xml = JUnitXml() - for sub_dir1 in sub_dirs_1: - with Timer("Comparison test suite {} execution time".format(sub_dir1)) as t: - ts = TestSuite(sub_dir1) - errors,failures, skipped, tests = 0, 0, 0, 0 - if sub_dir1 in sub_dirs_2: - metrics_file1 = glob.glob("{}/{}/SAlogs_*".format(dir1, sub_dir1)) - metrics_file2 = glob.glob("{}/{}/SAlogs_*".format(dir2, sub_dir1)) - if not (metrics_file1 and metrics_file2): - metrics_file1 = glob.glob("{}/{}/local_*".format(dir1, sub_dir1)) - metrics_file2 = glob.glob("{}/{}/local_*".format(dir2, sub_dir1)) - if not (metrics_file1 and metrics_file2): - logger.info("Metrics monitoring logs are not captured for {} in either of the runs.".format(sub_dir1)) - rows.append([run_name1, run_name2, sub_dir1, "log_file", sub_dir1, sub_dir1, "metrics are not captured for either of the runs", "pass"]) - tc = TestCase("metrics_file_present") - tc.result = Skipped("Metrics are not captured for either of the runs") - ts.add_testcase(tc) - skipped += 1 - continue - - - metrics_from_file1 = pd.read_csv(metrics_file1[0]) - metrics_from_file2 = pd.read_csv(metrics_file2[0]) - - - metrics, diff_percents = get_compare_metric_list(dir1, sub_dir1) - for col, diff_percent in zip(metrics, diff_percents): - for agg_func in aggregates: - name = "{}_{}".format(agg_func, str(col)) - try: - val1 = getattr(metrics_from_file1[str(col)], agg_func)() - except TypeError: - val1 = "NULL" - - if str(col) in metrics_from_file2: - try: - val2 = getattr(metrics_from_file2[str(col)], agg_func)() - except TypeError: - val2 = "NULL" - else: - val2 = "NA" - - if val1 == val2 and val1 == "NULL": - diff = "NULL" - pass_fail = "pass" - elif val1 == "NULL": - diff = val2 - pass_fail = "fail" - elif val2 == "NULL": - diff = val1 - pass_fail = "fail" - elif val2 == "NA": - diff = val2 - pass_fail = "pass" - else: - try: - if float(val2) != float(val1): - val1 = float(val1) - val2 = float(val2) - diff = (abs(val2 - val1) / ((val2 + val1)/2)) * 100 - pass_fail = "pass" if diff < float(diff_percent) else "fail" - else: # special case of 0 - pass_fail = "pass" - diff = 0 - - except Exception as e: - logger.info("error while calculating the diff for val1={} and val2={}." - "Error is: {}".format(val1, val2, str(e))) - diff = str(e) - pass_fail = "fail" - - if over_all_pass: - over_all_pass = pass_fail == "pass" - - result_row = [run_name1, run_name2, sub_dir1, name, val1, val2, diff, pass_fail] - rows.append(result_row) - tc = TestCase(name) - tc.result = None if pass_fail == 'pass' else Failure(dict(zip(header, result_row)) ) - ts.add_testcase(tc) - if pass_fail == 'fail': - failures += 1 - else: - tests += 1 - - else: - rows.append([run_name1, run_name2, sub_dir1, "log_file", "log file available", "log file not available", "NA", "pass"]) - - tc = TestCase("metrics_file_present") - tc.result = Skipped("Metrics log file not available for {}".format(run_name2)) - ts.add_testcase(tc) - skipped += 1 - - ts.hostname = run_name1 + " and " + run_name1 - ts.timestamp = t.start - ts.time = t.diff() - ts.tests = tests - ts.failures = failures - ts.skipped = skipped - ts.errors = errors - ts.update_statistics() - junit_xml.add_testsuite(ts) - - junit_xml.update_statistics() - junit_xml_path = '{}/comparison_results.xml'.format(out_dir) - junit_html_path = '{}/comparison_results.html'.format(out_dir) - junit_xml.write(junit_xml_path) - run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) - - out_path = "{}/comparison_results.csv".format(out_dir) - logger.info("Writing comparison report to log file {}".format(out_path)) - with open(out_path, 'w') as csvfile: - csv_writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) - csv_writer.writerows(rows) - return over_all_pass - - -def get_options(artifacts_dir, jmeter_path=None): - options=[] - if jmeter_path: - options.append('-o modules.jmeter.path={}'.format(jmeter_path)) - options.append('-o settings.artifacts-dir={}'.format(artifacts_dir)) - options.append('-o modules.console.disable=true') - options.append('-o settings.env.BASEDIR={}'.format(artifacts_dir)) - options_str = ' '.join(options) - - return options_str - @click.command() @click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True)) @click.option('-t', '--test-dir', help='Directory containing tests.', type=click.Path(exists=True), default=None) -@click.option('-p', '--pattern', help='Test case folder name pattern', default="[!e][!x]*") +@click.option('-p', '--pattern', help='Test case folder name glob pattern', default="*") +@click.option('-x', '--exclude-pattern', help='Test case folder name glob pattern to exclude', default=None) @click.option('-j', '--jmeter-path', help='JMeter executable path.') @click.option('--monit/--no-monit', help='Start Monitoring server', default=True) @click.option('-e', '--env-name', help='environment name which defines threshold values used by the test cases. ' - 'This is the name of a file found in the tests environment folder without ' - 'the extension', required=True) + 'This is the name of a file found in the tests environment folder without ' + 'the extension', required=True) @click.option('--compare-local/--no-compare-local', help='Compare with previous run with files stored' ' in artifacts directory', default=True) -def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_name, compare_local): +def run_test_suite(artifacts_dir, test_dir, pattern, exclude_pattern, + jmeter_path, monit, env_name, compare_local): + """Collect test suites, run them and generate reports""" + if test_dir is None: test_dir = "{}/tests".format(base_file_path) @@ -455,66 +77,50 @@ def run_test_suite(artifacts_dir, test_dir, pattern, jmeter_path, monit, env_nam else: artifacts_dir = os.path.abspath(artifacts_dir) artifacts_dir = "{}/{}".format(artifacts_dir, artifacts_folder_name) - logger.info("Artifacts will be stored in directory {}".format(artifacts_dir)) + logger.info("Artifacts will be stored in directory %s", artifacts_dir) with Monitoring(base_file_path, monit): junit_xml = JUnitXml() pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(base_file_path)) - test_dirs = get_sub_dirs(test_dir, exclude_list=[], include_pattern=pattern) - logger.info("Collected tests {}".format(test_dirs)) + test_dirs = get_sub_dirs(test_dir, exclude_list=[], include_pattern=pattern, + exclude_pattern=exclude_pattern) + logger.info("Collected tests %s", test_dirs) for suite_name in tqdm(test_dirs, desc="Test Suites"): with Timer("Test suite {} execution time".format(suite_name)) as t: suite_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) - options_str = get_options(suite_artifacts_dir, jmeter_path) + options_str = get_taurus_options(suite_artifacts_dir, jmeter_path) env_yaml_path = "{}/{}/environments/{}.yaml".format(test_dir, suite_name, env_name) - env_yaml_path = "" if not os.path.exists(env_yaml_path) else env_yaml_path - test_file = "{}/{}/{}.yaml".format(test_dir, suite_name, suite_name) - with Suite(suite_name, suite_artifacts_dir, junit_xml, t, env_name) as s: + env_yaml_path = "" if not os.path.exists(env_yaml_path) else env_yaml_path + test_file = "{0}/{1}/{1}.yaml".format(test_dir, suite_name) + with x2junit.X2Junit(suite_name, suite_artifacts_dir, junit_xml, t, env_name) as s: s.code, s.err = run_process("{} bzt {} {} {} {}".format(pre_command, options_str, - test_file, env_yaml_path, GLOBAL_CONFIG_PATH)) + test_file, env_yaml_path, + GLOBAL_CONFIG_PATH)) + + update_metric_log_header(suite_artifacts_dir, test_file) - metrics_log_file = glob.glob("{}/SAlogs_*".format(suite_artifacts_dir)) - if metrics_log_file: - metrics = get_mon_metrics_list(test_file) - if metrics: - with open(metrics_log_file[0]) as from_file: - line = from_file.readline() - with open(metrics_log_file[0], mode="w") as to_file: - to_file.write(','.join(line.split(',')[0:1] + metrics)+"\n") - shutil.copyfileobj(from_file, to_file) + genrate_junit_report(junit_xml, artifacts_dir, 'performance_results') - junit_xml.update_statistics() - junit_xml_path = '{}/performance_results.xml'.format(artifacts_dir) - junit_html_path = '{}/performance_results.html'.format(artifacts_dir) - junit_xml.write(junit_xml_path) - run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) + storage_class = LocalStorage if compare_local else S3Storage + compare_result = compare(storage_class(artifacts_dir, artifacts_folder_name, env_name)) + + compare_status, exit_code = ('failed', 4) if not compare_result else ('passed', 0) + suite_status, exit_code = ('failed', 3) if junit_xml.errors or junit_xml.failures \ + or junit_xml.skipped else ('passed', 0) - compare_class = LocalCompare if compare_local else S3Compare - compare_obj = compare_class(artifacts_dir, artifacts_folder_name, env_name) - compare_result = compare_obj.compare() - logger.info("\n\nResult Summary:") - exit_code = 0 - if not compare_result: - compare_status = 'failed' - exit_code = 4 - else: - compare_status = 'passed' - logger.info("Comparison with monitoring metrics of previous run has {}.".format(compare_status)) - logger.info("Comparison test suite report - {}/comparison_results.html".format(artifacts_dir)) + comparison_result_html = "{}/comparison_results.html".format(artifacts_dir) + suite_result_html = "{}/performance_results.html".format(artifacts_dir) + if os.path.exists(comparison_result_html): + logger.info("Comparison with monitoring metrics of previous run has %s.", compare_status) + logger.info("Comparison test suite report - %s", comparison_result_html) - if junit_xml.errors or junit_xml.failures or junit_xml.skipped: - suite_status = 'failed' - exit_code = 3 - else: - suite_status = 'passed' - logger.info("Test suite run has {}.".format(suite_status)) - logger.info("Test suite report - {}/performance_results.html".format(artifacts_dir)) - + logger.info("Performance Regression Test suite run has %s.", suite_status) + logger.info("Performance Regression Test suite report - %s", suite_result_html) + + open_report(suite_result_html) sys.exit(exit_code) if __name__ == "__main__": run_test_suite() - - diff --git a/tests/performance/runs/__init__.py b/tests/performance/runs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/performance/runs/compare.py b/tests/performance/runs/compare.py new file mode 100644 index 000000000..d48182888 --- /dev/null +++ b/tests/performance/runs/compare.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Compare artifacts between runs +""" +# pylint: disable=redefined-builtin, self-assigning-variable, broad-except + + +import glob +import logging +import csv +import sys + +import pandas as pd + +from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure +from utils import Timer, get_sub_dirs +from runs.junit import genrate_junit_report +from runs.taurus import reader as taurus_reader + +logger = logging.getLogger(__name__) +logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + + +def compare(store_obj): + """Driver method to get comparison directory, do the comparison of it with current run directory + and then store results + """ + compare_dir, compare_run_name = store_obj.get_dir_to_compare() + result = True + if compare_run_name: + result = compare_artifacts(store_obj.artifacts_dir, compare_dir, store_obj.artifacts_dir, + store_obj.current_run_name, compare_run_name) + else: + logger.warning("The latest run not found for env.") + + store_obj.store_results() + return result + + +class CompareTestSuite(): + """ + Wrapper helper class over JUnit parser Test Suite + """ + + result_types = {"pass": [lambda x: None, "tests"], + "fail": [Failure, "failures"], + "error": [Error, "errors"], + "skip": [Skipped, "skipped"]} + + def __init__(self, name, hostname, t): + self.ts = TestSuite(name) + self.ts.errors, self.ts.failures, self.ts.skipped, self.ts.tests = 0, 0, 0, 0 + self.ts.hostname = hostname + self.ts.timestamp = t.start + + def add_test_case(self, name, msg, type): + tc = TestCase(name) + result_type = CompareTestSuite.result_types[type] + tc.result = result_type[0](msg) + self.ts.add_testcase(tc) + setattr(self.ts, result_type[1], getattr(self.ts, result_type[1]) + 1) + + +def get_log_files(sub_dir1, dir1, dir2): + """Get metric monitoring log files""" + metrics_file1 = glob.glob("{}/{}/SAlogs_*".format(dir1, sub_dir1)) + metrics_file2 = glob.glob("{}/{}/SAlogs_*".format(dir2, sub_dir1)) + if not (metrics_file1 and metrics_file2): + metrics_file1 = glob.glob("{}/{}/local_*".format(dir1, sub_dir1)) + metrics_file2 = glob.glob("{}/{}/local_*".format(dir2, sub_dir1)) + + return metrics_file1, metrics_file2 + + +def get_aggregate_val(df, agg_func, col): + """Get aggregate values of a pandas datframe coulmn for given aggregate function""" + val = None + if str(col) in df: + try: + val = float(getattr(df[str(col)], agg_func)()) + except TypeError: + val = None + return val + + +def compare_values(val1, val2, diff_percent, run_name1, run_name2): + """ Compare percentage diff values of val1 and val2 """ + if pd.isna(val1) or pd.isna(val2): + msg = "Either of the value can not be determined. The run1 value is '{}' and " \ + "run2 value is {}.".format(val1, val2) + pass_fail, diff, msg = "error", "NA", msg + else: + try: + if val2 != val1: + diff = (abs(val2 - val1) / ((val2 + val1) / 2)) * 100 + + if diff < float(diff_percent): + pass_fail, diff, msg = "pass", diff, "passed" + else: + msg = "The diff_percent criteria has failed. The expected diff_percent is '{}' and actual " \ + "diff percent is '{}' and the '{}' run value is '{}' and '{}' run value is '{}'. ". \ + format(diff_percent, diff, run_name1, val1, run_name2, val2) + + pass_fail, diff, msg = "fail", diff, msg + else: # special case of 0 + pass_fail, diff, msg = "pass", 0, "" + except Exception as e: + msg = "error while calculating the diff for val1={} and val2={}." \ + "Error is: {}".format(val1, val2, str(e)) + logger.info(msg) + pass_fail, diff, msg = "pass", "NA", msg + + return diff, pass_fail, msg + + +def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): + """Compare artifacts from dir1 with di2 and store results in out_dir""" + + logger.info("Comparing artifacts from %s with %s", dir1, dir2) + sub_dirs_1 = get_sub_dirs(dir1) + + over_all_pass = True + aggregates = ["mean", "max", "min"] + header = ["run_name1", "run_name2", "test_suite", "metric", "run1", "run2", "percentage_diff", "expected_diff", + "result", "message"] + rows = [header] + + junit_xml = JUnitXml() + for sub_dir1 in sub_dirs_1: + with Timer("Comparison test suite {} execution time".format(sub_dir1)) as t: + comp_ts = CompareTestSuite(sub_dir1, run_name1 + " and " + run_name1, t) + + metrics_file1, metrics_file2 = get_log_files(sub_dir1, dir1, dir2) + if not (metrics_file1 and metrics_file2): + msg = "Metrics monitoring logs are not captured for {} in either " \ + "of the runs.".format(sub_dir1) + logger.info(msg) + rows.append([run_name1, run_name2, sub_dir1, "metrics_log_file_availability", + "NA", "NA", "NA", "NA", "pass", msg]) + comp_ts.add_test_case("metrics_log_file_availability", msg, "skip") + continue + + metrics_from_file1 = pd.read_csv(metrics_file1[0]) + metrics_from_file2 = pd.read_csv(metrics_file2[0]) + metrics, diff_percents = taurus_reader.get_compare_metric_list(dir1, sub_dir1) + + for col, diff_percent in zip(metrics, diff_percents): + for agg_func in aggregates: + name = "{}_{}".format(agg_func, str(col)) + + val1 = get_aggregate_val(metrics_from_file1, agg_func, col) + val2 = get_aggregate_val(metrics_from_file2, agg_func, col) + + diff, pass_fail, msg = compare_values(val1, val2, diff_percent, run_name1, run_name2) + + if over_all_pass: + over_all_pass = pass_fail == "pass" + + result_row = [run_name1, run_name2, sub_dir1, name, val1, val2, + diff, diff_percent, pass_fail, msg] + rows.append(result_row) + test_name = "{}: diff_percent < {}".format(name, diff_percent) + comp_ts.add_test_case(test_name, msg, pass_fail) + + comp_ts.ts.time = t.diff() + comp_ts.ts.update_statistics() + junit_xml.add_testsuite(comp_ts.ts) + + genrate_junit_report(junit_xml, out_dir, "comparison_results") + + out_path = "{}/comparison_results.csv".format(out_dir) + logger.info("Writing comparison report to log file %s", out_path) + with open(out_path, 'w') as csvfile: + csv_writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) + csv_writer.writerows(rows) + + return over_all_pass diff --git a/tests/performance/runs/junit.py b/tests/performance/runs/junit.py new file mode 100644 index 000000000..ae0a7f79f --- /dev/null +++ b/tests/performance/runs/junit.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Start and stop monitoring server +""" +# pylint: disable=redefined-builtin + +from utils import run_process + + +def genrate_junit_report(junit_xml, out_dir, report_name): + junit_xml.update_statistics() + junit_xml_path = '{}/{}.xml'.format(out_dir, report_name) + junit_html_path = '{}/{}.html'.format(out_dir, report_name) + junit_xml.write(junit_xml_path) + run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) diff --git a/tests/performance/runs/monitoring_driver.py b/tests/performance/runs/monitoring_driver.py new file mode 100644 index 000000000..f7fd6c1c0 --- /dev/null +++ b/tests/performance/runs/monitoring_driver.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Start and stop monitoring server +""" +# pylint: disable=redefined-builtin + +import sys +import time +from utils import run_process + + +class Monitoring(object): + """ + Context Manager class to start and stop the Monitoring Agent metrics_monitoring_server.py + """ + def __init__(self, path, use=True, check_mms_server_status=False): + self.path = "{}/{}".format(path, "agents/metrics_monitoring_server.py") + self.use = use + self.check_mms_server_status = check_mms_server_status + + def __enter__(self): + if self.use: + start_monitoring_server = "{} {} --start".format(sys.executable, self.path) + run_process(start_monitoring_server, wait=False) + time.sleep(2) + return self + + def __exit__(self, type, value, traceback): + if self.use: + stop_monitoring_server = "{} {} --stop".format(sys.executable, self.path) + run_process(stop_monitoring_server) diff --git a/tests/performance/runs/storage.py b/tests/performance/runs/storage.py new file mode 100644 index 000000000..3400099d4 --- /dev/null +++ b/tests/performance/runs/storage.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Result store classes +""" +# pylint: disable=redefined-builtin + + +import logging +import os +import pathlib +import sys + +import boto3 + +from agents import configuration +from utils import run_process + +logger = logging.getLogger(__name__) +logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) +S3_BUCKET = configuration.get('suite', 's3_bucket') + + +class Storage(): + """Class to store and retrieve artifacts""" + + def __init__(self, artifacts_dir, current_run_name, env_name): + self.artifacts_dir = artifacts_dir + self.current_run_name = current_run_name + self.env_name = env_name + + def get_dir_to_compare(self): + """get the artifiacts dir to compare to""" + + def store_results(self): + """Store the results""" + + @staticmethod + def get_latest(names, env_name, exclude_name): + """ + Get latest directory for same env_name name given a list of them. + :param names: list of folder names in the format env_name___commitid__timestamp + :param env_name: filter for env_name + :param exclude_name: any name to exclude + :return: latest directory name + """ + max_ts = 0 + latest_run = '' + for run_name in names: + run_name_list = run_name.split('__') + if env_name == run_name_list[0] and run_name != exclude_name: + if int(run_name_list[2]) > max_ts: + max_ts = int(run_name_list[2]) + latest_run = run_name + + return latest_run + + +class LocalStorage(Storage): + """ + Compare the monitoring metrics for current and previous run for the same env_name + """ + + def get_dir_to_compare(self): + """Get latest run directory name to be compared with""" + parent_dir = pathlib.Path(self.artifacts_dir).parent + names = [di for di in os.listdir(parent_dir) if os.path.isdir(os.path.join(parent_dir, di))] + latest_run = self.get_latest(names, self.env_name, self.current_run_name) + return os.path.join(parent_dir, latest_run), latest_run + + +class S3Storage(Storage): + """Compare current run results with the results stored on S3""" + + def get_dir_to_compare(self): + """Get latest run result artifacts directory for same env_name from S3 bucket + and store it locally for further comparison + """ + tgt_path = "{}/comp_data".format(self.artifacts_dir) + s3 = boto3.resource('s3') + bucket = s3.Bucket(S3_BUCKET) + result = bucket.meta.client.list_objects(Bucket=bucket.name, + Delimiter='/') + run_names = [] + for o in result.get('CommonPrefixes'): + run_names.append(o.get('Prefix')[:-1]) + + latest_run = self.get_latest(run_names, self.env_name, self.current_run_name) + if not latest_run: + logger.info("No run found for env_id %s", self.env_name) + return '', '' + + if not os.path.exists(tgt_path): + os.makedirs(tgt_path) + + tgt_path = "{}/{}".format(tgt_path, latest_run) + run_process("aws s3 cp s3://{}/{} {} --recursive".format(bucket.name, latest_run, tgt_path)) + + return tgt_path, latest_run + + def store_results(self): + """Store the run results back to S3""" + run_process("aws s3 cp {} s3://{}/{} --recursive".format(self.artifacts_dir, S3_BUCKET, + self.current_run_name)) diff --git a/tests/performance/runs/taurus/__init__.py b/tests/performance/runs/taurus/__init__.py new file mode 100644 index 000000000..619556055 --- /dev/null +++ b/tests/performance/runs/taurus/__init__.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Convert the Taurus Test suite XML to Junit XML +""" +# pylint: disable=redefined-builtin + +import glob +import shutil +from .reader import get_mon_metrics_list + + +def get_taurus_options(artifacts_dir, jmeter_path=None): + """The options for Taurus BZT command""" + options = [] + if jmeter_path: + options.append('-o modules.jmeter.path={}'.format(jmeter_path)) + options.append('-o settings.artifacts-dir={}'.format(artifacts_dir)) + options.append('-o modules.console.disable=true') + options.append('-o settings.env.BASEDIR={}'.format(artifacts_dir)) + options_str = ' '.join(options) + + return options_str + + +def update_metric_log_header(suite_artifacts_dir, test_file): + """The order of the columns in header of server metric monitoring SALogs file generated by taurus + is not inline with data. + So as a work around this function rewrites the header based on order defined in the test yaml + """ + metrics_log_file = glob.glob("{}/SAlogs_*".format(suite_artifacts_dir)) + if metrics_log_file: + metrics = get_mon_metrics_list(test_file) + if metrics: + with open(metrics_log_file[0]) as from_file: + line = from_file.readline() + with open(metrics_log_file[0], mode="w") as to_file: + to_file.write(','.join(line.split(',')[0:1] + metrics) + "\n") + shutil.copyfileobj(from_file, to_file) diff --git a/tests/performance/runs/taurus/reader.py b/tests/performance/runs/taurus/reader.py new file mode 100644 index 000000000..4320adc95 --- /dev/null +++ b/tests/performance/runs/taurus/reader.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Run shell command utilities +""" +# pylint: disable=redefined-builtin + +import yaml + + +def get_mon_metrics_list(test_yaml_path): + """Utility method to get list of server-agent metrics which are being monitored from a test yaml file""" + metrics = [] + with open(test_yaml_path) as test_yaml: + test_yaml = yaml.safe_load(test_yaml) + for rep_section in test_yaml.get('services', []): + if rep_section.get('module', None) == 'monitoring' and "server-agent" in rep_section: + for mon_section in rep_section.get('server-agent', []): + if isinstance(mon_section, dict): + metrics.extend(mon_section.get('metrics', [])) + + return metrics + + +def get_compare_metric_list(dir, sub_dir): + """Utility method to get list of compare monitoring metrics identified by diff_percent property""" + diff_percents = [] + metrics = [] + test_yaml = "{}/{}/{}".format(dir, sub_dir, "effective.yml") + with open(test_yaml) as test_yaml: + test_yaml = yaml.safe_load(test_yaml) + for rep_section in test_yaml.get('reporting', []): + if rep_section.get('module', None) == 'passfail': + for criterion in rep_section.get('criteria', []): + if isinstance(criterion, dict) and 'monitoring' in criterion.get('class', ''): + subject = criterion["subject"] + metric = subject.rsplit('/', 1) + metric = metric[1] if len(metric) == 2 else metric[0] + diff_percent = criterion.get("diff_percent", None) + + if diff_percent: + metrics.append(metric) + diff_percents.append(diff_percent) + + return metrics, diff_percents diff --git a/tests/performance/runs/taurus/x2junit.py b/tests/performance/runs/taurus/x2junit.py new file mode 100644 index 000000000..7da4295da --- /dev/null +++ b/tests/performance/runs/taurus/x2junit.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Convert the Taurus Test suite XML to Junit XML +""" +# pylint: disable=redefined-builtin + + +import os +from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure + + +class X2Junit(object): + """ + Context Manager class to do convert Taurus Test suite XML report which is in Xunit specifications + to JUnit XML report. + """ + def __init__(self, name, artifacts_dir, junit_xml, timer, env_name): + self.ts = TestSuite(name) + self.name = name + self.junit_xml = junit_xml + self.timer = timer + self.artifacts_dir = artifacts_dir + self.env_name = env_name + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + xunit_file = "{}/xunit.xml".format(self.artifacts_dir) + tests, failures, skipped, errors = 0, 0, 0, 0 + if os.path.exists(xunit_file): + xml = JUnitXml.fromfile(xunit_file) + for i, suite in enumerate(xml): + for case in suite: + name = "scenario_{}: {}".format(i, case.name) + result = case.result + if isinstance(result, Error): + failures += 1 + result = Failure(result.message, result.type) + elif isinstance(result, Failure): + errors += 1 + result = Error(result.message, result.type) + elif isinstance(result, Skipped): + skipped += 1 + else: + tests += 1 + + tc = TestCase(name) + tc.result = result + self.ts.add_testcase(tc) + else: + tc = TestCase(self.name) + tc.result = Skipped() + self.ts.add_testcase(tc) + + self.ts.hostname = self.env_name + self.ts.timestamp = self.timer.start + self.ts.time = self.timer.diff() + self.ts.tests = tests + self.ts.failures = failures + self.ts.skipped = skipped + self.ts.errors = errors + self.ts.update_statistics() + self.junit_xml.add_testsuite(self.ts) diff --git a/tests/performance/tests/api_description/api_description.yaml b/tests/performance/tests/api_description/api_description.yaml index ee23dbee2..da3316228 100644 --- a/tests/performance/tests/api_description/api_description.yaml +++ b/tests/performance/tests/api_description/api_description.yaml @@ -11,7 +11,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor + class : metrics_monitoring_inproc.Monitor services: - module: shellexec diff --git a/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml b/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml index a1fea699c..73e9ab957 100644 --- a/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml +++ b/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml @@ -11,8 +11,8 @@ scenarios: modules: server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. + # metrics_monitoring_inproc and dependencies should be in python path + class : metrics_monitoring_inproc.Monitor # monitoring class. services: - module: shellexec @@ -30,7 +30,7 @@ services: - "multi-model-server --stop > /dev/null 2>&1" - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + ServerLocalClient: # keyword from metrics_monitoring_inproc.Monitor - interval: 1s logging : True metrics: diff --git a/tests/performance/tests/batch_inference/batch_inference.yaml b/tests/performance/tests/batch_inference/batch_inference.yaml index 80a9b7509..7c4485c06 100644 --- a/tests/performance/tests/batch_inference/batch_inference.yaml +++ b/tests/performance/tests/batch_inference/batch_inference.yaml @@ -11,8 +11,8 @@ scenarios: modules: server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. + # metrics_monitoring_inproc and dependencies should be in python path + class : metrics_monitoring_inproc.Monitor # monitoring class. services: - module: shellexec @@ -28,7 +28,7 @@ services: - "multi-model-server --stop > /dev/null 2>&1" - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + ServerLocalClient: # keyword from metrics_monitoring_inproc.Monitor - interval: 1s logging : True metrics: diff --git a/tests/performance/tests/examples_local_criteria/environments/xlarge.yaml b/tests/performance/tests/examples_local_criteria/environments/xlarge.yaml index 14287339a..6c3835292 100644 --- a/tests/performance/tests/examples_local_criteria/environments/xlarge.yaml +++ b/tests/performance/tests/examples_local_criteria/environments/xlarge.yaml @@ -5,4 +5,4 @@ settings: P90 : 290ms AVG_RT : 1s TOTAL_WORKERS_MEM : 132000000 - PERCENT_DIFF_TOTAL_WORKERS_MEM : 30 + PERCENT_DIFF_TOTAL_WORKERS_MEM : 5 diff --git a/tests/performance/tests/examples_local_criteria/examples_local_criteria.yaml b/tests/performance/tests/examples_local_criteria/examples_local_criteria.yaml index 62e232f9a..9d8b87907 100644 --- a/tests/performance/tests/examples_local_criteria/examples_local_criteria.yaml +++ b/tests/performance/tests/examples_local_criteria/examples_local_criteria.yaml @@ -7,12 +7,12 @@ execution: scenarios: Inference: - script: examples_local_criterion.jmx + script: examples_local_criteria.jmx modules: server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. + # metrics_monitoring_inproc and dependencies should be in python path + class : metrics_monitoring_inproc.Monitor # monitoring class. services: - module: shellexec @@ -24,7 +24,7 @@ services: - "multi-model-server --stop > /dev/null 2>&1" - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + ServerLocalClient: # keyword from metrics_monitoring_inproc.Monitor - interval: 1s logging : True metrics: diff --git a/tests/performance/tests/examples_local_monitoring/examples_local_monitoring.yaml b/tests/performance/tests/examples_local_monitoring/examples_local_monitoring.yaml index 26fa9e81a..d00226470 100644 --- a/tests/performance/tests/examples_local_monitoring/examples_local_monitoring.yaml +++ b/tests/performance/tests/examples_local_monitoring/examples_local_monitoring.yaml @@ -10,8 +10,8 @@ scenarios: modules: server_local_monitoring: - # metrics_monitoring_taurus and dependencies should be in python path - class : metrics_monitoring_taurus.Monitor # monitoring class. + # metrics_monitoring_inproc and dependencies should be in python path + class : metrics_monitoring_inproc.Monitor # monitoring class. services: - module: shellexec @@ -23,7 +23,7 @@ services: - "multi-model-server --stop > /dev/null 2>&1" - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section - ServerLocalClient: # keyword from metrics_monitoring_taurus.Monitor + ServerLocalClient: # keyword from metrics_monitoring_inproc.Monitor - interval: 1s metrics: - cpu diff --git a/tests/performance/tests/health_check/health_check.yaml b/tests/performance/tests/health_check/health_check.yaml index 979af2526..2c8785e3c 100644 --- a/tests/performance/tests/health_check/health_check.yaml +++ b/tests/performance/tests/health_check/health_check.yaml @@ -11,7 +11,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor + class : metrics_monitoring_inproc.Monitor services: - module: shellexec diff --git a/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml b/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml index d76183b8f..3244c4d8f 100644 --- a/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml +++ b/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml @@ -11,7 +11,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor + class : metrics_monitoring_inproc.Monitor services: - module: shellexec diff --git a/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml b/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml index aa1f313c2..5d73624a6 100644 --- a/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml +++ b/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml @@ -12,7 +12,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor + class : metrics_monitoring_inproc.Monitor services: - module: shellexec diff --git a/tests/performance/tests/inference_single_worker/inference_single_worker.yaml b/tests/performance/tests/inference_single_worker/inference_single_worker.yaml index f04bce5e7..ece9e5b74 100644 --- a/tests/performance/tests/inference_single_worker/inference_single_worker.yaml +++ b/tests/performance/tests/inference_single_worker/inference_single_worker.yaml @@ -12,7 +12,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor + class : metrics_monitoring_inproc.Monitor services: - module: shellexec diff --git a/tests/performance/tests/list_models/list_models.yaml b/tests/performance/tests/list_models/list_models.yaml index 1cda84771..81dd8ada7 100644 --- a/tests/performance/tests/list_models/list_models.yaml +++ b/tests/performance/tests/list_models/list_models.yaml @@ -11,7 +11,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor + class : metrics_monitoring_inproc.Monitor services: - module: shellexec diff --git a/tests/performance/tests/model_description/model_description.yaml b/tests/performance/tests/model_description/model_description.yaml index 4d36ffd77..05358a53c 100644 --- a/tests/performance/tests/model_description/model_description.yaml +++ b/tests/performance/tests/model_description/model_description.yaml @@ -11,7 +11,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor + class : metrics_monitoring_inproc.Monitor services: - module: shellexec diff --git a/tests/performance/tests/register_unregister/register_unregister.yaml b/tests/performance/tests/register_unregister/register_unregister.yaml index ca8a616f6..070f50143 100644 --- a/tests/performance/tests/register_unregister/register_unregister.yaml +++ b/tests/performance/tests/register_unregister/register_unregister.yaml @@ -11,7 +11,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor + class : metrics_monitoring_inproc.Monitor services: - module: shellexec diff --git a/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml index 062553357..5c8fcb85e 100644 --- a/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml +++ b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml @@ -11,7 +11,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor + class : metrics_monitoring_inproc.Monitor services: - module: shellexec diff --git a/tests/performance/tests/scale_down_workers/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers/scale_down_workers.yaml index 54787b21b..dc8cc1382 100644 --- a/tests/performance/tests/scale_down_workers/scale_down_workers.yaml +++ b/tests/performance/tests/scale_down_workers/scale_down_workers.yaml @@ -11,7 +11,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor + class : metrics_monitoring_inproc.Monitor services: - module: shellexec diff --git a/tests/performance/tests/scale_up_workers/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers/scale_up_workers.yaml index ee4ea5d89..125aff830 100644 --- a/tests/performance/tests/scale_up_workers/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers/scale_up_workers.yaml @@ -11,7 +11,7 @@ scenarios: modules: server_local_monitoring: - class : metrics_monitoring_taurus.Monitor + class : metrics_monitoring_inproc.Monitor services: - module: shellexec diff --git a/tests/performance/utils/__init__.py b/tests/performance/utils/__init__.py new file mode 100644 index 000000000..861ae8b7f --- /dev/null +++ b/tests/performance/utils/__init__.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Run Tarus test cases and generate the Junit XML report +""" +# pylint: disable=redefined-builtin + +from .fs import get_sub_dirs +from .timer import Timer +from .pyshell import run_process diff --git a/tests/performance/utils/fs.py b/tests/performance/utils/fs.py new file mode 100644 index 000000000..2a2a73178 --- /dev/null +++ b/tests/performance/utils/fs.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +File system utilities +""" +# pylint: disable=redefined-builtin, logging-format-interpolation, dangerous-default-value +import logging +import sys +import os +import glob + +logger = logging.getLogger(__name__) +logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + + +def get_sub_dirs(dir, exclude_list=['comp_data'], include_pattern='*', exclude_pattern=None): + """Utility method to get list of folders in a directory""" + dir = dir.strip() + if not os.path.exists(dir): + msg = "The path {} does not exit".format(dir) + logger.error("The path {} does not exit".format(dir)) + raise Exception(msg) + + pattern_list = glob.glob(dir + "/" + include_pattern) + exclude_pattern_list = glob.glob(dir + "/" + exclude_pattern) if exclude_pattern is not None else [] + return list([x for x in os.listdir(dir) if os.path.isdir(dir + "/" + x) + and x not in exclude_list + and dir + "/" + x in pattern_list + and dir + "/" + x not in exclude_pattern_list]) diff --git a/tests/performance/utils/pyshell.py b/tests/performance/utils/pyshell.py new file mode 100644 index 000000000..108cb9253 --- /dev/null +++ b/tests/performance/utils/pyshell.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Run shell command utilities +""" +# pylint: disable=redefined-builtin, logging-format-interpolation + +import logging +import sys +import os +import subprocess + +logger = logging.getLogger(__name__) +logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + + +def run_process(cmd, wait=True): + """Utility method to run the shell commands""" + logger.info("running command : %s", cmd) + + if wait: + os.environ["PYTHONUNBUFFERED"] = "1" + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + shell=True) + lines = [] + while True: + line = process.stdout.readline().decode('utf-8').rstrip() + if not line: + break + lines.append(line) + logger.info(line) + + return process.returncode, '\n'.join(lines) + else: + process = subprocess.Popen(cmd, shell=True) + return process.returncode, '' diff --git a/tests/performance/utils/timer.py b/tests/performance/utils/timer.py new file mode 100644 index 000000000..1fa47d086 --- /dev/null +++ b/tests/performance/utils/timer.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Timer utilities +""" +# pylint: disable=redefined-builtin + +import logging +import sys +import time + +logger = logging.getLogger(__name__) +logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + + +class Timer(object): + """ + Helper context manager class to capture time diff + """ + def __init__(self, description): + self.description = description + + def __enter__(self): + self.start = int(time.time()) + return self + + def __exit__(self, type, value, traceback): + logger.info("%s: %ss", self.description, self.diff()) + + def diff(self): + return int(time.time()) - self.start From b44beef865060a5d56f0e69056d4a612e603b64f Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 26 Jun 2020 00:13:04 +0530 Subject: [PATCH 227/329] test suite movement --- tests/performance/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/performance/README.md b/tests/performance/README.md index e5c6b8e55..040f73059 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -54,9 +54,12 @@ parameters * `--test-dir` or `-t` is a directory containing the test cases. The default value is `$MMS_HOME/tests/performance/tests`. - * `--pattern` or `-p` picks up certain test cases for execution within the `test-dir`. The default value picks up + * `--pattern` or `-p` glob pattern picks up certain test cases for execution within the `test-dir`. The default value picks up all test cases. + * `--exclude-pattern` or `-x` glob pattern excludes certain test cases for execution within the `test-dir`. +The default value excludes nothing. + * `--env-name` or `-e` specifies the environment name to use while running the test cases. The environment name is the name of the file (minus the extension) found inside the environments folder in each test case. They encapsulate parameter values which are specific to the execution environment. This is a mandatory parameter. From 4fb5975eabd26dfdcd9ffa9a5670ad7f0d31851a Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 26 Jun 2020 12:15:34 +0530 Subject: [PATCH 228/329] use os.path.join and misc refactoring Signed-off-by: Dhruva Ray --- tests/performance/run_performance_suite.py | 100 ++++++++------------ tests/performance/runs/compare.py | 24 ++--- tests/performance/runs/context.py | 86 +++++++++++++++++ tests/performance/runs/junit.py | 8 +- tests/performance/runs/monitoring_driver.py | 42 -------- tests/performance/runs/storage.py | 16 ++-- tests/performance/runs/taurus/__init__.py | 3 +- tests/performance/runs/taurus/reader.py | 4 +- tests/performance/runs/taurus/x2junit.py | 3 +- 9 files changed, 155 insertions(+), 131 deletions(-) create mode 100644 tests/performance/runs/context.py delete mode 100644 tests/performance/runs/monitoring_driver.py diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 2edb57cda..62a41eda6 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -11,77 +11,74 @@ # permissions and limitations under the License. """ -Run Tarus test cases and generate the Junit XML report +Run Performance Regression Test Cases and Generate Reports """ # pylint: disable=redefined-builtin, no-value-for-parameter import logging import os -import pathlib import subprocess import sys import time -import webbrowser import click -from junitparser import JUnitXml +import pathlib +from runs.context import ExecutionEnv +from runs.taurus import get_taurus_options, x2junit, update_taurus_metric_log_header from tqdm import tqdm from utils import run_process, Timer, get_sub_dirs -from runs.storage import LocalStorage, S3Storage -from runs.monitoring_driver import Monitoring -from runs.junit import genrate_junit_report -from runs.taurus import get_taurus_options, x2junit, update_metric_log_header -from runs.compare import compare logger = logging.getLogger(__name__) logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) -base_file_path = pathlib.Path(__file__).parent.absolute() -run_artifacts_path = "{}/run_artifacts/".format(base_file_path) -GLOBAL_CONFIG_PATH = "{}/tests/global_config.yaml".format(base_file_path) +ROOT_PATH = pathlib.Path(__file__).parent.absolute() +RUN_ARTIFACTS_PATH = os.path.join(ROOT_PATH, "run_artifacts") +GLOBAL_CONFIG_PATH = os.path.join(ROOT_PATH, "tests", "global_config.yaml") +MONITORING_AGENT = os.path.join(ROOT_PATH, "agents", "metrics_monitoring_server.py") + +def get_artifacts_dir(ctx, param, value): + commit_id = subprocess.check_output('git rev-parse --short HEAD'.split()).decode("utf-8")[:-1] + run_name = "{}__{}__{}".format(ctx.params['env_name'], commit_id, int(time.time())) + if value is None: + artifacts_dir = os.path.join(RUN_ARTIFACTS_PATH, run_name) + else: + artifacts_dir = os.path.abspath(value) + artifacts_dir = os.path.join(artifacts_dir, run_name) + return artifacts_dir -def open_report(file_path): - if os.path.exists(file_path): - return webbrowser.open_new_tab('file://' + os.path.realpath(file_path)) - return False + +def validate_env(ctx, param, value): + try: + if '__' in value: + raise ValueError + return value + except ValueError: + raise click.BadParameter('Environment name should not have double underscores in it.') @click.command() -@click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True)) -@click.option('-t', '--test-dir', help='Directory containing tests.', type=click.Path(exists=True), default=None) +@click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True), + callback=get_artifacts_dir) +@click.option('-t', '--test-dir', help='Directory containing tests.', type=click.Path(exists=True), + default="{}/tests".format(ROOT_PATH)) @click.option('-p', '--pattern', help='Test case folder name glob pattern', default="*") @click.option('-x', '--exclude-pattern', help='Test case folder name glob pattern to exclude', default=None) @click.option('-j', '--jmeter-path', help='JMeter executable path.') +@click.option('-e', '--env-name', help='Environment filename without the extension. Contains threshold values.', + required=True, callback=validate_env) @click.option('--monit/--no-monit', help='Start Monitoring server', default=True) -@click.option('-e', '--env-name', help='environment name which defines threshold values used by the test cases. ' - 'This is the name of a file found in the tests environment folder without ' - 'the extension', required=True) @click.option('--compare-local/--no-compare-local', help='Compare with previous run with files stored' ' in artifacts directory', default=True) def run_test_suite(artifacts_dir, test_dir, pattern, exclude_pattern, - jmeter_path, monit, env_name, compare_local): + jmeter_path, env_name, monit, compare_local): """Collect test suites, run them and generate reports""" - if test_dir is None: - test_dir = "{}/tests".format(base_file_path) - - if '__' in env_name: - raise Exception("Environment name should not have double underscores in it.") - - commit_id = subprocess.check_output('git rev-parse --short HEAD'.split()).decode("utf-8")[:-1] - artifacts_folder_name = "{}__{}__{}".format(env_name, commit_id, int(time.time())) - if artifacts_dir is None: - artifacts_dir = "{}/{}".format(run_artifacts_path, artifacts_folder_name) - else: - artifacts_dir = os.path.abspath(artifacts_dir) - artifacts_dir = "{}/{}".format(artifacts_dir, artifacts_folder_name) logger.info("Artifacts will be stored in directory %s", artifacts_dir) - with Monitoring(base_file_path, monit): - junit_xml = JUnitXml() - pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(base_file_path)) + with ExecutionEnv(MONITORING_AGENT, artifacts_dir, env_name, compare_local, monit) as prt: + pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(ROOT_PATH)) test_dirs = get_sub_dirs(test_dir, exclude_list=[], include_pattern=pattern, exclude_pattern=exclude_pattern) logger.info("Collected tests %s", test_dirs) @@ -92,35 +89,12 @@ def run_test_suite(artifacts_dir, test_dir, pattern, exclude_pattern, env_yaml_path = "{}/{}/environments/{}.yaml".format(test_dir, suite_name, env_name) env_yaml_path = "" if not os.path.exists(env_yaml_path) else env_yaml_path test_file = "{0}/{1}/{1}.yaml".format(test_dir, suite_name) - with x2junit.X2Junit(suite_name, suite_artifacts_dir, junit_xml, t, env_name) as s: + with x2junit.X2Junit(suite_name, suite_artifacts_dir, prt.reporter, t, env_name) as s: s.code, s.err = run_process("{} bzt {} {} {} {}".format(pre_command, options_str, test_file, env_yaml_path, GLOBAL_CONFIG_PATH)) - update_metric_log_header(suite_artifacts_dir, test_file) - - genrate_junit_report(junit_xml, artifacts_dir, 'performance_results') - - storage_class = LocalStorage if compare_local else S3Storage - compare_result = compare(storage_class(artifacts_dir, artifacts_folder_name, env_name)) - - compare_status, exit_code = ('failed', 4) if not compare_result else ('passed', 0) - suite_status, exit_code = ('failed', 3) if junit_xml.errors or junit_xml.failures \ - or junit_xml.skipped else ('passed', 0) - - logger.info("\n\nResult Summary:") - comparison_result_html = "{}/comparison_results.html".format(artifacts_dir) - suite_result_html = "{}/performance_results.html".format(artifacts_dir) - if os.path.exists(comparison_result_html): - logger.info("Comparison with monitoring metrics of previous run has %s.", compare_status) - logger.info("Comparison test suite report - %s", comparison_result_html) - - logger.info("Performance Regression Test suite run has %s.", suite_status) - logger.info("Performance Regression Test suite report - %s", suite_result_html) - - open_report(suite_result_html) - sys.exit(exit_code) - + update_taurus_metric_log_header(suite_artifacts_dir, test_file) if __name__ == "__main__": run_test_suite() diff --git a/tests/performance/runs/compare.py b/tests/performance/runs/compare.py index d48182888..f36e09417 100644 --- a/tests/performance/runs/compare.py +++ b/tests/performance/runs/compare.py @@ -16,35 +16,35 @@ # pylint: disable=redefined-builtin, self-assigning-variable, broad-except +import csv import glob import logging -import csv import sys import pandas as pd - from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure -from utils import Timer, get_sub_dirs -from runs.junit import genrate_junit_report +from runs.junit import generate_junit_report from runs.taurus import reader as taurus_reader +from utils import Timer, get_sub_dirs + logger = logging.getLogger(__name__) logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) -def compare(store_obj): +def compare(store): """Driver method to get comparison directory, do the comparison of it with current run directory and then store results """ - compare_dir, compare_run_name = store_obj.get_dir_to_compare() + compare_dir, compare_run_name = store.get_dir_to_compare() result = True if compare_run_name: - result = compare_artifacts(store_obj.artifacts_dir, compare_dir, store_obj.artifacts_dir, - store_obj.current_run_name, compare_run_name) + result = compare_artifacts(store.artifacts_dir, compare_dir, store.artifacts_dir, + store.current_run_name, compare_run_name) else: logger.warning("The latest run not found for env.") - store_obj.store_results() + store.store_results() return result @@ -136,7 +136,7 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): "result", "message"] rows = [header] - junit_xml = JUnitXml() + reporter = JUnitXml() for sub_dir1 in sub_dirs_1: with Timer("Comparison test suite {} execution time".format(sub_dir1)) as t: comp_ts = CompareTestSuite(sub_dir1, run_name1 + " and " + run_name1, t) @@ -175,9 +175,9 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): comp_ts.ts.time = t.diff() comp_ts.ts.update_statistics() - junit_xml.add_testsuite(comp_ts.ts) + reporter.add_testsuite(comp_ts.ts) - genrate_junit_report(junit_xml, out_dir, "comparison_results") + generate_junit_report(reporter, out_dir, "comparison_results") out_path = "{}/comparison_results.csv".format(out_dir) logger.info("Writing comparison report to log file %s", out_path) diff --git a/tests/performance/runs/context.py b/tests/performance/runs/context.py new file mode 100644 index 000000000..d72f1d5a2 --- /dev/null +++ b/tests/performance/runs/context.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# http://www.apache.org/licenses/LICENSE-2.0 +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +""" +Start and stop monitoring server +""" +# pylint: disable=redefined-builtin + +import logging +import os +import sys +import time +import webbrowser + +from junitparser import JUnitXml +from runs.compare import compare +from runs.junit import generate_junit_report +from runs.storage import LocalStorage, S3Storage + +from utils import run_process + +logger = logging.getLogger(__name__) +logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + + +class ExecutionEnv(object): + """ + Context Manager class to run the performance regression suites + """ + + def __init__(self, agent, artifacts_dir, env, local_run, use=True, check_mms_server_status=False): + self.monitoring_agent = agent + self.artifacts_dir = artifacts_dir + self.use = use + self.env = env + self.storage = LocalStorage if local_run else S3Storage + self.check_mms_server_status = check_mms_server_status + self.reporter = JUnitXml() + + def __enter__(self): + if self.use: + start_monitoring_server = "{} {} --start".format(sys.executable, self.monitoring_agent) + run_process(start_monitoring_server, wait=False) + time.sleep(2) + return self + + def __exit__(self, type, value, traceback): + if self.use: + stop_monitoring_server = "{} {} --stop".format(sys.executable, self.monitoring_agent) + run_process(stop_monitoring_server) + + def open_report(file_path): + if os.path.exists(file_path): + return webbrowser.open_new_tab('file://' + os.path.realpath(file_path)) + return False + + generate_junit_report(self.reporter, self.artifacts_dir, 'performance_results') + + compare_result = compare(self.storage(self.artifacts_dir, self.env)) + comparison_status, exit_code = ('failed', 4) if not compare_result else ('passed', 0) + performance_status, exit_code = ('failed', 3) if self.reporter.errors or self.reporter.failures \ + or self.reporter.skipped else ('passed', 0) + + logger.info("\n\nResult Summary:") + comparison_result_html = os.path.join(self.artifacts_dir, "comparison_results.html") + performance_result_html = os.path.join(self.artifacts_dir, "performance_results.html") + + if os.path.exists(comparison_result_html): + logger.info("Comparison with monitoring metrics of previous run has %s.", comparison_status) + logger.info("Comparison test suite report - %s", comparison_result_html) + open_report(comparison_result_html) + + logger.info("Performance Regression Test suite run has %s.", performance_status) + logger.info("Performance Regression Test suite report - %s", performance_result_html) + open_report(performance_result_html) + + sys.exit(exit_code) diff --git a/tests/performance/runs/junit.py b/tests/performance/runs/junit.py index ae0a7f79f..9a3fb4107 100644 --- a/tests/performance/runs/junit.py +++ b/tests/performance/runs/junit.py @@ -15,12 +15,14 @@ """ # pylint: disable=redefined-builtin +import os + from utils import run_process -def genrate_junit_report(junit_xml, out_dir, report_name): +def generate_junit_report(junit_xml, out_dir, report_name): junit_xml.update_statistics() - junit_xml_path = '{}/{}.xml'.format(out_dir, report_name) - junit_html_path = '{}/{}.html'.format(out_dir, report_name) + junit_xml_path = os.path.join(out_dir, '{}.xml'.format(report_name)) + junit_html_path = os.path.join(out_dir, '{}.html'.format(report_name)) junit_xml.write(junit_xml_path) run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) diff --git a/tests/performance/runs/monitoring_driver.py b/tests/performance/runs/monitoring_driver.py deleted file mode 100644 index f7fd6c1c0..000000000 --- a/tests/performance/runs/monitoring_driver.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at -# http://www.apache.org/licenses/LICENSE-2.0 -# or in the "license" file accompanying this file. This file is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -# express or implied. See the License for the specific language governing -# permissions and limitations under the License. - -""" -Start and stop monitoring server -""" -# pylint: disable=redefined-builtin - -import sys -import time -from utils import run_process - - -class Monitoring(object): - """ - Context Manager class to start and stop the Monitoring Agent metrics_monitoring_server.py - """ - def __init__(self, path, use=True, check_mms_server_status=False): - self.path = "{}/{}".format(path, "agents/metrics_monitoring_server.py") - self.use = use - self.check_mms_server_status = check_mms_server_status - - def __enter__(self): - if self.use: - start_monitoring_server = "{} {} --start".format(sys.executable, self.path) - run_process(start_monitoring_server, wait=False) - time.sleep(2) - return self - - def __exit__(self, type, value, traceback): - if self.use: - stop_monitoring_server = "{} {} --stop".format(sys.executable, self.path) - run_process(stop_monitoring_server) diff --git a/tests/performance/runs/storage.py b/tests/performance/runs/storage.py index 3400099d4..acf605067 100644 --- a/tests/performance/runs/storage.py +++ b/tests/performance/runs/storage.py @@ -18,12 +18,12 @@ import logging import os -import pathlib import sys import boto3 - +import pathlib from agents import configuration + from utils import run_process logger = logging.getLogger(__name__) @@ -34,13 +34,13 @@ class Storage(): """Class to store and retrieve artifacts""" - def __init__(self, artifacts_dir, current_run_name, env_name): - self.artifacts_dir = artifacts_dir - self.current_run_name = current_run_name + def __init__(self, path, env_name): + self.artifacts_dir = os.path.dirname(path) + self.current_run_name = os.path.basename(path) self.env_name = env_name def get_dir_to_compare(self): - """get the artifiacts dir to compare to""" + """get the artifacts dir to compare to""" def store_results(self): """Store the results""" @@ -86,7 +86,7 @@ def get_dir_to_compare(self): """Get latest run result artifacts directory for same env_name from S3 bucket and store it locally for further comparison """ - tgt_path = "{}/comp_data".format(self.artifacts_dir) + tgt_path = os.path.join(self.artifacts_dir, "comp_data") s3 = boto3.resource('s3') bucket = s3.Bucket(S3_BUCKET) result = bucket.meta.client.list_objects(Bucket=bucket.name, @@ -103,7 +103,7 @@ def get_dir_to_compare(self): if not os.path.exists(tgt_path): os.makedirs(tgt_path) - tgt_path = "{}/{}".format(tgt_path, latest_run) + tgt_path = os.path.join(tgt_path, latest_run) run_process("aws s3 cp s3://{}/{} {} --recursive".format(bucket.name, latest_run, tgt_path)) return tgt_path, latest_run diff --git a/tests/performance/runs/taurus/__init__.py b/tests/performance/runs/taurus/__init__.py index 619556055..db113ac6f 100644 --- a/tests/performance/runs/taurus/__init__.py +++ b/tests/performance/runs/taurus/__init__.py @@ -17,6 +17,7 @@ import glob import shutil + from .reader import get_mon_metrics_list @@ -33,7 +34,7 @@ def get_taurus_options(artifacts_dir, jmeter_path=None): return options_str -def update_metric_log_header(suite_artifacts_dir, test_file): +def update_taurus_metric_log_header(suite_artifacts_dir, test_file): """The order of the columns in header of server metric monitoring SALogs file generated by taurus is not inline with data. So as a work around this function rewrites the header based on order defined in the test yaml diff --git a/tests/performance/runs/taurus/reader.py b/tests/performance/runs/taurus/reader.py index 4320adc95..2222afdbe 100644 --- a/tests/performance/runs/taurus/reader.py +++ b/tests/performance/runs/taurus/reader.py @@ -15,6 +15,8 @@ """ # pylint: disable=redefined-builtin +import os + import yaml @@ -36,7 +38,7 @@ def get_compare_metric_list(dir, sub_dir): """Utility method to get list of compare monitoring metrics identified by diff_percent property""" diff_percents = [] metrics = [] - test_yaml = "{}/{}/{}".format(dir, sub_dir, "effective.yml") + test_yaml = os.path.join(dir, sub_dir, "effective.yml") with open(test_yaml) as test_yaml: test_yaml = yaml.safe_load(test_yaml) for rep_section in test_yaml.get('reporting', []): diff --git a/tests/performance/runs/taurus/x2junit.py b/tests/performance/runs/taurus/x2junit.py index 7da4295da..0219ef76d 100644 --- a/tests/performance/runs/taurus/x2junit.py +++ b/tests/performance/runs/taurus/x2junit.py @@ -17,6 +17,7 @@ import os + from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure @@ -37,7 +38,7 @@ def __enter__(self): return self def __exit__(self, type, value, traceback): - xunit_file = "{}/xunit.xml".format(self.artifacts_dir) + xunit_file = os.path.join(self.artifacts_dir, "xunit.xml") tests, failures, skipped, errors = 0, 0, 0, 0 if os.path.exists(xunit_file): xml = JUnitXml.fromfile(xunit_file) From 4440b9260947b19f79995e921ef41f55cc322cd4 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 26 Jun 2020 12:30:21 +0530 Subject: [PATCH 229/329] pretty print summary --- tests/performance/run_performance_suite.py | 43 ++++++++++++++-------- tests/performance/runs/junit.py | 29 +++++++++++++++ 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 2edb57cda..ac77367b0 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -30,7 +30,7 @@ from utils import run_process, Timer, get_sub_dirs from runs.storage import LocalStorage, S3Storage from runs.monitoring_driver import Monitoring -from runs.junit import genrate_junit_report +from runs.junit import genrate_junit_report, junit2tabulate from runs.taurus import get_taurus_options, x2junit, update_metric_log_header from runs.compare import compare @@ -48,6 +48,30 @@ def open_report(file_path): return False +def report_summary(junit_xml, compare_result, artifacts_dir): + compare_status, exit_code = ('failed', 4) if not compare_result else ('passed', 0) + suite_status, exit_code = ('failed', 3) if junit_xml.errors or junit_xml.failures \ + or junit_xml.skipped else ('passed', 0) + + logger.info("\n\nResult Summary:") + comparison_result_html = "{}/comparison_results.html".format(artifacts_dir) + comparison_result_xml = "{}/comparison_results.xml".format(artifacts_dir) + suite_result_html = "{}/performance_results.html".format(artifacts_dir) + if os.path.exists(comparison_result_html): + logger.info("Comparison with monitoring metrics of previous run has %s.", compare_status) + logger.info("Comparison test suite report - %s", comparison_result_html) + logger.info("Comparison test suite summary:") + print(junit2tabulate(comparison_result_xml)) + + logger.info("Performance Regression Test suite run has %s.", suite_status) + logger.info("Performance Regression Test suite report - %s", suite_result_html) + logger.info("Comparison test suite summary:") + print(junit2tabulate(junit_xml)) + open_report(suite_result_html) + + return exit_code + + @click.command() @click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True)) @click.option('-t', '--test-dir', help='Directory containing tests.', type=click.Path(exists=True), default=None) @@ -101,24 +125,11 @@ def run_test_suite(artifacts_dir, test_dir, pattern, exclude_pattern, genrate_junit_report(junit_xml, artifacts_dir, 'performance_results') + # compare with previous run storage_class = LocalStorage if compare_local else S3Storage compare_result = compare(storage_class(artifacts_dir, artifacts_folder_name, env_name)) - compare_status, exit_code = ('failed', 4) if not compare_result else ('passed', 0) - suite_status, exit_code = ('failed', 3) if junit_xml.errors or junit_xml.failures \ - or junit_xml.skipped else ('passed', 0) - - logger.info("\n\nResult Summary:") - comparison_result_html = "{}/comparison_results.html".format(artifacts_dir) - suite_result_html = "{}/performance_results.html".format(artifacts_dir) - if os.path.exists(comparison_result_html): - logger.info("Comparison with monitoring metrics of previous run has %s.", compare_status) - logger.info("Comparison test suite report - %s", comparison_result_html) - - logger.info("Performance Regression Test suite run has %s.", suite_status) - logger.info("Performance Regression Test suite report - %s", suite_result_html) - - open_report(suite_result_html) + exit_code = report_summary(junit_xml, compare_result, artifacts_dir) sys.exit(exit_code) diff --git a/tests/performance/runs/junit.py b/tests/performance/runs/junit.py index ae0a7f79f..18f3ebd7e 100644 --- a/tests/performance/runs/junit.py +++ b/tests/performance/runs/junit.py @@ -15,12 +15,41 @@ """ # pylint: disable=redefined-builtin +import os +import tabulate from utils import run_process +from junitparser import JUnitXml +header = ["suite_name", "test_case", "result", "message"] def genrate_junit_report(junit_xml, out_dir, report_name): + """This generates xml and hml report""" junit_xml.update_statistics() junit_xml_path = '{}/{}.xml'.format(out_dir, report_name) junit_html_path = '{}/{}.html'.format(out_dir, report_name) junit_xml.write(junit_xml_path) + + #vjunit pip package is used here run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) + + +def junit2array(junit_xml): + rows = [header] + for i, suite in enumerate(junit_xml): + for case in suite: + name = "scenario_{}: {}".format(i, case.name) + result = case.result + rows.append(suite, name, result._tag, result.message) + + return rows + + +def junit2tabulate(junit_xml): + if not isinstance(junit_xml, JUnitXml): + if os.path.exists(junit_xml): + junit_xml = JUnitXml.fromfile(junit_xml) + else: + return tabulate.tabulate([[header]], headers='firstrow') + data = junit2array(junit_xml) + return tabulate.tabulate(data, headers='firstrow') + From eb1d9990617384f1b70fe8fa9272d4dcb928af29 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 14:29:51 +0530 Subject: [PATCH 230/329] Handle exceptions --- tests/performance/agents/metrics/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index c0e3b5a90..7d0c65042 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -15,7 +15,7 @@ from statistics import mean import psutil -from psutil import ZombieProcess +from psutil import NoSuchProcess, ZombieProcess class ProcessType(Enum): @@ -95,8 +95,8 @@ def get_metrics(server_process, child_processes, logger): logger.debug("children : {0}".format(",".join([str(c.pid) for c in children]))) def update_metric(metric_name, proc_type, stats): - stats = stats if stats else [0] stats = list(filter(lambda x: isinstance(x, (float, int)), stats)) + stats = stats if len(stats) else [0] if proc_type == ProcessType.WORKER: proc_name = 'workers' @@ -117,12 +117,13 @@ def update_metric(metric_name, proc_type, stats): processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) for child in children: try: - if psutil.pid_exists(child.pid) and WORKER_NAME in child.cmdline()[1]: + child_cmdline = child.cmdline() + if psutil.pid_exists(child.pid) and len(child_cmdline) >= 2 and WORKER_NAME in child_cmdline[1]: processes_stats.append({'type': ProcessType.WORKER, 'stats': child.as_dict()}) else: reclaimed_pids.append(child) logger.debug('child {0} no longer available'.format(child.pid)) - except ZombieProcess: + except (NoSuchProcess, ZombieProcess): reclaimed_pids.append(child) logger.debug('child {0} no longer available'.format(child.pid)) From 3c256a8635336d045a22ed880019c5fabb47dba6 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 14:42:05 +0530 Subject: [PATCH 231/329] child process handling delegated to metrics module --- tests/performance/agents/utils/process.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/performance/agents/utils/process.py b/tests/performance/agents/utils/process.py index e241e46f6..7ffaf5122 100644 --- a/tests/performance/agents/utils/process.py +++ b/tests/performance/agents/utils/process.py @@ -51,8 +51,7 @@ def get_child_processes(process): """Get all running child processes recursively""" child_processes = set() for p in process.children(recursive=True): - if p.status() == 'running': - child_processes.add(p) + child_processes.add(p) return child_processes From 01b2ec4f28ed55c12e1e835fce508e7e8a993a01 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 14:46:33 +0530 Subject: [PATCH 232/329] for 5h without passfail criteria --- .../register_unregister.yaml | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/tests/performance/tests/register_unregister/register_unregister.yaml b/tests/performance/tests/register_unregister/register_unregister.yaml index 070f50143..28f22050f 100644 --- a/tests/performance/tests/register_unregister/register_unregister.yaml +++ b/tests/performance/tests/register_unregister/register_unregister.yaml @@ -2,7 +2,8 @@ execution: - concurrency: 1 ramp-up: 0s - iterations: 5 + hold-for: 5h +# iterations: 5 scenario: register_unregister scenarios: @@ -31,33 +32,33 @@ services: - sum_all_file_descriptors - frontend_memory_rss -reporting: -- module: passfail - criteria: - # Inbuilt Criteria - - success of RegisterModel<${REG_SUCC}, stop as failed - - success of UnregisterModel<${UNREG_SUCC}, stop as failed - - avg-rt of RegisterModel>${REG_RT}, stop as failed - - avg-rt of UnregisterModel>${UNREG_RT}, stop as failed - # Custom Criteria - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_processes - condition: '>' - threshold: ${TOTAL_PROCS} - timeframe: 5s - stop : true - fail : true - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/sum_all_file_descriptors - condition: '>' - threshold: ${TOTAL_FDS} - timeframe: 5s - stop : true - fail : true +#reporting: +#- module: passfail +# criteria: +# # Inbuilt Criteria +# - success of RegisterModel<${REG_SUCC}, stop as failed +# - success of UnregisterModel<${UNREG_SUCC}, stop as failed +# - avg-rt of RegisterModel>${REG_RT}, stop as failed +# - avg-rt of UnregisterModel>${UNREG_RT}, stop as failed +# # Custom Criteria # - class: bzt.modules.monitoring.MonitoringCriteria -# subject: ServerLocalClient/frontend_memory_rss +# subject: ServerLocalClient/total_processes # condition: '>' -# threshold: ${FRNTEND_MEM} +# threshold: ${TOTAL_PROCS} # timeframe: 5s # stop : true -# fail : true \ No newline at end of file +# fail : true +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/sum_all_file_descriptors +# condition: '>' +# threshold: ${TOTAL_FDS} +# timeframe: 5s +# stop : true +# fail : true +## - class: bzt.modules.monitoring.MonitoringCriteria +## subject: ServerLocalClient/frontend_memory_rss +## condition: '>' +## threshold: ${FRNTEND_MEM} +## timeframe: 5s +## stop : true +## fail : true \ No newline at end of file From 52887aadd7bbdfc7a6865560d2957fd6b73553b4 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 14:47:29 +0530 Subject: [PATCH 233/329] monitor orphans as well --- .../tests/register_unregister/register_unregister.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/performance/tests/register_unregister/register_unregister.yaml b/tests/performance/tests/register_unregister/register_unregister.yaml index 28f22050f..f89efca40 100644 --- a/tests/performance/tests/register_unregister/register_unregister.yaml +++ b/tests/performance/tests/register_unregister/register_unregister.yaml @@ -28,6 +28,7 @@ services: - interval: 1s logging : True metrics: + - orphans - total_processes - sum_all_file_descriptors - frontend_memory_rss From d96338b1187e5d30049202152699179611da41f8 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 15:23:13 +0530 Subject: [PATCH 234/329] smoke and full builds --- .circleci/README.md | 8 ++++---- .circleci/config.yml | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index 952dbd90b..a00131961 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -6,8 +6,8 @@ _config.yml_ contains MMS build logic which will be used by CircleCI. ## Workflows and Jobs Currently, following _workflows_ are available - -1. **build_and_test_python27** -2. **build_and_test_python36** +- **smoke_python36**, **full_python36** +- **smoke_python27**, **full_python27** Following _jobs_ are executed under each workflow 1. **build** - Builds _frontend/model-server.jar_ and executes tests from gradle @@ -47,8 +47,8 @@ Developers can use the following command to build MMS locally: **_./run_circleci_tests.py _** ```bash $ cd multi-model-server -$ ./run_circleci_tests.py build_and_test_python27 python_tests -$ ./run_circleci_tests.py build_and_test_python36 api_tests +$ ./run_circleci_tests.py smoke_python27 python_tests +$ ./run_circleci_tests.py full_python36 api_tests ``` ###### Checklist diff --git a/.circleci/config.yml b/.circleci/config.yml index 19450916e..9bfedd385 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -154,7 +154,23 @@ jobs: workflows: version: 2 - build_and_test_python36: + + smoke_python36: + jobs: + - build: + executor: docker-python-3 + - modelarchiver: + executor: docker-python-3 + - python_tests: + executor: docker-python-3 + requires: + - build + - api_tests: + executor: docker-python-3 + requires: + - build + + full_python36: jobs: - build: executor: docker-python-3 @@ -177,7 +193,23 @@ workflows: requires: - build - build_and_test_python27: + + smoke_python27: + jobs: + - build: + executor: docker-python-2 + - modelarchiver: + executor: docker-python-2 + - python_tests: + executor: docker-python-2 + requires: + - build + - api_tests: + executor: docker-python-2 + requires: + - build + + full_python27: jobs: - build: executor: docker-python-2 From 42ac1dbe6b5408cd3dc9174122af31966cc6be9d Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 17:11:55 +0530 Subject: [PATCH 235/329] matrix to dry config.yml --- .circleci/config.yml | 111 ++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 69 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9bfedd385..db11d8e83 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,13 +2,13 @@ version: 2.1 executors: - docker-python-3: + py36: docker: - image: prashantsail/mms-build:python3.6 environment: _JAVA_OPTIONS: "-Xmx2048m" - docker-python-2: + py27: docker: - image: prashantsail/mms-build:python2.7 environment: @@ -155,79 +155,52 @@ jobs: workflows: version: 2 - smoke_python36: + smoke: jobs: - - build: - executor: docker-python-3 - - modelarchiver: - executor: docker-python-3 - - python_tests: - executor: docker-python-3 + - &build + build: + name: build-<< matrix.version >> + matrix: + parameters: + executor: ["py27", "py36"] + - &modelarchiver + modelarchiver: + name: modelarchiver-<< matrix.version >> + matrix: + parameters: + executor: ["py27", "py36"] + - &python_tests + python_tests: + name: python_tests-<< matrix.version >> requires: - - build - - api_tests: - executor: docker-python-3 + - build-<< matrix.version >> + matrix: + parameters: + executor: ["py27", "py36"] + - &api_tests + api_tests: + name: api_tests-<< matrix.version >> requires: - - build + - build-<< matrix.version >> + matrix: + parameters: + executor: ["py27", "py36"] - full_python36: + nightly: jobs: - - build: - executor: docker-python-3 - - modelarchiver: - executor: docker-python-3 - - python_tests: - executor: docker-python-3 - requires: - - build - - api_tests: - executor: docker-python-3 - requires: - - build - - benchmark: - executor: docker-python-3 - requires: - - build - - performance_regression: - executor: docker-python-3 - requires: - - build - - - smoke_python27: - jobs: - - build: - executor: docker-python-2 - - modelarchiver: - executor: docker-python-2 - - python_tests: - executor: docker-python-2 - requires: - - build - - api_tests: - executor: docker-python-2 - requires: - - build - - full_python27: - jobs: - - build: - executor: docker-python-2 - - modelarchiver: - executor: docker-python-2 - - python_tests: - executor: docker-python-2 - requires: - - build - - api_tests: - executor: docker-python-2 - requires: - - build + - *build + - *modelarchiver + - *python_tests + - *api_tests - benchmark: - executor: docker-python-2 requires: - - build + - build-<< matrix.version >> + matrix: + parameters: + executor: ["py27", "py36"] - performance_regression: - executor: docker-python-2 requires: - - build \ No newline at end of file + - build-<< matrix.version >> + matrix: + parameters: + executor: ["py27", "py36"] From 8dbae22dc7872791d74255797749fa4ed55977d6 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 17:13:36 +0530 Subject: [PATCH 236/329] correct parameter name --- .circleci/config.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index db11d8e83..ffdf04172 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -159,29 +159,29 @@ workflows: jobs: - &build build: - name: build-<< matrix.version >> + name: build-<< matrix.executor >> matrix: parameters: executor: ["py27", "py36"] - &modelarchiver modelarchiver: - name: modelarchiver-<< matrix.version >> + name: modelarchiver-<< matrix.executor >> matrix: parameters: executor: ["py27", "py36"] - &python_tests python_tests: - name: python_tests-<< matrix.version >> + name: python_tests-<< matrix.executor >> requires: - - build-<< matrix.version >> + - build-<< matrix.executor >> matrix: parameters: executor: ["py27", "py36"] - &api_tests api_tests: - name: api_tests-<< matrix.version >> + name: api_tests-<< matrix.executor >> requires: - - build-<< matrix.version >> + - build-<< matrix.executor >> matrix: parameters: executor: ["py27", "py36"] @@ -194,13 +194,13 @@ workflows: - *api_tests - benchmark: requires: - - build-<< matrix.version >> + - build-<< matrix.executor >> matrix: parameters: executor: ["py27", "py36"] - performance_regression: requires: - - build-<< matrix.version >> + - build-<< matrix.executor >> matrix: parameters: executor: ["py27", "py36"] From 05b856f8272beea77e5aeb724be39dec7fe9509f Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 17:23:58 +0530 Subject: [PATCH 237/329] full suite trigger nightly --- .circleci/config.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ffdf04172..479dd96c5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -160,47 +160,47 @@ workflows: - &build build: name: build-<< matrix.executor >> + &matrix matrix: parameters: executor: ["py27", "py36"] - &modelarchiver modelarchiver: name: modelarchiver-<< matrix.executor >> - matrix: - parameters: - executor: ["py27", "py36"] + *matrix - &python_tests python_tests: name: python_tests-<< matrix.executor >> requires: - build-<< matrix.executor >> - matrix: - parameters: - executor: ["py27", "py36"] + *matrix - &api_tests api_tests: name: api_tests-<< matrix.executor >> requires: - build-<< matrix.executor >> - matrix: - parameters: - executor: ["py27", "py36"] + *matrix nightly: + triggers: + - schedule: + cron: "0 0 * * *" + filters: + branches: + only: + - circleci-project jobs: - *build - *modelarchiver - *python_tests - *api_tests - benchmark: + name: benchmark--<< matrix.executor >> requires: - build-<< matrix.executor >> - matrix: - parameters: - executor: ["py27", "py36"] + *matrix - performance_regression: + name: performance_regression--<< matrix.executor >> requires: - build-<< matrix.executor >> - matrix: - parameters: - executor: ["py27", "py36"] + *matrix From 420375ba0e6c212ebd6ff7c71d48aa1290ac7481 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 17:35:07 +0530 Subject: [PATCH 238/329] correct yaml anchor and ref --- .circleci/config.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 479dd96c5..8ec1a14fa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -160,26 +160,25 @@ workflows: - &build build: name: build-<< matrix.executor >> - &matrix - matrix: + matrix: &matrix parameters: executor: ["py27", "py36"] - &modelarchiver modelarchiver: name: modelarchiver-<< matrix.executor >> - *matrix + matrix: *matrix - &python_tests python_tests: name: python_tests-<< matrix.executor >> requires: - build-<< matrix.executor >> - *matrix + matrix: *matrix - &api_tests api_tests: name: api_tests-<< matrix.executor >> requires: - build-<< matrix.executor >> - *matrix + matrix: *matrix nightly: triggers: @@ -198,9 +197,9 @@ workflows: name: benchmark--<< matrix.executor >> requires: - build-<< matrix.executor >> - *matrix + matrix: *matrix - performance_regression: name: performance_regression--<< matrix.executor >> requires: - build-<< matrix.executor >> - *matrix + matrix: *matrix From 115e27d843084c905cecc6bcdc38b5a609181b20 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 17:51:18 +0530 Subject: [PATCH 239/329] Remove performance regression suite --- .circleci/README.md | 6 +++--- .circleci/config.yml | 44 +++++++++++++++----------------------------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index a00131961..3e16db5b7 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -6,8 +6,9 @@ _config.yml_ contains MMS build logic which will be used by CircleCI. ## Workflows and Jobs Currently, following _workflows_ are available - -- **smoke_python36**, **full_python36** -- **smoke_python27**, **full_python27** +1. smoke +2. nightly +3. weekly Following _jobs_ are executed under each workflow 1. **build** - Builds _frontend/model-server.jar_ and executes tests from gradle @@ -15,7 +16,6 @@ Following _jobs_ are executed under each workflow 3. **python_tests** - Executes pytests from _mms/tests/unit_tests/_ 4. **benchmark** - Executes latency benchmark using resnet-18 model 5. (NEW!) **api_tests** - Executes newman test suite for API testing -6. (NEW!) **performance_regression** - Executes performance tests using taurus/jmeter > Checkout _workflows_ and _jobs_ section in _config.yml_ for an up to date list diff --git a/.circleci/config.yml b/.circleci/config.yml index 8ec1a14fa..429cffa00 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -118,24 +118,6 @@ jobs: path: /tmp/MMSBenchmark/out/latency/resnet-18/report/ destination: benchmark-latency-resnet-18 - performance_regression: - parameters: - executor: - type: executor - executor: << parameters.executor >> - steps: - - attach-mms-workspace - - install-mms-server - - run: - name: Start MMS, Execute performance regression testcases, Stop MMS - command: .circleci/scripts/linux_test_perf_regression.sh - - store_artifacts: - name: Store server logs - path: mms.log - - store_artifacts: - name: Store artifacts from performance regression run - path: /tmp/mms-performance-regression/ - modelarchiver: parameters: executor: @@ -173,12 +155,6 @@ workflows: requires: - build-<< matrix.executor >> matrix: *matrix - - &api_tests - api_tests: - name: api_tests-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix nightly: triggers: @@ -192,14 +168,24 @@ workflows: - *build - *modelarchiver - *python_tests - - *api_tests - - benchmark: - name: benchmark--<< matrix.executor >> + - &api_tests + api_tests: + name: api_tests-<< matrix.executor >> requires: - build-<< matrix.executor >> matrix: *matrix - - performance_regression: - name: performance_regression--<< matrix.executor >> + + weekly: + triggers: + - schedule: + cron: "0 0 * * 0" + filters: + branches: + only: + - circleci-project + jobs: + - benchmark: + name: benchmark--<< matrix.executor >> requires: - build-<< matrix.executor >> matrix: *matrix From 9211311329cfb0c13c4a381c17cc4fd856a3e617 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 17:52:48 +0530 Subject: [PATCH 240/329] add build step before benchmark --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 429cffa00..96f58a991 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -184,8 +184,9 @@ workflows: only: - circleci-project jobs: + - *build - benchmark: - name: benchmark--<< matrix.executor >> + name: benchmark-<< matrix.executor >> requires: - build-<< matrix.executor >> matrix: *matrix From 2eb9014d514db5207055dd077f00dd77e5b47f26 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 18:29:41 +0530 Subject: [PATCH 241/329] test for scheduled --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 96f58a991..b16edab36 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -159,7 +159,7 @@ workflows: nightly: triggers: - schedule: - cron: "0 0 * * *" + cron: "05 13 26 06 *" filters: branches: only: From 0189e12d66bfa3f131070e7890bff81f5d0e2a2e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 18:31:47 +0530 Subject: [PATCH 242/329] [skip ci] no need of single worklfow --- run_circleci_tests.py | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/run_circleci_tests.py b/run_circleci_tests.py index 8a5b18a62..8557f3553 100755 --- a/run_circleci_tests.py +++ b/run_circleci_tests.py @@ -21,7 +21,6 @@ job = args.job cci_config_file = '.circleci/config.yml' -sw_cci_config_file = '.circleci/sw_config.yml' processed_file = '.circleci/processed.yml' xformed_file = '.circleci/xformed.yml' processed_config = {} @@ -29,26 +28,8 @@ xformed_job_name = 'mms_xformed_job' blacklisted_steps = ['persist_to_workspace', 'attach_workspace', 'store_artifacts'] -# Generate a new CircleCI config which only has ONLY one workflow (workflow name from cli args) -# WHY ? -# > In case of multiple workflows (py27, py36, etc), CircleCI updates the job names(myjob -> myjob-1) while creating -# > processed.yml. This causes mismatch between 'job name from cli args' and 'job name in processed yaml' -# > As a workaround, we pre-process config to only have 1 workflow. This ensures unchanged job names in process.yml -with open(cci_config_file) as f1stream: - try: - config = yaml.safe_load(f1stream) - single_workflow_config = { - 'version': config['workflows']['version'], - workflow: config['workflows'][workflow] - } - config['workflows'] = single_workflow_config - with open(sw_cci_config_file, 'w+') as f2stream: - yaml.dump(config, f2stream) - except yaml.YAMLError as err: - print(err) - # Create processed YAML using circleci cli's 'config process' commands -subprocess.check_call('circleci config process {} > {}'.format(sw_cci_config_file, processed_file), shell=True) +subprocess.check_call('circleci config process {} > {}'.format(cci_config_file, processed_file), shell=True) # Read the processed config with open(processed_file) as fstream: @@ -111,4 +92,4 @@ def get_jobs_steps(result, job_name): subprocess.check_call('circleci local execute -c {} --job {}'.format(xformed_file, xformed_job_name), shell=True) # Clean up, remove the processed and transformed yml files -subprocess.check_call('rm {} {} {}'.format(sw_cci_config_file, processed_file, xformed_file), shell=True) +subprocess.check_call('rm {} {}'.format(processed_file, xformed_file), shell=True) From ddbf83a1f9b9bd413aa24dc3e6169e92d96f6f88 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 18:39:25 +0530 Subject: [PATCH 243/329] Tweaked TC for orphans --- .../environments/xlarge.yaml | 1 + .../register_unregister.yaml | 65 ++++++++++--------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/tests/performance/tests/register_unregister/environments/xlarge.yaml b/tests/performance/tests/register_unregister/environments/xlarge.yaml index a5d1cee32..0e099afed 100644 --- a/tests/performance/tests/register_unregister/environments/xlarge.yaml +++ b/tests/performance/tests/register_unregister/environments/xlarge.yaml @@ -7,4 +7,5 @@ settings: UNREG_RT: 10ms TOTAL_PROCS : 1 TOTAL_FDS : 66 + TOTAL_ORPHANS : 0 FRNTEND_MEM : 75000000 #75MB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister/register_unregister.yaml b/tests/performance/tests/register_unregister/register_unregister.yaml index f89efca40..1feb22487 100644 --- a/tests/performance/tests/register_unregister/register_unregister.yaml +++ b/tests/performance/tests/register_unregister/register_unregister.yaml @@ -2,8 +2,8 @@ execution: - concurrency: 1 ramp-up: 0s - hold-for: 5h -# iterations: 5 +# hold-for: 5h + iterations: 5 scenario: register_unregister scenarios: @@ -28,38 +28,45 @@ services: - interval: 1s logging : True metrics: - - orphans - total_processes - sum_all_file_descriptors - frontend_memory_rss + - orphans -#reporting: -#- module: passfail -# criteria: -# # Inbuilt Criteria -# - success of RegisterModel<${REG_SUCC}, stop as failed -# - success of UnregisterModel<${UNREG_SUCC}, stop as failed -# - avg-rt of RegisterModel>${REG_RT}, stop as failed -# - avg-rt of UnregisterModel>${UNREG_RT}, stop as failed -# # Custom Criteria -# - class: bzt.modules.monitoring.MonitoringCriteria -# subject: ServerLocalClient/total_processes -# condition: '>' -# threshold: ${TOTAL_PROCS} -# timeframe: 5s -# stop : true -# fail : true +reporting: +- module: passfail + criteria: + # Inbuilt Criteria + - success of RegisterModel<${REG_SUCC}, stop as failed + - success of UnregisterModel<${UNREG_SUCC}, stop as failed + - avg-rt of RegisterModel>${REG_RT}, stop as failed + - avg-rt of UnregisterModel>${UNREG_RT}, stop as failed + # Custom Criteria + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_processes + condition: '>' + threshold: ${TOTAL_PROCS} + timeframe: 5s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_all_file_descriptors + condition: '>' + threshold: ${TOTAL_FDS} + timeframe: 5s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '>' + threshold: ${TOTAL_ORPHANS} + timeframe: 1s + stop : true + fail : true # - class: bzt.modules.monitoring.MonitoringCriteria -# subject: ServerLocalClient/sum_all_file_descriptors +# subject: ServerLocalClient/frontend_memory_rss # condition: '>' -# threshold: ${TOTAL_FDS} +# threshold: ${FRNTEND_MEM} # timeframe: 5s # stop : true -# fail : true -## - class: bzt.modules.monitoring.MonitoringCriteria -## subject: ServerLocalClient/frontend_memory_rss -## condition: '>' -## threshold: ${FRNTEND_MEM} -## timeframe: 5s -## stop : true -## fail : true \ No newline at end of file +# fail : true \ No newline at end of file From c9f7fb0281e77c633b0ce2264e771870c679c1ec Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 26 Jun 2020 19:08:22 +0530 Subject: [PATCH 244/329] path join and coloroma --- tests/performance/agents/configuration.py | 3 +- tests/performance/agents/metrics_collector.py | 4 +- .../agents/metrics_monitoring_server.py | 3 +- tests/performance/agents/utils/process.py | 2 +- tests/performance/run_performance_suite.py | 16 ++-- tests/performance/runs/compare.py | 74 ++++++++++--------- tests/performance/runs/context.py | 59 ++++++++------- tests/performance/runs/junit.py | 29 ++++++-- tests/performance/runs/storage.py | 2 +- 9 files changed, 104 insertions(+), 88 deletions(-) diff --git a/tests/performance/agents/configuration.py b/tests/performance/agents/configuration.py index a59f70f3d..2f4425bae 100644 --- a/tests/performance/agents/configuration.py +++ b/tests/performance/agents/configuration.py @@ -14,12 +14,13 @@ Read configuration file """ # pylint: disable=redefined-builtin, bare-except +import os import configparser import pathlib config = configparser.ConfigParser() path = pathlib.Path(__file__).parent.absolute() -config.read('{}/config.ini'.format(path)) +config.read(os.path.join(path, 'config.ini')) def get(section, key, default=''): diff --git a/tests/performance/agents/metrics_collector.py b/tests/performance/agents/metrics_collector.py index c900c606c..9201341be 100755 --- a/tests/performance/agents/metrics_collector.py +++ b/tests/performance/agents/metrics_collector.py @@ -31,8 +31,8 @@ logger = logging.getLogger(__name__) TMP_DIR = tempfile.gettempdir() -METRICS_LOG_FILE = "{}/server_metrics_{}.log".format(TMP_DIR, int(time.time())) -METRICS_COLLECTOR_PID_FILE = "{}/.metrics_collector.pid".format(TMP_DIR) +METRICS_LOG_FILE = os.path.join(TMP_DIR, "server_metrics_{}.log".format(int(time.time()))) +METRICS_COLLECTOR_PID_FILE = os.path.join(TMP_DIR, "metrics_collector.pid") PID_FILE = configuration.get('server', 'pid_file', 'model_server.pid') MONITOR_INTERVAL = 1 diff --git a/tests/performance/agents/metrics_monitoring_server.py b/tests/performance/agents/metrics_monitoring_server.py index 8a393e1c6..b13aa62c4 100644 --- a/tests/performance/agents/metrics_monitoring_server.py +++ b/tests/performance/agents/metrics_monitoring_server.py @@ -19,6 +19,7 @@ import logging import sys import tempfile +import os from gevent import monkey from gevent import select @@ -35,7 +36,7 @@ logger = logging.getLogger(__name__) TMP_DIR = tempfile.gettempdir() -METRICS_MON_SERVER_PID_FILE = "{}/.metrics_monitoring_server.pid".format(TMP_DIR) +METRICS_MON_SERVER_PID_FILE = os.path.join(TMP_DIR, ".metrics_monitoring_server.pid") PID_FILE = configuration.get('server', 'pid_file', 'model_server.pid') HOST = str(configuration.get('monitoring', 'HOST')) diff --git a/tests/performance/agents/utils/process.py b/tests/performance/agents/utils/process.py index e241e46f6..5356dd293 100644 --- a/tests/performance/agents/utils/process.py +++ b/tests/performance/agents/utils/process.py @@ -69,4 +69,4 @@ def get_server_processes(server_process_pid): def get_server_pidfile(file): - return "{0}/.{1}".format(tempfile.gettempdir(), file) + return os.path.join(tempfile.gettempdir(), ".{}".format(file)) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 62a41eda6..28f94442c 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python + # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # Licensed under the Apache License, Version 2.0 (the "License"). @@ -24,7 +24,7 @@ import click import pathlib from runs.context import ExecutionEnv -from runs.taurus import get_taurus_options, x2junit, update_taurus_metric_log_header +from runs.taurus import get_taurus_options, x2junit, update_taurus_metric_files from tqdm import tqdm from utils import run_process, Timer, get_sub_dirs @@ -62,7 +62,7 @@ def validate_env(ctx, param, value): @click.option('-a', '--artifacts-dir', help='Directory to store artifacts.', type=click.Path(writable=True), callback=get_artifacts_dir) @click.option('-t', '--test-dir', help='Directory containing tests.', type=click.Path(exists=True), - default="{}/tests".format(ROOT_PATH)) + default=os.path.join(ROOT_PATH, "tests")) @click.option('-p', '--pattern', help='Test case folder name glob pattern', default="*") @click.option('-x', '--exclude-pattern', help='Test case folder name glob pattern to exclude', default=None) @click.option('-j', '--jmeter-path', help='JMeter executable path.') @@ -78,23 +78,23 @@ def run_test_suite(artifacts_dir, test_dir, pattern, exclude_pattern, logger.info("Artifacts will be stored in directory %s", artifacts_dir) with ExecutionEnv(MONITORING_AGENT, artifacts_dir, env_name, compare_local, monit) as prt: - pre_command = 'export PYTHONPATH={}/agents:$PYTHONPATH; '.format(str(ROOT_PATH)) + pre_command = 'export PYTHONPATH={}:$PYTHONPATH;'.format(os.path.join(str(ROOT_PATH), "agents")) test_dirs = get_sub_dirs(test_dir, exclude_list=[], include_pattern=pattern, exclude_pattern=exclude_pattern) logger.info("Collected tests %s", test_dirs) for suite_name in tqdm(test_dirs, desc="Test Suites"): with Timer("Test suite {} execution time".format(suite_name)) as t: - suite_artifacts_dir = "{}/{}".format(artifacts_dir, suite_name) + suite_artifacts_dir = os.path.join(artifacts_dir, suite_name) options_str = get_taurus_options(suite_artifacts_dir, jmeter_path) - env_yaml_path = "{}/{}/environments/{}.yaml".format(test_dir, suite_name, env_name) + env_yaml_path = os.path.join(test_dir, suite_name, "environments", "{}.yaml".format(env_name)) env_yaml_path = "" if not os.path.exists(env_yaml_path) else env_yaml_path - test_file = "{0}/{1}/{1}.yaml".format(test_dir, suite_name) + test_file = os.path.join(test_dir, suite_name, "{}.yaml".format(suite_name)) with x2junit.X2Junit(suite_name, suite_artifacts_dir, prt.reporter, t, env_name) as s: s.code, s.err = run_process("{} bzt {} {} {} {}".format(pre_command, options_str, test_file, env_yaml_path, GLOBAL_CONFIG_PATH)) - update_taurus_metric_log_header(suite_artifacts_dir, test_file) + update_taurus_metric_files(suite_artifacts_dir, test_file) if __name__ == "__main__": run_test_suite() diff --git a/tests/performance/runs/compare.py b/tests/performance/runs/compare.py index f36e09417..8ebbb4b67 100644 --- a/tests/performance/runs/compare.py +++ b/tests/performance/runs/compare.py @@ -20,11 +20,12 @@ import glob import logging import sys +import os import pandas as pd from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure -from runs.junit import generate_junit_report from runs.taurus import reader as taurus_reader +from runs.storage import LocalStorage, S3Storage from utils import Timer, get_sub_dirs @@ -32,20 +33,32 @@ logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) -def compare(store): - """Driver method to get comparison directory, do the comparison of it with current run directory - and then store results - """ - compare_dir, compare_run_name = store.get_dir_to_compare() - result = True - if compare_run_name: - result = compare_artifacts(store.artifacts_dir, compare_dir, store.artifacts_dir, - store.current_run_name, compare_run_name) - else: - logger.warning("The latest run not found for env.") +class CompareReportGenerator(): + + def __init__(self, path, env_name, local_run): + self.artifacts_dir = path + self.current_run_name = os.path.basename(path) + self.env_name = env_name + storage_class = LocalStorage if local_run else S3Storage + self.storage = storage_class(self.artifacts_dir, self.env_name) + self.junit_reporter = None + self.pandas_result = None + self.pass_fail = True - store.store_results() - return result + def gen(self): + """Driver method to get comparison directory, do the comparison of it with current run directory + and then store results + """ + compare_dir, compare_run_name = self.storage.get_dir_to_compare() + if compare_run_name: + self.junit_reporter, self.pandas_result = compare_artifacts(self.storage.artifacts_dir, compare_dir, + self.storage.current_run_name, compare_run_name) + self.pandas_result.to_csv(os.path.join(self.artifacts_dir, "comparison_result.csv")) + else: + logger.warning("The latest run not found for env.") + + self.storage.store_results() + return self.junit_reporter class CompareTestSuite(): @@ -72,15 +85,10 @@ def add_test_case(self, name, msg, type): setattr(self.ts, result_type[1], getattr(self.ts, result_type[1]) + 1) -def get_log_files(sub_dir1, dir1, dir2): +def get_log_file(dir, sub_dir): """Get metric monitoring log files""" - metrics_file1 = glob.glob("{}/{}/SAlogs_*".format(dir1, sub_dir1)) - metrics_file2 = glob.glob("{}/{}/SAlogs_*".format(dir2, sub_dir1)) - if not (metrics_file1 and metrics_file2): - metrics_file1 = glob.glob("{}/{}/local_*".format(dir1, sub_dir1)) - metrics_file2 = glob.glob("{}/{}/local_*".format(dir2, sub_dir1)) - - return metrics_file1, metrics_file2 + metrics_file = os.path.join(dir, sub_dir, "metrics.csv") + return metrics_file if os.path.exists(metrics_file) else None def get_aggregate_val(df, agg_func, col): @@ -124,7 +132,7 @@ def compare_values(val1, val2, diff_percent, run_name1, run_name2): return diff, pass_fail, msg -def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): +def compare_artifacts(dir1, dir2, run_name1, run_name2): """Compare artifacts from dir1 with di2 and store results in out_dir""" logger.info("Comparing artifacts from %s with %s", dir1, dir2) @@ -132,8 +140,8 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): over_all_pass = True aggregates = ["mean", "max", "min"] - header = ["run_name1", "run_name2", "test_suite", "metric", "run1", "run2", "percentage_diff", "expected_diff", - "result", "message"] + header = ["run_name1", "run_name2", "test_suite", "metric", "run1", "run2", + "percentage_diff", "expected_diff", "result", "message"] rows = [header] reporter = JUnitXml() @@ -141,7 +149,7 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): with Timer("Comparison test suite {} execution time".format(sub_dir1)) as t: comp_ts = CompareTestSuite(sub_dir1, run_name1 + " and " + run_name1, t) - metrics_file1, metrics_file2 = get_log_files(sub_dir1, dir1, dir2) + metrics_file1, metrics_file2 = get_log_file(dir1, sub_dir1), get_log_file(dir2, sub_dir1) if not (metrics_file1 and metrics_file2): msg = "Metrics monitoring logs are not captured for {} in either " \ "of the runs.".format(sub_dir1) @@ -151,8 +159,8 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): comp_ts.add_test_case("metrics_log_file_availability", msg, "skip") continue - metrics_from_file1 = pd.read_csv(metrics_file1[0]) - metrics_from_file2 = pd.read_csv(metrics_file2[0]) + metrics_from_file1 = pd.read_csv(metrics_file1) + metrics_from_file2 = pd.read_csv(metrics_file2) metrics, diff_percents = taurus_reader.get_compare_metric_list(dir1, sub_dir1) for col, diff_percent in zip(metrics, diff_percents): @@ -177,12 +185,6 @@ def compare_artifacts(dir1, dir2, out_dir, run_name1, run_name2): comp_ts.ts.update_statistics() reporter.add_testsuite(comp_ts.ts) - generate_junit_report(reporter, out_dir, "comparison_results") - - out_path = "{}/comparison_results.csv".format(out_dir) - logger.info("Writing comparison report to log file %s", out_path) - with open(out_path, 'w') as csvfile: - csv_writer = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) - csv_writer.writerows(rows) + dataframe = pd.DataFrame(rows[1:], columns=rows[0]) + return reporter, dataframe - return over_all_pass diff --git a/tests/performance/runs/context.py b/tests/performance/runs/context.py index 5fb262186..0ccb9c22a 100644 --- a/tests/performance/runs/context.py +++ b/tests/performance/runs/context.py @@ -20,11 +20,11 @@ import sys import time import webbrowser +from termcolor import colored from junitparser import JUnitXml -from runs.compare import compare -from runs.junit import generate_junit_report, junit2tabulate -from runs.storage import LocalStorage, S3Storage +from runs.compare import CompareReportGenerator +from runs.junit import JunitConverter, junit2tabulate from utils import run_process @@ -42,9 +42,10 @@ def __init__(self, agent, artifacts_dir, env, local_run, use=True, check_mms_ser self.artifacts_dir = artifacts_dir self.use = use self.env = env - self.storage = LocalStorage if local_run else S3Storage + self.local_run = local_run self.check_mms_server_status = check_mms_server_status self.reporter = JUnitXml() + self.compare_reporter_generator = CompareReportGenerator(self.artifacts_dir, self.env, self.local_run) def __enter__(self): if self.use: @@ -60,36 +61,34 @@ def open_report(file_path): return False @staticmethod - def report_summary(junit_xml, compare_result, artifacts_dir): - compare_status, exit_code = ('failed', 4) if not compare_result else ('passed', 0) - suite_status, exit_code = ('failed', 3) if junit_xml.errors or junit_xml.failures \ - or junit_xml.skipped else ('passed', 0) - - logger.info("\n\nResult Summary:") - comparison_result_html = "{}/comparison_results.html".format(artifacts_dir) - comparison_result_xml = "{}/comparison_results.xml".format(artifacts_dir) - suite_result_html = "{}/performance_results.html".format(artifacts_dir) - if os.path.exists(comparison_result_html): - logger.info("Comparison with monitoring metrics of previous run has %s.", compare_status) - logger.info("Comparison test suite report - %s", comparison_result_html) - logger.info("Comparison test suite summary:") - print(junit2tabulate(comparison_result_xml)) - - logger.info("Performance Regression Test suite run has %s.", suite_status) - logger.info("Performance Regression Test suite report - %s", suite_result_html) - logger.info("Comparison test suite summary:") - print(junit2tabulate(junit_xml)) - ExecutionEnv.open_report(suite_result_html) - - return exit_code + def report_summary(reporter, suite_name): + status = reporter.junit_xml.errors or reporter.junit_xml.failures or reporter.junit_xml.skipped + status, code, color = ("failed", 3, "red") if status else ("passed", 0, "green") + + if reporter and os.path.exists(reporter.junit_html_path): + msg = "{} run has {}.".format(suite_name, status) + logger.info(colored(msg, color, attrs=['reverse', 'blink'])) + logger.info("%s report - %s", suite_name, reporter.junit_html_path) + logger.info("%s summary:", suite_name) + print(junit2tabulate(reporter.junit_xml)) + ExecutionEnv.open_report(reporter.junit_html_path) + + return code def __exit__(self, type, value, traceback): if self.use: stop_monitoring_server = "{} {} --stop".format(sys.executable, self.monitoring_agent) run_process(stop_monitoring_server) - generate_junit_report(self.reporter, self.artifacts_dir, 'performance_results') - compare_result = compare(self.storage(self.artifacts_dir, self.env)) + junit_reporter = JunitConverter(self.reporter, self.artifacts_dir, 'performance_results') + junit_reporter.generate_junit_report() + junit_compare = self.compare_reporter_generator.gen() + junit_compare_reporter = None + if junit_compare: + junit_compare_reporter = JunitConverter(junit_compare, self.artifacts_dir, 'comparison_results') + junit_compare_reporter.generate_junit_report() + + compare_exit_code = ExecutionEnv.report_summary(junit_compare_reporter, "Comparison Test suite") + exit_code = ExecutionEnv.report_summary(junit_reporter, "Performance Regression Test suite") - exit_code = ExecutionEnv.report_summary(self.reporter, compare_result, self.artifacts_dir) - sys.exit(exit_code) + sys.exit(0 if 0 == exit_code == compare_exit_code else 3) diff --git a/tests/performance/runs/junit.py b/tests/performance/runs/junit.py index 5992c8be7..70f63c1d2 100644 --- a/tests/performance/runs/junit.py +++ b/tests/performance/runs/junit.py @@ -16,20 +16,32 @@ # pylint: disable=redefined-builtin import os +import html +import textwrap import tabulate from utils import run_process from junitparser import JUnitXml + header = ["suite_name", "test_case", "result", "message"] -def generate_junit_report(junit_xml, out_dir, report_name): - junit_xml.update_statistics() - junit_xml_path = os.path.join(out_dir, '{}.xml'.format(report_name)) - junit_html_path = os.path.join(out_dir, '{}.html'.format(report_name)) - junit_xml.write(junit_xml_path) +class JunitConverter(): + + def __init__(self, junit_xml, out_dir, report_name): + self.junit_xml = junit_xml + self.junit_xml_path = os.path.join(out_dir, '{}.xml'.format(report_name)) + self.junit_html_path = os.path.join(out_dir, '{}.html'.format(report_name)) + + def generate_junit_report(self): + self.junit_xml.update_statistics() + self.junit_xml.write(self.junit_xml_path) + # vjunit pip package is used here + run_process("vjunit -f {} -o {}".format(self.junit_xml_path, self.junit_html_path)) + - # vjunit pip package is used here - run_process("vjunit -f {} -o {}".format(junit_xml_path, junit_html_path)) +def unescape(data): + """Unsescape the html characters from the data""" + return html.unescape(html.unescape(data)) def junit2array(junit_xml): @@ -39,7 +51,8 @@ def junit2array(junit_xml): for case in suite: result = case.result tag, msg = (result._tag, result.message) if result else ("pass", "") - rows.append([suite.name, case.name, tag, msg]) + msg = textwrap.fill(unescape(msg), width=50) + rows.append([suite.name, unescape(case.name), tag, msg]) return rows diff --git a/tests/performance/runs/storage.py b/tests/performance/runs/storage.py index acf605067..9d015bc1a 100644 --- a/tests/performance/runs/storage.py +++ b/tests/performance/runs/storage.py @@ -35,7 +35,7 @@ class Storage(): """Class to store and retrieve artifacts""" def __init__(self, path, env_name): - self.artifacts_dir = os.path.dirname(path) + self.artifacts_dir = path self.current_run_name = os.path.basename(path) self.env_name = env_name From 34c5a783385ffbedf712a1144a0326478d8775ca Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 26 Jun 2020 19:10:19 +0530 Subject: [PATCH 245/329] os.path join --- tests/performance/runs/taurus/__init__.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/performance/runs/taurus/__init__.py b/tests/performance/runs/taurus/__init__.py index db113ac6f..4a07717ec 100644 --- a/tests/performance/runs/taurus/__init__.py +++ b/tests/performance/runs/taurus/__init__.py @@ -17,6 +17,7 @@ import glob import shutil +import os from .reader import get_mon_metrics_list @@ -34,12 +35,17 @@ def get_taurus_options(artifacts_dir, jmeter_path=None): return options_str -def update_taurus_metric_log_header(suite_artifacts_dir, test_file): - """The order of the columns in header of server metric monitoring SALogs file generated by taurus - is not inline with data. - So as a work around this function rewrites the header based on order defined in the test yaml +def update_taurus_metric_files(suite_artifacts_dir, test_file): """ - metrics_log_file = glob.glob("{}/SAlogs_*".format(suite_artifacts_dir)) + It renames the server and local metric monitoring log files to metrics.csv. + The order of the columns in header of server metric monitoring SALogs file generated by taurus + is not inline with data. So as a work around this function rewrites the header based on order + defined in the test yaml. + """ + metrics_new_file = os.path.join(suite_artifacts_dir, "metrics.csv") + + server_metric_file_pattern = os.path.join(suite_artifacts_dir, "SAlogs_*") + metrics_log_file = glob.glob(server_metric_file_pattern) if metrics_log_file: metrics = get_mon_metrics_list(test_file) if metrics: @@ -48,3 +54,10 @@ def update_taurus_metric_log_header(suite_artifacts_dir, test_file): with open(metrics_log_file[0], mode="w") as to_file: to_file.write(','.join(line.split(',')[0:1] + metrics) + "\n") shutil.copyfileobj(from_file, to_file) + + os.rename(metrics_log_file[0], metrics_new_file) + + else: + metrics_log_file = os.path.join(suite_artifacts_dir, "local_monitoring_logs.csv") + if os.path.exists(metrics_log_file): + os.rename(metrics_log_file, metrics_new_file) From f887dd0af514fec8fa880557cf88a26f61dec1b3 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 26 Jun 2020 19:16:06 +0530 Subject: [PATCH 246/329] termcolor --- tests/performance/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/performance/requirements.txt b/tests/performance/requirements.txt index 3893d6b09..2fa19c26a 100644 --- a/tests/performance/requirements.txt +++ b/tests/performance/requirements.txt @@ -7,4 +7,5 @@ boto3==1.14.3 awscli==1.18.80 click==7.1.2 tabulate==0.8.7 -pandas==1.0.3 \ No newline at end of file +pandas==1.0.3 +termcolor==1.1.0 \ No newline at end of file From b664860bc621ac4df4d16275b1f7aea29d4aed82 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 26 Jun 2020 19:23:44 +0530 Subject: [PATCH 247/329] skipped --- tests/performance/runs/junit.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/performance/runs/junit.py b/tests/performance/runs/junit.py index 70f63c1d2..6e23f8d8d 100644 --- a/tests/performance/runs/junit.py +++ b/tests/performance/runs/junit.py @@ -48,11 +48,14 @@ def junit2array(junit_xml): """convert junit xml junitparser.JUnitXml object to 2d array """ rows = [header] for i, suite in enumerate(junit_xml): - for case in suite: - result = case.result - tag, msg = (result._tag, result.message) if result else ("pass", "") - msg = textwrap.fill(unescape(msg), width=50) - rows.append([suite.name, unescape(case.name), tag, msg]) + if len(suite) == 0: + rows.append([suite.name, "", "skipped", "No metrics to compare"]) + else: + for case in suite: + result = case.result + tag, msg = (result._tag, result.message) if result else ("passed", "") + msg = textwrap.fill(unescape(msg), width=50) + rows.append([suite.name, unescape(case.name), tag, msg]) return rows From 56cdaf67a090e17171bd68f01eaa3c5ae1f708f3 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 19:32:17 +0530 Subject: [PATCH 248/329] replace _ with - --- .circleci/README.md | 8 ++++---- .circleci/config.yml | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index 3e16db5b7..dba649376 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -13,9 +13,9 @@ Currently, following _workflows_ are available - Following _jobs_ are executed under each workflow 1. **build** - Builds _frontend/model-server.jar_ and executes tests from gradle 2. **modelarchiver** - Builds and tests modelarchiver module -3. **python_tests** - Executes pytests from _mms/tests/unit_tests/_ +3. **python-tests** - Executes pytests from _mms/tests/unit_tests/_ 4. **benchmark** - Executes latency benchmark using resnet-18 model -5. (NEW!) **api_tests** - Executes newman test suite for API testing +5. (NEW!) **api-tests** - Executes newman test suite for API testing > Checkout _workflows_ and _jobs_ section in _config.yml_ for an up to date list @@ -47,8 +47,8 @@ Developers can use the following command to build MMS locally: **_./run_circleci_tests.py _** ```bash $ cd multi-model-server -$ ./run_circleci_tests.py smoke_python27 python_tests -$ ./run_circleci_tests.py full_python36 api_tests +$ ./run_circleci_tests.py smoke python-tests +$ ./run_circleci_tests.py nightly api-tests ``` ###### Checklist diff --git a/.circleci/config.yml b/.circleci/config.yml index b16edab36..7eaf18ae5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,7 +70,7 @@ jobs: paths: - . - python_tests: + python-tests: parameters: executor: type: executor @@ -84,7 +84,7 @@ jobs: name: Store python Test results path: htmlcov - api_tests: + api-tests: parameters: executor: type: executor @@ -149,9 +149,9 @@ workflows: modelarchiver: name: modelarchiver-<< matrix.executor >> matrix: *matrix - - &python_tests - python_tests: - name: python_tests-<< matrix.executor >> + - &python-tests + python-tests: + name: python-tests-<< matrix.executor >> requires: - build-<< matrix.executor >> matrix: *matrix @@ -159,18 +159,18 @@ workflows: nightly: triggers: - schedule: - cron: "05 13 26 06 *" + cron: "0 0 * * *" filters: branches: only: - - circleci-project + - master jobs: - *build - *modelarchiver - - *python_tests - - &api_tests - api_tests: - name: api_tests-<< matrix.executor >> + - *python-tests + - &api-tests + api-tests: + name: api-tests-<< matrix.executor >> requires: - build-<< matrix.executor >> matrix: *matrix @@ -182,7 +182,7 @@ workflows: filters: branches: only: - - circleci-project + - master jobs: - *build - benchmark: From 8e5576cb0e44a2d898abfa1cf4997bd975b71aa5 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Fri, 26 Jun 2020 19:59:59 +0530 Subject: [PATCH 249/329] test suite movement --- tests/performance/runs/context.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/performance/runs/context.py b/tests/performance/runs/context.py index 0ccb9c22a..8b3c5d9b6 100644 --- a/tests/performance/runs/context.py +++ b/tests/performance/runs/context.py @@ -62,18 +62,20 @@ def open_report(file_path): @staticmethod def report_summary(reporter, suite_name): - status = reporter.junit_xml.errors or reporter.junit_xml.failures or reporter.junit_xml.skipped - status, code, color = ("failed", 3, "red") if status else ("passed", 0, "green") - if reporter and os.path.exists(reporter.junit_html_path): + status = reporter.junit_xml.errors or reporter.junit_xml.failures or reporter.junit_xml.skipped + status, code, color = ("failed", 3, "red") if status else ("passed", 0, "green") + msg = "{} run has {}.".format(suite_name, status) logger.info(colored(msg, color, attrs=['reverse', 'blink'])) logger.info("%s report - %s", suite_name, reporter.junit_html_path) logger.info("%s summary:", suite_name) print(junit2tabulate(reporter.junit_xml)) ExecutionEnv.open_report(reporter.junit_html_path) + return code - return code + else: + return 0 def __exit__(self, type, value, traceback): if self.use: From 3f87c91f5f300641ea6d79f052dc9160b48e8348 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 20:00:38 +0530 Subject: [PATCH 250/329] [skip ci] updated readme --- .circleci/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index dba649376..5f8fa0a3d 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -11,19 +11,19 @@ Currently, following _workflows_ are available - 3. weekly Following _jobs_ are executed under each workflow -1. **build** - Builds _frontend/model-server.jar_ and executes tests from gradle -2. **modelarchiver** - Builds and tests modelarchiver module -3. **python-tests** - Executes pytests from _mms/tests/unit_tests/_ -4. **benchmark** - Executes latency benchmark using resnet-18 model -5. (NEW!) **api-tests** - Executes newman test suite for API testing +1. **build** : Builds _frontend/model-server.jar_ and executes tests from gradle +2. **modelarchiver** : Builds and tests modelarchiver module +3. **python-tests** : Executes pytests from _mms/tests/unit_tests/_ +4. **benchmark** : Executes latency benchmark using resnet-18 model +5. (NEW!) **api-tests** : Executes newman test suite for API testing -> Checkout _workflows_ and _jobs_ section in _config.yml_ for an up to date list +> Please check the _workflows_ and _jobs_ section in _config.yml_ for an up to date list ## scripts Instead of using inline commands inside _config.yml_, job steps are configured as shell scripts. This is easier for maintenance and reduces chances of error in config.yml -## images (WIP) +## images MMS uses customized docker image for its CircleCI build. To make sure MMS is compatible with both Python2 and Python3, we use two build projects. We have published two docker images on docker hub for code build @@ -38,7 +38,7 @@ Following files in the _images_ folder are used to create the docker images To make it easy for developers to debug build issues locally, MMS supports CircleCI cli for running a job in a container on your machine. #### Dependencies -1. CircleCI cli [Quick Install](https://circleci.com/docs/2.0/local-cli/#quick-installation) +1. CircleCI cli ([Quick Install](https://circleci.com/docs/2.0/local-cli/#quick-installation)) 2. PyYAML (pip install PyYaml) 3. docker (installed and running) @@ -54,8 +54,8 @@ $ ./run_circleci_tests.py nightly api-tests ###### Checklist > 1. Make sure docker is running before you start local execution. > 2. Docker containers to have **at least 4GB RAM, 2 CPU**. -> 3. If you are on a network with low bandwidth availability, explicitly pull the docker images beforehand using - +> 3. If you are on a network with low bandwidth, we advise you to explicitly pull the docker images - > docker pull prashantsail/mms-build:python2.7 > docker pull prashantsail/mms-build:python3.6 -> To avoid Pull Request build failures on github, developers should always make sure that their local builds pass. +`To avoid Pull Request build failures on github, developers should always make sure that their local builds pass.` From b909a0618bec3adf251ddddfe12d847628643168 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 26 Jun 2020 20:03:44 +0530 Subject: [PATCH 251/329] [skip ci] updated readme --- .circleci/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index 5f8fa0a3d..5aa1460eb 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -47,8 +47,8 @@ Developers can use the following command to build MMS locally: **_./run_circleci_tests.py _** ```bash $ cd multi-model-server -$ ./run_circleci_tests.py smoke python-tests -$ ./run_circleci_tests.py nightly api-tests +$ ./run_circleci_tests.py smoke python-tests-py27 +$ ./run_circleci_tests.py nightly api-tests-py36 ``` ###### Checklist From 9dad05a1c4a28a817648f0b727a246fd3b3758b4 Mon Sep 17 00:00:00 2001 From: Dhruva Ray Date: Fri, 26 Jun 2020 20:23:07 +0530 Subject: [PATCH 252/329] show new console output Signed-off-by: Dhruva Ray --- tests/performance/README.md | 11 ++++++++++- tests/performance/assets/console.png | Bin 0 -> 90647 bytes 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/performance/assets/console.png diff --git a/tests/performance/README.md b/tests/performance/README.md index 040f73059..32f3c59f9 100644 --- a/tests/performance/README.md +++ b/tests/performance/README.md @@ -83,16 +83,25 @@ cd $MMS_HOME/tests/performance # check variables such as MMS server PORT etc # vi tests/common/global_config.yaml +#all tests python -m run_performance_suite -e xlarge + +#run a specific test +python -m run_performance_suite -e xlarge -p inference_single_worker + ``` ### C. Understanding the test suite artifacts and reports 1. The $artifacts-dir$//performance_results.html is a summary report of the test run. 2. Each test yaml is treated as a test suite. Each criteria in the test suite is treated as a test case. If the test suite does not specify any criteria, then the test suite is reported as skipped with 0 test cases. -3. For each test suite, a sub-directory is created containing relevant run artifacts. +3. For each test suite, a sub-directory is created containing relevant run artifacts. Important files in this directory are + * metrics.csv -- contains the values of the various system-monitored metrics over time + * finals_stats.csv -- contains the values of the various api metrics over time 4. The $artifacts-dir$//comparison_results.html is a summary report which shows performance difference between the last two commits. +5. The run completes with a console summary of the performance and comparision suites which have failed +![](assets/console.png) ## Add a new test diff --git a/tests/performance/assets/console.png b/tests/performance/assets/console.png new file mode 100644 index 0000000000000000000000000000000000000000..da9c9fb4809c1855dd3813e398a96e0c5bd18e83 GIT binary patch literal 90647 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

  • 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 Date: Fri, 26 Jun 2020 21:21:06 +0530 Subject: [PATCH 253/329] test suite movement --- tests/performance/agents/metrics_collector.py | 2 + .../agents/metrics_monitoring_server.py | 56 ++++++++++--------- tests/performance/run_performance_suite.py | 10 +++- tests/performance/runs/context.py | 2 + tests/performance/runs/junit.py | 15 +++-- 5 files changed, 50 insertions(+), 35 deletions(-) diff --git a/tests/performance/agents/metrics_collector.py b/tests/performance/agents/metrics_collector.py index 9201341be..39a58f4f6 100755 --- a/tests/performance/agents/metrics_collector.py +++ b/tests/performance/agents/metrics_collector.py @@ -30,6 +30,8 @@ import configuration logger = logging.getLogger(__name__) +logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) + TMP_DIR = tempfile.gettempdir() METRICS_LOG_FILE = os.path.join(TMP_DIR, "server_metrics_{}.log".format(int(time.time()))) METRICS_COLLECTOR_PID_FILE = os.path.join(TMP_DIR, "metrics_collector.pid") diff --git a/tests/performance/agents/metrics_monitoring_server.py b/tests/performance/agents/metrics_monitoring_server.py index b13aa62c4..388e5c445 100644 --- a/tests/performance/agents/metrics_monitoring_server.py +++ b/tests/performance/agents/metrics_monitoring_server.py @@ -35,6 +35,7 @@ logger = logging.getLogger(__name__) +logging.basicConfig(stream=sys.stdout, format="%(message)s", level=logging.INFO) TMP_DIR = tempfile.gettempdir() METRICS_MON_SERVER_PID_FILE = os.path.join(TMP_DIR, ".metrics_monitoring_server.pid") PID_FILE = configuration.get('server', 'pid_file', 'model_server.pid') @@ -47,6 +48,34 @@ interval = 1 +def process_data(sock): + """ process data recieved on socket""" + # receiving data from the socket. + data = sock.recv(RECV_BUFFER).decode() + if data: + if data == 'test\n': + send_message(sock, "Yep\n") + elif data == 'exit\n': + close_socket(sock) + elif data.startswith('interval'): + try: + global interval + interval = int(data.split(":")[1][:-1]) + except Exception: + send_message(sock, "In-correct interval data") + elif data.startswith('metrics'): + metrics = data[:-1].split("metrics:")[1].split("\t") + server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) + server_process = get_server_processes(server_pid) + start_metric_collection(server_process, metrics, interval, sock) + else: + # TODO - decide what to do here + pass + else: + # remove the socket that's broken + if sock in SOCKET_LIST: + SOCKET_LIST.remove(sock) + def perf_server(): """ start performance moniting server on a socket """ server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -69,32 +98,7 @@ def perf_server(): # a message from a client, not a new connection else: try: - # receiving data from the socket. - data = sock.recv(RECV_BUFFER).decode() - if data: - if data == 'test\n': - send_message(sock, "Yep\n") - elif data == 'exit\n': - close_socket(sock) - elif data.startswith('interval'): - try: - global interval - interval = int(data.split(":")[1][:-1]) - except Exception: - send_message(sock, "In-correct interval data") - elif data.startswith('metrics'): - metrics = data[:-1].split("metrics:")[1].split("\t") - server_pid = get_process_pid_from_file(get_server_pidfile(PID_FILE)) - server_process = get_server_processes(server_pid) - start_metric_collection(server_process, metrics, interval, sock) - else: - # TODO - decide what to do here - pass - - else: - # remove the socket that's broken - if sock in SOCKET_LIST: - SOCKET_LIST.remove(sock) + process_data(sock) except Exception as e: logger.warning("Error %s", str(e)) continue diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 28f94442c..6ab1b4e3c 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -76,12 +76,16 @@ def run_test_suite(artifacts_dir, test_dir, pattern, exclude_pattern, """Collect test suites, run them and generate reports""" logger.info("Artifacts will be stored in directory %s", artifacts_dir) + test_dirs = get_sub_dirs(test_dir, exclude_list=[], include_pattern=pattern, + exclude_pattern=exclude_pattern) + if not test_dirs: + logger.info("No test cases are collected...Existing.") + sys.exit(3) + else: + logger.info("Collected tests %s", test_dirs) with ExecutionEnv(MONITORING_AGENT, artifacts_dir, env_name, compare_local, monit) as prt: pre_command = 'export PYTHONPATH={}:$PYTHONPATH;'.format(os.path.join(str(ROOT_PATH), "agents")) - test_dirs = get_sub_dirs(test_dir, exclude_list=[], include_pattern=pattern, - exclude_pattern=exclude_pattern) - logger.info("Collected tests %s", test_dirs) for suite_name in tqdm(test_dirs, desc="Test Suites"): with Timer("Test suite {} execution time".format(suite_name)) as t: suite_artifacts_dir = os.path.join(artifacts_dir, suite_name) diff --git a/tests/performance/runs/context.py b/tests/performance/runs/context.py index 8b3c5d9b6..a204c67bc 100644 --- a/tests/performance/runs/context.py +++ b/tests/performance/runs/context.py @@ -75,6 +75,8 @@ def report_summary(reporter, suite_name): return code else: + msg = "{} run report is not generated.".format(suite_name) + logger.info(colored(msg, "yellow", attrs=['reverse', 'blink'])) return 0 def __exit__(self, type, value, traceback): diff --git a/tests/performance/runs/junit.py b/tests/performance/runs/junit.py index 6e23f8d8d..8ff8f1951 100644 --- a/tests/performance/runs/junit.py +++ b/tests/performance/runs/junit.py @@ -39,9 +39,12 @@ def generate_junit_report(self): run_process("vjunit -f {} -o {}".format(self.junit_xml_path, self.junit_html_path)) -def unescape(data): - """Unsescape the html characters from the data""" - return html.unescape(html.unescape(data)) +def pretty_text(data): + """Unsescape the html characters from the data & wrap it""" + if data is not None: + return textwrap.fill(html.unescape(html.unescape(data)), width=60) + else: + return "" def junit2array(junit_xml): @@ -49,13 +52,13 @@ def junit2array(junit_xml): rows = [header] for i, suite in enumerate(junit_xml): if len(suite) == 0: - rows.append([suite.name, "", "skipped", "No metrics to compare"]) + rows.append([suite.name, "", "skipped", + "No criteria specified or there is an error."]) else: for case in suite: result = case.result tag, msg = (result._tag, result.message) if result else ("passed", "") - msg = textwrap.fill(unescape(msg), width=50) - rows.append([suite.name, unescape(case.name), tag, msg]) + rows.append([suite.name, pretty_text(case.name), tag, pretty_text(msg)]) return rows From f7c43cd2fcb56d66650cab8b8d4ef7b1be651f12 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 29 Jun 2020 13:00:00 +0530 Subject: [PATCH 254/329] Do not crash on exception --- tests/performance/agents/metrics/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/performance/agents/metrics/__init__.py b/tests/performance/agents/metrics/__init__.py index 7d0c65042..9d7bf7eb2 100644 --- a/tests/performance/agents/metrics/__init__.py +++ b/tests/performance/agents/metrics/__init__.py @@ -110,11 +110,14 @@ def update_metric(metric_name, proc_type, stats): for op_name in operators: result['{}_{}_{}'.format(op_name, proc_name, metric_name)] = operators[op_name](stats) - # as_dict() gets all stats in one shot processes_stats = [] reclaimed_pids = [] - processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) + try: + # as_dict() gets all stats in one shot + processes_stats.append({'type': ProcessType.FRONTEND, 'stats': server_process.as_dict()}) + except: + pass for child in children: try: child_cmdline = child.cmdline() From b8874da9bed4c4fe69473b7e12c13609a02df51b Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 29 Jun 2020 13:41:34 +0530 Subject: [PATCH 255/329] Updated readme --- .circleci/README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index 5aa1460eb..e4c622418 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -10,14 +10,18 @@ Currently, following _workflows_ are available - 2. nightly 3. weekly -Following _jobs_ are executed under each workflow +Following _jobs_ are executed under each workflow - 1. **build** : Builds _frontend/model-server.jar_ and executes tests from gradle 2. **modelarchiver** : Builds and tests modelarchiver module 3. **python-tests** : Executes pytests from _mms/tests/unit_tests/_ 4. **benchmark** : Executes latency benchmark using resnet-18 model 5. (NEW!) **api-tests** : Executes newman test suite for API testing -> Please check the _workflows_ and _jobs_ section in _config.yml_ for an up to date list +Following _executors_ are available for job execution - +1. py27 +2. py36 + +> Please check the _workflows_, _jobs_ and _executors_ section in _config.yml_ for an up to date list ## scripts Instead of using inline commands inside _config.yml_, job steps are configured as shell scripts. @@ -44,7 +48,7 @@ To make it easy for developers to debug build issues locally, MMS supports Circl #### Command Developers can use the following command to build MMS locally: -**_./run_circleci_tests.py _** +**_./run_circleci_tests.py - ```bash $ cd multi-model-server $ ./run_circleci_tests.py smoke python-tests-py27 From 00bd959f52c891fb028fa03ef77bfe04a04676f3 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 29 Jun 2020 17:31:34 +0530 Subject: [PATCH 256/329] support for multiple job execution --- run_circleci_tests.py | 113 +++++++++++++++++++++++++++++++----------- 1 file changed, 84 insertions(+), 29 deletions(-) diff --git a/run_circleci_tests.py b/run_circleci_tests.py index 8557f3553..b424c4c70 100755 --- a/run_circleci_tests.py +++ b/run_circleci_tests.py @@ -6,6 +6,8 @@ # 3. docker import subprocess +import sys + import yaml import copy import argparse @@ -14,22 +16,34 @@ parser = argparse.ArgumentParser(description='Execute circleci jobs in a container on local machine') parser.add_argument('workflow', type=str, help='Workflow name from config.yml') -parser.add_argument('job', type=str, help='Job name from config.yml') +parser.add_argument('-j', '--job', type=str, help='Job name from config.yml') +parser.add_argument('-e', '--executor', type=str, help='Executor name from config.yml') args = parser.parse_args() workflow = args.workflow job = args.job +executor = args.executor cci_config_file = '.circleci/config.yml' processed_file = '.circleci/processed.yml' xformed_file = '.circleci/xformed.yml' +cci_config = {} processed_config = {} xformed_config = {} xformed_job_name = 'mms_xformed_job' blacklisted_steps = ['persist_to_workspace', 'attach_workspace', 'store_artifacts'] +# Read CircleCI's config +with open(cci_config_file) as fstream: + try: + cci_config = yaml.safe_load(fstream) + except yaml.YAMLError as err: + print(err) + # Create processed YAML using circleci cli's 'config process' commands -subprocess.check_call('circleci config process {} > {}'.format(cci_config_file, processed_file), shell=True) +porcess_config_cmd = 'circleci config process {} > {}'.format(cci_config_file, processed_file) +print("Executing command : ",porcess_config_cmd) +subprocess.check_call(porcess_config_cmd, shell=True) # Read the processed config with open(processed_file) as fstream: @@ -38,58 +52,99 @@ except yaml.YAMLError as err: print(err) +# All executors available in the config file +available_executors = list(cci_config['executors']) + +# All jobs available under the specified workflow jobs_in_workflow = processed_config['workflows'][workflow]['jobs'] # Recursively iterate over jobs in the workflow to generate an ordered list of parent jobs -def get_jobs_to_exec(job_name): +def get_processed_job_sequence(processed_job_name): result = [] for job in jobs_in_workflow : - if isinstance(job, str) and job_name == job: + if isinstance(job, str) and processed_job_name == job: # We have reached a root job (outter most parent), no further traversal break - elif isinstance(job, dict) and job_name == list(job)[0]: + elif isinstance(job, dict) and processed_job_name == list(job)[0]: # Find all parent jpbs, recurse to find their respective ancestors - parent_jobs = job[job_name].get('requires',[]) + parent_jobs = job[processed_job_name].get('requires',[]) for pjob in parent_jobs: - result += get_jobs_to_exec(pjob) + result += get_processed_job_sequence(pjob) break - return result + [job_name] + return result + [processed_job_name] + + +def get_jobs_to_exec(job): + result = {} + executors = [executor] if executor else available_executors + for exec in executors: + if job is None: + # List of all job names(as string) + result[exec] = map(lambda j: j if isinstance(j, str) else list(j)[0], jobs_in_workflow) + # Filter processed job names as per the executor + # "job_name-executor_name" is a convention set in config.yml + result[exec] = filter(lambda j: exec in j, result[exec]) + else: + # The list might contain duplicate parent jobs due to multiple fan-ins like config; remove the duplicates + # "job_name-executor_name" is a convention set in config.yml + result[exec] = OrderedDict.fromkeys(get_processed_job_sequence(job+'-'+exec)) + result[exec] = list(result[exec]) -# The list might contain duplicate parent jobs due to multiple fan-ins like config; remove the duplicates -jobs_to_exec = list(OrderedDict.fromkeys(get_jobs_to_exec(job))) + return result +# jobs_to_exec is a dict, with executor(s) as the key and list of jobs to be executed as its value +jobs_to_exec = get_jobs_to_exec(job) -# Merge all the steps from jobs to be executed + +# Merge all the steps from list of jobs to execute def get_jobs_steps(result, job_name): job_steps = processed_config['jobs'][job_name]['steps'] filtered_job_steps = list(filter(lambda step: list(step)[0] not in blacklisted_steps, job_steps)) return result + filtered_job_steps +result = {} -merged_steps = reduce(get_jobs_steps, jobs_to_exec, []) +for exec, jobs in jobs_to_exec.items(): + merged_steps = reduce(get_jobs_steps, jobs, []) -# Create a new job, using the first job as a reference -# This ensures configs like executor, environment, etc are maintained as it is from the first job -first_job = jobs_to_exec[0] -xformed_job = copy.deepcopy(processed_config['jobs'][first_job]) + # Create a new job, using the first job as a reference + # This ensures configs like executor, environment, etc are maintained as it is from the first job + first_job = jobs[0] + xformed_job = copy.deepcopy(processed_config['jobs'][first_job]) -# Add the merged steps to this newly introduced job -xformed_job['steps'] = merged_steps + # Add the merged steps to this newly introduced job + xformed_job['steps'] = merged_steps -# Create a duplicate config(transformed) with the newly introduced job (as the only job in config) -xformed_config = copy.deepcopy(processed_config) -xformed_config['jobs'] = {} -xformed_config['jobs'][xformed_job_name] = xformed_job + # Create a duplicate config(transformed) with the newly introduced job (as the only job in config) + xformed_config = copy.deepcopy(processed_config) + xformed_config['jobs'] = {} + xformed_config['jobs'][xformed_job_name] = xformed_job -# Create a transformed yaml -with open(xformed_file, 'w+') as fstream: - yaml.dump(xformed_config, fstream) + # Create a transformed yaml + with open(xformed_file, 'w+') as fstream: + yaml.dump(xformed_config, fstream) -# Locally execute the newly created job -# This job has all the steps(ordered and merged) from the mentioned job along with it's ancestors -subprocess.check_call('circleci local execute -c {} --job {}'.format(xformed_file, xformed_job_name), shell=True) + try: + # Locally execute the newly created job + # This job has all the steps(ordered and merged) from the mentioned job along with it's ancestors(if any) + local_execute_command = 'circleci local execute -c {} --job {}'.format(xformed_file, xformed_job_name) + print('Executing command : ', local_execute_command) + result[exec] = subprocess.check_call(local_execute_command, shell=True) + except subprocess.CalledProcessError as err: + result[exec] = err.returncode # Clean up, remove the processed and transformed yml files -subprocess.check_call('rm {} {}'.format(processed_file, xformed_file), shell=True) +cleanup_cmd = 'rm {} {}'.format(processed_file, xformed_file) +print('Executing command : ', cleanup_cmd) +subprocess.check_call(cleanup_cmd, shell=True) + +# Print job execution details +for exec, retcode in result.items(): + colorcode, status = ('\033[0;37;42m', 'successful') if retcode == 0 else ('\033[0;37;41m', 'failed') + print("{} Job execution {} using {} executor \x1b[0m".format(colorcode, status, exec)) + +# Exit as per overall status +sys_exit_code = 0 if all(retcode == 0 for exec, retcode in result.items()) else 1 +sys.exit(sys_exit_code) From 511a0ef51ebb3bfb04116b78e3c330ee57379779 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 29 Jun 2020 17:47:11 +0530 Subject: [PATCH 257/329] No need to remove .git --- .circleci/config.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7eaf18ae5..c8022a037 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -59,9 +59,6 @@ jobs: - run: name: Build frontend command: .circleci/scripts/linux_build.sh - - run: - name: Optimize before moving to workspace - command: rm -rf .git # Save disk space & time required for copy to\from workspace - store_artifacts: name: Store gradle testng results path: frontend/server/build/reports/tests/test From 9fd2233c021c03cb2e239d3801388d5e666af777 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 29 Jun 2020 18:28:41 +0530 Subject: [PATCH 258/329] Updated readme --- .circleci/README.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/.circleci/README.md b/.circleci/README.md index e4c622418..1d5022579 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -48,11 +48,25 @@ To make it easy for developers to debug build issues locally, MMS supports Circl #### Command Developers can use the following command to build MMS locally: -**_./run_circleci_tests.py - +**_./run_circleci_tests.py -j -e ** + +- _workflow_name_ +This is a madatory parameter + +- _-j, --job job_name_ +If specified, executes only the specified job name (along with the required parents). +If not specified, all jobs in the workflow are executed sequentially. + +- _-e, --executor executor_name_ +If specified, job is executed only on the specified executor(docker image). +If not specified, job is executed on all the available executors. + ```bash $ cd multi-model-server -$ ./run_circleci_tests.py smoke python-tests-py27 -$ ./run_circleci_tests.py nightly api-tests-py36 +$ ./run_circleci_tests.py smoke +$ ./run_circleci_tests.py smoke -j modelarchiver +$ ./run_circleci_tests.py smoke -e py36 +$ ./run_circleci_tests.py smoke -j modelarchiver -e py36 ``` ###### Checklist From 912108aa01c722602825e38d6f86a1c5be775d30 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 29 Jun 2020 19:45:03 +0530 Subject: [PATCH 259/329] pylint score 9.78 --- run_circleci_tests.py | 154 +++++++++++++++++++++++------------------- 1 file changed, 86 insertions(+), 68 deletions(-) diff --git a/run_circleci_tests.py b/run_circleci_tests.py index b424c4c70..a16a549d0 100755 --- a/run_circleci_tests.py +++ b/run_circleci_tests.py @@ -1,20 +1,29 @@ #!/usr/bin/env python +""" +- This script helps to execute circleci jobs in a container on developer's local machine. +- The script accepts workflow(mandatory), job(optional) and executor(optional) arguments. +- The script used circleci cli's process command to generate a processed yaml. +- The processed yaml, is parsed and twekaed to generate a new transformed yaml. +- The transformed yaml contains a single job, which is merged and ordered list of job steps +from the specfied and requird parent jobs. +""" # Make sure you have following dependencies installed on your local machine # 1. PyYAML (pip install PyYaml) # 2. CircleCI cli from - https://circleci.com/docs/2.0/local-cli/#installation # 3. docker +from collections import OrderedDict +from functools import reduce + import subprocess import sys - -import yaml import copy import argparse -from collections import OrderedDict -from functools import reduce +import yaml -parser = argparse.ArgumentParser(description='Execute circleci jobs in a container on local machine') +parser = argparse.ArgumentParser(description='Execute circleci jobs in a container \ + on your local machine') parser.add_argument('workflow', type=str, help='Workflow name from config.yml') parser.add_argument('-j', '--job', type=str, help='Job name from config.yml') parser.add_argument('-e', '--executor', type=str, help='Executor name from config.yml') @@ -24,127 +33,136 @@ job = args.job executor = args.executor -cci_config_file = '.circleci/config.yml' -processed_file = '.circleci/processed.yml' -xformed_file = '.circleci/xformed.yml' -cci_config = {} -processed_config = {} -xformed_config = {} -xformed_job_name = 'mms_xformed_job' -blacklisted_steps = ['persist_to_workspace', 'attach_workspace', 'store_artifacts'] +CCI_CONFIG_FILE = '.circleci/config.yml' +PROCESSED_FILE = '.circleci/processed.yml' +XFORMED_FILE = '.circleci/xformed.yml' +CCI_CONFIG = {} +PROCESSED_CONFIG = {} +XFORMED_CONFIG = {} +XFORMED_JOB_NAME = 'mms_xformed_job' +BLACKLISTED_STEPS = ['persist_to_workspace', 'attach_workspace', 'store_artifacts'] # Read CircleCI's config -with open(cci_config_file) as fstream: +with open(CCI_CONFIG_FILE) as fstream: try: - cci_config = yaml.safe_load(fstream) + CCI_CONFIG = yaml.safe_load(fstream) except yaml.YAMLError as err: print(err) # Create processed YAML using circleci cli's 'config process' commands -porcess_config_cmd = 'circleci config process {} > {}'.format(cci_config_file, processed_file) -print("Executing command : ",porcess_config_cmd) -subprocess.check_call(porcess_config_cmd, shell=True) +PROCESS_CONFIG_CMD = 'circleci config process {} > {}'.format(CCI_CONFIG_FILE, PROCESSED_FILE) +print("Executing command : ", PROCESS_CONFIG_CMD) +subprocess.check_call(PROCESS_CONFIG_CMD, shell=True) # Read the processed config -with open(processed_file) as fstream: +with open(PROCESSED_FILE) as fstream: try: - processed_config = yaml.safe_load(fstream) + PROCESSED_CONFIG = yaml.safe_load(fstream) except yaml.YAMLError as err: print(err) # All executors available in the config file -available_executors = list(cci_config['executors']) +available_executors = list(CCI_CONFIG['executors']) # All jobs available under the specified workflow -jobs_in_workflow = processed_config['workflows'][workflow]['jobs'] +jobs_in_workflow = PROCESSED_CONFIG['workflows'][workflow]['jobs'] -# Recursively iterate over jobs in the workflow to generate an ordered list of parent jobs def get_processed_job_sequence(processed_job_name): - result = [] - for job in jobs_in_workflow : - if isinstance(job, str) and processed_job_name == job: + """ Recursively iterate over jobs in the workflow to generate an ordered list of parent jobs """ + jobs_in_sequence = [] + for job_dict in jobs_in_workflow: + if isinstance(job_dict, str) and processed_job_name == job_dict: # We have reached a root job (outter most parent), no further traversal break - elif isinstance(job, dict) and processed_job_name == list(job)[0]: + elif isinstance(job_dict, dict) and processed_job_name == list(job_dict)[0]: # Find all parent jpbs, recurse to find their respective ancestors - parent_jobs = job[processed_job_name].get('requires',[]) + parent_jobs = job_dict[processed_job_name].get('requires', []) for pjob in parent_jobs: - result += get_processed_job_sequence(pjob) + jobs_in_sequence += get_processed_job_sequence(pjob) break - return result + [processed_job_name] + return jobs_in_sequence + [processed_job_name] -def get_jobs_to_exec(job): - result = {} +def get_jobs_to_exec(job_name): + """ Returns a dictionary of executors and a list of jobs to be executed in them """ + jobs_dict = {} executors = [executor] if executor else available_executors - for exec in executors: - if job is None: + for exectr_name in executors: + if job_name is None: # List of all job names(as string) - result[exec] = map(lambda j: j if isinstance(j, str) else list(j)[0], jobs_in_workflow) + jobs_dict[exectr_name] = map(lambda j: j if isinstance(j, str) \ + else list(j)[0], jobs_in_workflow) # Filter processed job names as per the executor # "job_name-executor_name" is a convention set in config.yml - result[exec] = filter(lambda j: exec in j, result[exec]) + jobs_dict[exectr_name] = filter(lambda j: exectr_name in j, jobs_dict[exectr_name]) else: - # The list might contain duplicate parent jobs due to multiple fan-ins like config; remove the duplicates + # The list might contain duplicate parent jobs due to multiple fan-ins like config + # - Remove the duplicates # "job_name-executor_name" is a convention set in config.yml - result[exec] = OrderedDict.fromkeys(get_processed_job_sequence(job+'-'+exec)) - result[exec] = list(result[exec]) + jobs_dict[exectr_name] = \ + OrderedDict.fromkeys(get_processed_job_sequence(job_name + '-' + exectr_name)) + jobs_dict[exectr_name] = list(jobs_dict[exectr_name]) + + return jobs_dict - return result # jobs_to_exec is a dict, with executor(s) as the key and list of jobs to be executed as its value jobs_to_exec = get_jobs_to_exec(job) -# Merge all the steps from list of jobs to execute -def get_jobs_steps(result, job_name): - job_steps = processed_config['jobs'][job_name]['steps'] - filtered_job_steps = list(filter(lambda step: list(step)[0] not in blacklisted_steps, job_steps)) - return result + filtered_job_steps +def get_jobs_steps(steps, job_name): + """ Merge all the steps from list of jobs to execute """ + job_steps = PROCESSED_CONFIG['jobs'][job_name]['steps'] + filtered_job_steps = list(filter(lambda step: list(step)[0] not in BLACKLISTED_STEPS, \ + job_steps)) + return steps + filtered_job_steps + -result = {} +result_dict = {} -for exec, jobs in jobs_to_exec.items(): +for exectr, jobs in jobs_to_exec.items(): merged_steps = reduce(get_jobs_steps, jobs, []) # Create a new job, using the first job as a reference - # This ensures configs like executor, environment, etc are maintained as it is from the first job + # This ensures configs like executor, environment, etc are maintained from the first job first_job = jobs[0] - xformed_job = copy.deepcopy(processed_config['jobs'][first_job]) + xformed_job = copy.deepcopy(PROCESSED_CONFIG['jobs'][first_job]) # Add the merged steps to this newly introduced job xformed_job['steps'] = merged_steps - # Create a duplicate config(transformed) with the newly introduced job (as the only job in config) - xformed_config = copy.deepcopy(processed_config) - xformed_config['jobs'] = {} - xformed_config['jobs'][xformed_job_name] = xformed_job + # Create a duplicate config(transformed) with the newly introduced job as the only job in config + XFORMED_CONFIG = copy.deepcopy(PROCESSED_CONFIG) + XFORMED_CONFIG['jobs'] = {} + XFORMED_CONFIG['jobs'][XFORMED_JOB_NAME] = xformed_job # Create a transformed yaml - with open(xformed_file, 'w+') as fstream: - yaml.dump(xformed_config, fstream) + with open(XFORMED_FILE, 'w+') as fstream: + yaml.dump(XFORMED_CONFIG, fstream) try: # Locally execute the newly created job - # This job has all the steps(ordered and merged) from the mentioned job along with it's ancestors(if any) - local_execute_command = 'circleci local execute -c {} --job {}'.format(xformed_file, xformed_job_name) - print('Executing command : ', local_execute_command) - result[exec] = subprocess.check_call(local_execute_command, shell=True) + # This newly created job has all the steps (ordered and merged from steps in parent job(s)) + LOCAL_EXECUTE_CMD = 'circleci local execute -c {} --job {}'.format(XFORMED_FILE, \ + XFORMED_JOB_NAME) + print('Executing command : ', LOCAL_EXECUTE_CMD) + result_dict[exectr] = subprocess.check_call(LOCAL_EXECUTE_CMD, shell=True) except subprocess.CalledProcessError as err: - result[exec] = err.returncode + result_dict[exectr] = err.returncode # Clean up, remove the processed and transformed yml files -cleanup_cmd = 'rm {} {}'.format(processed_file, xformed_file) -print('Executing command : ', cleanup_cmd) -subprocess.check_call(cleanup_cmd, shell=True) +CLEANUP_CMD = 'rm {} {}'.format(PROCESSED_FILE, XFORMED_FILE) +print('Executing command : ', CLEANUP_CMD) +subprocess.check_call(CLEANUP_CMD, shell=True) # Print job execution details -for exec, retcode in result.items(): - colorcode, status = ('\033[0;37;42m', 'successful') if retcode == 0 else ('\033[0;37;41m', 'failed') - print("{} Job execution {} using {} executor \x1b[0m".format(colorcode, status, exec)) +for exectr, retcode in result_dict.items(): + colorcode, status = ('\033[0;37;42m', 'successful') if retcode == 0 \ + else ('\033[0;37;41m', 'failed') + print("{} Job execution {} using {} executor \x1b[0m".format(colorcode, status, exectr)) # Exit as per overall status -sys_exit_code = 0 if all(retcode == 0 for exec, retcode in result.items()) else 1 -sys.exit(sys_exit_code) +SYS_EXIT_CODE = 0 if all(retcode == 0 for exectr, retcode in result_dict.items()) else 1 +sys.exit(SYS_EXIT_CODE) From 98d3e5538f6f8e54516d7d5c128c2f4a7381b4ee Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 29 Jun 2020 20:05:28 +0530 Subject: [PATCH 260/329] pylint score 10 --- run_circleci_tests.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/run_circleci_tests.py b/run_circleci_tests.py index a16a549d0..d3e54b086 100755 --- a/run_circleci_tests.py +++ b/run_circleci_tests.py @@ -71,16 +71,15 @@ def get_processed_job_sequence(processed_job_name): """ Recursively iterate over jobs in the workflow to generate an ordered list of parent jobs """ jobs_in_sequence = [] - for job_dict in jobs_in_workflow: - if isinstance(job_dict, str) and processed_job_name == job_dict: - # We have reached a root job (outter most parent), no further traversal - break - elif isinstance(job_dict, dict) and processed_job_name == list(job_dict)[0]: - # Find all parent jpbs, recurse to find their respective ancestors - parent_jobs = job_dict[processed_job_name].get('requires', []) - for pjob in parent_jobs: - jobs_in_sequence += get_processed_job_sequence(pjob) - break + + job_dict = next((jd for jd in jobs_in_workflow \ + if isinstance(jd, dict) and processed_job_name == list(jd)[0]), None) + if job_dict: + # Find all parent jobs, recurse to find their respective ancestors + parent_jobs = job_dict[processed_job_name].get('requires', []) + for pjob in parent_jobs: + jobs_in_sequence += get_processed_job_sequence(pjob) + return jobs_in_sequence + [processed_job_name] @@ -96,7 +95,8 @@ def get_jobs_to_exec(job_name): else list(j)[0], jobs_in_workflow) # Filter processed job names as per the executor # "job_name-executor_name" is a convention set in config.yml - jobs_dict[exectr_name] = filter(lambda j: exectr_name in j, jobs_dict[exectr_name]) + jobs_dict[exectr_name] = filter(lambda j, exectr_name: exectr_name in j, \ + jobs_dict[exectr_name]) else: # The list might contain duplicate parent jobs due to multiple fan-ins like config # - Remove the duplicates From a1aae99ed314f3a0428ad8b64dba2c6fb97c26a3 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 29 Jun 2020 20:09:31 +0530 Subject: [PATCH 261/329] pylint score 10 --- run_circleci_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run_circleci_tests.py b/run_circleci_tests.py index d3e54b086..5001c0006 100755 --- a/run_circleci_tests.py +++ b/run_circleci_tests.py @@ -95,8 +95,8 @@ def get_jobs_to_exec(job_name): else list(j)[0], jobs_in_workflow) # Filter processed job names as per the executor # "job_name-executor_name" is a convention set in config.yml - jobs_dict[exectr_name] = filter(lambda j, exectr_name: exectr_name in j, \ - jobs_dict[exectr_name]) + # pylint: disable=cell-var-from-loop + jobs_dict[exectr_name] = filter(lambda j: exectr_name in j, jobs_dict[exectr_name]) else: # The list might contain duplicate parent jobs due to multiple fan-ins like config # - Remove the duplicates From dc1b6c4de92f0c11e3a9b8a482feb156bb4306c5 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 30 Jun 2020 12:41:03 +0530 Subject: [PATCH 262/329] multiple_inference_and_scaling --- .../environments/xlarge.yaml | 10 + .../multiple_inference_and_scaling.yaml | 74 +++++ .../multuple_inference_and_scaling.jmx | 302 ++++++++++++++++++ 3 files changed, 386 insertions(+) create mode 100644 tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml create mode 100644 tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml create mode 100644 tests/performance/tests/multiple_inference_and_scaling/multuple_inference_and_scaling.jmx diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml new file mode 100644 index 000000000..36b7dc0ad --- /dev/null +++ b/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml @@ -0,0 +1,10 @@ +--- +settings: + env: + INFR1_SUCC : 100% + INFR2_SUCC: 100% + INFR1_RT : 290ms + INFR2_RT: 450ms + TOTAL_PROCS : 5 + TOTAL_FDS : 107 + TOTAL_MEM : 600000000 #600MB \ No newline at end of file diff --git a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml new file mode 100644 index 000000000..265c9f7d4 --- /dev/null +++ b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml @@ -0,0 +1,74 @@ +--- +execution: +- concurrency: 10 + ramp-up: 1s + hold-for: 300s + scenario: inference_multiple_models + +scenarios: + inference_multiple_models: + script: multiple_inference_and_scaling.jmx + +modules: + server_local_monitoring: + class : metrics_monitoring_inproc.Monitor + +services: + - module: shellexec + prepare: + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "multi-model-server --start > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" + - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar" + - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true" + - "curl -s -X PUT http://localhost:8081/models/resnet-18?min_worker=1&synchronous=true" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" + - "rm kitten.jpg" + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + logging : True + metrics: + - total_processes + - sum_all_file_descriptors + - sum_all_memory_rss + +reporting: +- module: passfail + criteria: + # Inbuilt Criteria + - success of Inference1<${INFR1_SUCC}, stop as failed + - success of Inference2<${INFR2_SUCC}, stop as failed + - avg-rt of Inference1>${INFR1_RT}, stop as failed + - avg-rt of Inference2>${INFR2_RT}, stop as failed + # Custom Criteria + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_processes + condition: '>' + threshold: ${TOTAL_PROCS} + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_processes + condition: '<' + threshold: ${TOTAL_PROCS} + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_all_file_descriptors + condition: '>' + threshold: ${TOTAL_FDS} + timeframe: 5s + stop : true + fail : true +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/sum_all_memory_rss +# condition: '>' +# threshold: ${TOTAL_MEM} +# timeframe: 5s +# stop : true +# fail : true diff --git a/tests/performance/tests/multiple_inference_and_scaling/multuple_inference_and_scaling.jmx b/tests/performance/tests/multiple_inference_and_scaling/multuple_inference_and_scaling.jmx new file mode 100644 index 000000000..8a2bba2db --- /dev/null +++ b/tests/performance/tests/multiple_inference_and_scaling/multuple_inference_and_scaling.jmx @@ -0,0 +1,302 @@ + + + + + + false + true + false + + + + + + + + + + model1 + squeezenet_v1.1 + = + Model1 Name + + + model2 + resnet-18 + Model2 Name + = + + + scale_up_workers1 + 4 + = + + + scale_down_workers1 + 1 + = + + + scale_up_workers2 + 4 + = + + + scale_down_workers2 + 1 + = + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model1} + POST + true + false + true + true + + + + + + + 1 + 0 + 20 + + + + 1000 + 100 + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model1}?min_worker=${scale_down_workers1}&synchronous=true + PUT + true + false + true + false + + + + + + + 1 + 0 + 20 + + + + 1000 + 100 + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model1}?min_worker=${scale_up_workers1}&synchronous=true + PUT + true + false + true + false + + + + + + + 1 + 0 + 20 + + + + 1000 + 100 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model2} + POST + true + false + true + true + + + + + + + 1 + 0 + 20 + + + + 1000 + 100 + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model2}?min_worker=${scale_down_workers2}&synchronous=true + PUT + true + false + true + false + + + + + + + 1 + 0 + 20 + + + + 1000 + 100 + + + + + + + + + ${__P(management_port,8444)} + + + /models/${model2}?min_worker=${scale_up_workers2}&synchronous=true + PUT + true + false + true + false + + + + + + + 1 + 0 + 20 + + + + 1000 + 100 + + + + + + + From ff8b7fd3d21b6e6b0cf62f5937bdd72338d4d446 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 30 Jun 2020 12:54:10 +0530 Subject: [PATCH 263/329] rename --- ...ference_and_scaling.jmx => multiple_inference_and_scaling.jmx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/performance/tests/multiple_inference_and_scaling/{multuple_inference_and_scaling.jmx => multiple_inference_and_scaling.jmx} (100%) diff --git a/tests/performance/tests/multiple_inference_and_scaling/multuple_inference_and_scaling.jmx b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.jmx similarity index 100% rename from tests/performance/tests/multiple_inference_and_scaling/multuple_inference_and_scaling.jmx rename to tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.jmx From cabf5d3b01ce6e6f9e5d74746c1243909988c092 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 30 Jun 2020 12:57:18 +0530 Subject: [PATCH 264/329] test suite movement --- .../multiple_inference_and_scaling/environments/xlarge.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml index 36b7dc0ad..469498486 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml @@ -6,5 +6,5 @@ settings: INFR1_RT : 290ms INFR2_RT: 450ms TOTAL_PROCS : 5 - TOTAL_FDS : 107 + TOTAL_FDS : 155 TOTAL_MEM : 600000000 #600MB \ No newline at end of file From 3636634dcd756bed0244c885a75228bb5fa54295 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 30 Jun 2020 12:59:21 +0530 Subject: [PATCH 265/329] test suite movement --- .../multiple_inference_and_scaling/environments/xlarge.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml index 469498486..4b49d6ae4 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml @@ -5,6 +5,6 @@ settings: INFR2_SUCC: 100% INFR1_RT : 290ms INFR2_RT: 450ms - TOTAL_PROCS : 5 - TOTAL_FDS : 155 + TOTAL_PROCS : 11 + TOTAL_FDS : 300 TOTAL_MEM : 600000000 #600MB \ No newline at end of file From ffc229742d7aee20f40430cc627697ecdb5e41d9 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 30 Jun 2020 13:01:37 +0530 Subject: [PATCH 266/329] test suite movement --- .../multiple_inference_and_scaling.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml index 265c9f7d4..2fb0219a2 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml @@ -51,13 +51,6 @@ reporting: timeframe: 1s stop : true fail : true - - class: bzt.modules.monitoring.MonitoringCriteria - subject: ServerLocalClient/total_processes - condition: '<' - threshold: ${TOTAL_PROCS} - timeframe: 1s - stop : true - fail : true - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/sum_all_file_descriptors condition: '>' From 468fb02515d01a759184eced3138305f5a21804d Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 30 Jun 2020 13:04:19 +0530 Subject: [PATCH 267/329] test suite movement --- .../multiple_inference_and_scaling/environments/xlarge.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml index 4b49d6ae4..288c9aeb6 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml @@ -5,6 +5,6 @@ settings: INFR2_SUCC: 100% INFR1_RT : 290ms INFR2_RT: 450ms - TOTAL_PROCS : 11 + TOTAL_PROCS : 14 TOTAL_FDS : 300 TOTAL_MEM : 600000000 #600MB \ No newline at end of file From 22d036e136b328576c08172577f2223f4e16024b Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 30 Jun 2020 13:06:04 +0530 Subject: [PATCH 268/329] test suite movement --- .../multiple_inference_and_scaling.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml index 2fb0219a2..48805bca2 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml @@ -48,7 +48,7 @@ reporting: subject: ServerLocalClient/total_processes condition: '>' threshold: ${TOTAL_PROCS} - timeframe: 1s + timeframe: 10s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria From 9ffd9a5ac6de902ec55b94df5ae80972cc3bcf6a Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 30 Jun 2020 13:16:12 +0530 Subject: [PATCH 269/329] change test criteria --- .../environments/xlarge.yaml | 4 ++- .../multiple_inference_and_scaling.yaml | 30 ++++++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml index 288c9aeb6..1671213d1 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml @@ -7,4 +7,6 @@ settings: INFR2_RT: 450ms TOTAL_PROCS : 14 TOTAL_FDS : 300 - TOTAL_MEM : 600000000 #600MB \ No newline at end of file + TOTAL_MEM : 2000000000 #~2GB + TOTAL_ORPHANS : 0 + FRNTEND_MEM : 1000000000 #~1GB \ No newline at end of file diff --git a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml index 48805bca2..8f3324f2a 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml @@ -34,6 +34,8 @@ services: - total_processes - sum_all_file_descriptors - sum_all_memory_rss + - frontend_memory_rss + - orphans reporting: - module: passfail @@ -58,10 +60,24 @@ reporting: timeframe: 5s stop : true fail : true -# - class: bzt.modules.monitoring.MonitoringCriteria -# subject: ServerLocalClient/sum_all_memory_rss -# condition: '>' -# threshold: ${TOTAL_MEM} -# timeframe: 5s -# stop : true -# fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_all_memory_rss + condition: '>' + threshold: ${TOTAL_MEM} + timeframe: 5s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/orphans + condition: '>' + threshold: ${TOTAL_ORPHANS} + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/frontend_memory_rss + condition: '>' + threshold: ${FRNTEND_MEM} + timeframe: 5s + stop : true + fail : true From 40c1c2ffe87c832dbf3f448ed50df39c0be19350 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 30 Jun 2020 13:26:21 +0530 Subject: [PATCH 270/329] increase delay --- .../multiple_inference_and_scaling.jmx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.jmx b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.jmx index 8a2bba2db..ee82e7fe6 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.jmx +++ b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.jmx @@ -112,7 +112,7 @@ - 1000 + 100000 100 @@ -143,7 +143,7 @@ - 1000 + 100000 100 @@ -174,7 +174,7 @@ - 1000 + 100000 100 @@ -229,7 +229,7 @@ - 1000 + 100000 100 @@ -260,7 +260,7 @@ - 1000 + 100000 100 @@ -291,7 +291,7 @@ - 1000 + 100000 100 From 5e45190018be1f9d5a5711cf2315cbb31256cba8 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Tue, 30 Jun 2020 13:34:00 +0530 Subject: [PATCH 271/329] delay adjust --- .../multiple_inference_and_scaling.jmx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.jmx b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.jmx index ee82e7fe6..cbff8debc 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.jmx +++ b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.jmx @@ -112,7 +112,7 @@ - 100000 + 1000 100 @@ -143,7 +143,7 @@ - 100000 + 10000 100 @@ -174,7 +174,7 @@ - 100000 + 10000 100 @@ -229,7 +229,7 @@ - 100000 + 1000 100 @@ -260,7 +260,7 @@ - 100000 + 10000 100 @@ -291,7 +291,7 @@ - 100000 + 10000 100 From e500ba1c91d3894d4c56a283a7fc61286db6208e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 14:33:54 +0530 Subject: [PATCH 272/329] trial execution --- .circleci/config.yml | 95 +++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 93f24e29f..d107dcbfd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -155,55 +155,60 @@ jobs: workflows: version: 2 - smoke: - jobs: - - &build - build: - name: build-<< matrix.executor >> - matrix: &matrix - parameters: - executor: ["py27", "py36"] - - &modelarchiver - modelarchiver: - name: modelarchiver-<< matrix.executor >> - matrix: *matrix - - &python-tests - python-tests: - name: python-tests-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix - - nightly: - triggers: - - schedule: - cron: "0 0 * * *" - filters: - branches: - only: - - master - jobs: - - *build - - *modelarchiver - - *python-tests - - &api-tests - api-tests: - name: api-tests-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix +# smoke: +# jobs: +# - &build +# build: +# name: build-<< matrix.executor >> +# matrix: &matrix +# parameters: +# executor: ["py27", "py36"] +# - &modelarchiver +# modelarchiver: +# name: modelarchiver-<< matrix.executor >> +# matrix: *matrix +# - &python-tests +# python-tests: +# name: python-tests-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix +# +# nightly: +# triggers: +# - schedule: +# cron: "0 0 * * *" +# filters: +# branches: +# only: +# - master +# jobs: +# - *build +# - *modelarchiver +# - *python-tests +# - &api-tests +# api-tests: +# name: api-tests-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix weekly: - triggers: - - schedule: - cron: "0 0 * * 0" - filters: - branches: - only: - - master +# triggers: +# - schedule: +# cron: "0 0 * * 0" +# filters: +# branches: +# only: +# - master jobs: - *build - - benchmark: +# - benchmark: +# name: benchmark-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix + - performance_regression: name: benchmark-<< matrix.executor >> requires: - build-<< matrix.executor >> From 8fbd8157c8306417b10f7930a1e0e091ed9a93ff Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 14:37:01 +0530 Subject: [PATCH 273/329] trial execution --- .circleci/config.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d107dcbfd..660b09341 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -202,7 +202,17 @@ workflows: # only: # - master jobs: - - *build +# - *build + +########## Remove after dev ################# + - build: + name: build-<< matrix.executor >> + matrix: &matrix + parameters: + executor: ["py36"] +# executor: ["py27", "py36"] +############################################## + # - benchmark: # name: benchmark-<< matrix.executor >> # requires: From 0772db649d55a74987ca884717ce8e5d03745192 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 14:41:47 +0530 Subject: [PATCH 274/329] [skip ci] job rename, perf-regres... --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 660b09341..5357d5bf0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -115,7 +115,7 @@ jobs: path: /tmp/MMSBenchmark/out/latency/resnet-18/report/ destination: benchmark-latency-resnet-18 - performance_regression: + performance-regression: parameters: executor: type: executor @@ -219,7 +219,7 @@ workflows: # - build-<< matrix.executor >> # matrix: *matrix - performance_regression: - name: benchmark-<< matrix.executor >> + name: performance-regression-<< matrix.executor >> requires: - build-<< matrix.executor >> matrix: *matrix From d060096e486aac662f5f356cc93b3dff772f021e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 14:42:37 +0530 Subject: [PATCH 275/329] [skip ci] job rename, perf-regres... --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5357d5bf0..ef745754c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -218,7 +218,7 @@ workflows: # requires: # - build-<< matrix.executor >> # matrix: *matrix - - performance_regression: + - performance-regression: name: performance-regression-<< matrix.executor >> requires: - build-<< matrix.executor >> From 836e283e4f2c5e13846e4cfe930c9f12a41104ab Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 15:25:07 +0530 Subject: [PATCH 276/329] Added python shebang --- tests/performance/run_performance_suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 6ab1b4e3c..12a308968 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -1,4 +1,4 @@ - +#!/usr/bin/env python # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # Licensed under the Apache License, Version 2.0 (the "License"). From 31b8179bf47376b5a156bad754ff137f7952defe Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 15:33:28 +0530 Subject: [PATCH 277/329] update circleci config and script for new performance regression --- .circleci/config.yml | 6 +++--- .circleci/scripts/linux_test_perf_regression.sh | 17 ++++++++--------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ef745754c..6aa28d2cd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -128,13 +128,13 @@ jobs: command: .circleci/scripts/linux_test_perf_regression.sh - store_artifacts: name: Store server logs - path: mms.log + path: tests/performance/logs/ - store_artifacts: name: Store artifacts from performance regression run - path: /tmp/mms-performance-regression/ + path: tests/performance/run_artifacts/ - store_test_results: name: Store Taurus test results - path: /tmp/mms-performance-regression/report/ + path: tests/performance/run_artifacts/report/ modelarchiver: parameters: diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 25aa9e81d..a02fbb916 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -1,12 +1,12 @@ #!/bin/bash -ARTIFACTS_DIR='/tmp/mms-performance-regression' -RESULT_DIR=$ARTIFACTS_DIR'/report/performance' -JMETER_PATH='/opt/apache-jmeter-5.3/bin/jmeter' +ARTIFACTS_DIR='run_artifacts' +RESULT_DIR=$ARTIFACTS_DIR'/report/performance/' +#JMETER_PATH='/opt/apache-jmeter-5.3/bin/jmeter' # Start MMS server -multi-model-server --start >> mms.log 2>&1 -sleep 10 +#multi-model-server --start >> mms.log 2>&1 +#sleep 10 cd tests/performance @@ -23,16 +23,15 @@ pip install -r requirements.txt pip install bzt # Execute performance test suite and store exit code -curl -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg -./run_performance_suite.py --artifacts-dir=$ARTIFACTS_DIR --jmeter-path=$JMETER_PATH -p health_check.yaml +./run_performance_suite.py -e xlarge -p health_check.yaml EXIT_CODE=$? # Stop server -multi-model-server --stop >> mms.log 2>&1 +#multi-model-server --stop >> mms.log 2>&1 # Collect and store test results in result directory to be picked up by CircleCI mkdir -p $RESULT_DIR -cp $ARTIFACTS_DIR/**/junit.xml $RESULT_DIR +cp $ARTIFACTS_DIR/**/performance_results.xml $RESULT_DIR # Exit with the same error code as that of test execution exit EXIT_CODE From 2326847887542b14f9af9ff62a6aef4c465054ff Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 15:43:42 +0530 Subject: [PATCH 278/329] corrected script --- .circleci/scripts/linux_test_perf_regression.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index a02fbb916..edf823670 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -23,7 +23,7 @@ pip install -r requirements.txt pip install bzt # Execute performance test suite and store exit code -./run_performance_suite.py -e xlarge -p health_check.yaml +./run_performance_suite.py -e xlarge -p health_check EXIT_CODE=$? # Stop server From ffd4f26c1f11515a25dec73d2c6af04d4c8f0265 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 15:57:41 +0530 Subject: [PATCH 279/329] Use installed jmeter --- .circleci/scripts/linux_test_perf_regression.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index edf823670..0a4d45f17 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -2,7 +2,7 @@ ARTIFACTS_DIR='run_artifacts' RESULT_DIR=$ARTIFACTS_DIR'/report/performance/' -#JMETER_PATH='/opt/apache-jmeter-5.3/bin/jmeter' +JMETER_PATH='/opt/apache-jmeter-5.3/bin/jmeter' # Start MMS server #multi-model-server --start >> mms.log 2>&1 @@ -23,7 +23,7 @@ pip install -r requirements.txt pip install bzt # Execute performance test suite and store exit code -./run_performance_suite.py -e xlarge -p health_check +./run_performance_suite.py -j $JMETER_PATH -e xlarge EXIT_CODE=$? # Stop server From 8f520a9a84f63ff36e811f269172b5e0dc3f2d21 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 16:17:55 +0530 Subject: [PATCH 280/329] comparision with s3 --- .circleci/scripts/linux_test_perf_regression.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 0a4d45f17..bf0e9d8da 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -23,7 +23,7 @@ pip install -r requirements.txt pip install bzt # Execute performance test suite and store exit code -./run_performance_suite.py -j $JMETER_PATH -e xlarge +./run_performance_suite.py -j $JMETER_PATH -e xlarge --no-compare-local EXIT_CODE=$? # Stop server From 94fcac58ef29aab9ac65496d09eeb466be575d94 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 16:21:11 +0530 Subject: [PATCH 281/329] trigger only one case --- .circleci/scripts/linux_test_perf_regression.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index bf0e9d8da..dcee6186d 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -23,7 +23,7 @@ pip install -r requirements.txt pip install bzt # Execute performance test suite and store exit code -./run_performance_suite.py -j $JMETER_PATH -e xlarge --no-compare-local +./run_performance_suite.py -j $JMETER_PATH -e xlarge --no-compare-local -p health_check EXIT_CODE=$? # Stop server From 0e25b5db5fe93209c1f5d62f7a3d6d74935ecfd2 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 16:30:43 +0530 Subject: [PATCH 282/329] trigger --- .circleci/scripts/linux_test_perf_regression.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index dcee6186d..95d1ceaa5 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -35,3 +35,4 @@ cp $ARTIFACTS_DIR/**/performance_results.xml $RESULT_DIR # Exit with the same error code as that of test execution exit EXIT_CODE + From 8333aaa98c6f8b1f7fe4e45079c88787a6ddd064 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 17:12:54 +0530 Subject: [PATCH 283/329] performance regression on py27 --- .circleci/config.yml | 2 +- .circleci/scripts/linux_test_perf_regression.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6aa28d2cd..233f67e16 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -209,7 +209,7 @@ workflows: name: build-<< matrix.executor >> matrix: &matrix parameters: - executor: ["py36"] + executor: ["py27"] # executor: ["py27", "py36"] ############################################## diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 95d1ceaa5..9f6f75bd1 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -15,7 +15,7 @@ PY_MAJOR_VER=$(python -c 'import sys; major = sys.version_info.major; print(majo if [ $PY_MAJOR_VER -eq 2 ]; then # Hack to use python 3.6.5 for bzt installation and execution export PATH="/root/.pyenv/bin:/root/.pyenv/shims:$PATH" - pyenv local 3.6.5 + pyenv local 3.6.5 system fi # Install dependencies From a320bcde4c08fcc374c5dcad534626394bb598b6 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 17:31:24 +0530 Subject: [PATCH 284/329] all performance scenarios,partially roll back changes to config.yml --- .circleci/config.yml | 96 +++++++++---------- .../scripts/linux_test_perf_regression.sh | 11 +-- 2 files changed, 45 insertions(+), 62 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 233f67e16..a9bb7e8c5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -155,43 +155,43 @@ jobs: workflows: version: 2 -# smoke: -# jobs: -# - &build -# build: -# name: build-<< matrix.executor >> -# matrix: &matrix -# parameters: -# executor: ["py27", "py36"] -# - &modelarchiver -# modelarchiver: -# name: modelarchiver-<< matrix.executor >> -# matrix: *matrix -# - &python-tests -# python-tests: -# name: python-tests-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix -# -# nightly: -# triggers: -# - schedule: -# cron: "0 0 * * *" -# filters: -# branches: -# only: -# - master -# jobs: -# - *build -# - *modelarchiver -# - *python-tests -# - &api-tests -# api-tests: -# name: api-tests-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix + smoke: + jobs: + - &build + build: + name: build-<< matrix.executor >> + matrix: &matrix + parameters: + executor: ["py27", "py36"] + - &modelarchiver + modelarchiver: + name: modelarchiver-<< matrix.executor >> + matrix: *matrix + - &python-tests + python-tests: + name: python-tests-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix + + nightly: + triggers: + - schedule: + cron: "0 0 * * *" + filters: + branches: + only: + - master + jobs: + - *build + - *modelarchiver + - *python-tests + - &api-tests + api-tests: + name: api-tests-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix weekly: # triggers: @@ -202,22 +202,12 @@ workflows: # only: # - master jobs: -# - *build - -########## Remove after dev ################# - - build: - name: build-<< matrix.executor >> - matrix: &matrix - parameters: - executor: ["py27"] -# executor: ["py27", "py36"] -############################################## - -# - benchmark: -# name: benchmark-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix + - *build + - benchmark: + name: benchmark-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix - performance-regression: name: performance-regression-<< matrix.executor >> requires: diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 9f6f75bd1..5d3766dce 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -4,16 +4,13 @@ ARTIFACTS_DIR='run_artifacts' RESULT_DIR=$ARTIFACTS_DIR'/report/performance/' JMETER_PATH='/opt/apache-jmeter-5.3/bin/jmeter' -# Start MMS server -#multi-model-server --start >> mms.log 2>&1 -#sleep 10 - cd tests/performance # Only on a python 2 environment - PY_MAJOR_VER=$(python -c 'import sys; major = sys.version_info.major; print(major);') if [ $PY_MAJOR_VER -eq 2 ]; then # Hack to use python 3.6.5 for bzt installation and execution + # While MMS continues to use system python which is at 2.7.x export PATH="/root/.pyenv/bin:/root/.pyenv/shims:$PATH" pyenv local 3.6.5 system fi @@ -23,16 +20,12 @@ pip install -r requirements.txt pip install bzt # Execute performance test suite and store exit code -./run_performance_suite.py -j $JMETER_PATH -e xlarge --no-compare-local -p health_check +./run_performance_suite.py -j $JMETER_PATH -e xlarge --no-compare-local EXIT_CODE=$? -# Stop server -#multi-model-server --stop >> mms.log 2>&1 - # Collect and store test results in result directory to be picked up by CircleCI mkdir -p $RESULT_DIR cp $ARTIFACTS_DIR/**/performance_results.xml $RESULT_DIR # Exit with the same error code as that of test execution exit EXIT_CODE - From 9107441e90212be2223b8a580909a43339d8544d Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 21:15:31 +0530 Subject: [PATCH 285/329] newman suite moved from test/ to tests/api --- .circleci/scripts/linux_test_api.sh | 10 ++-- test/resources/config.properties | 4 -- test/resources/kitten.jpg | Bin 110969 -> 0 bytes {test => tests/api}/README.md | 4 +- {test => tests/api}/postman/environment.json | 0 .../api}/postman/https_test_collection.json | 0 .../inference_api_test_collection.json | 0 .../api}/postman/inference_data.json | 56 +++++++++--------- .../management_api_test_collection.json | 0 {test => tests/api}/regression_tests.sh | 10 ++-- {test => tests/api}/resources/certs.pem | 0 tests/api/resources/config.properties | 4 ++ {test => tests/api}/resources/key.pem | 0 {test => tests/api}/screenshot/postman.png | Bin 14 files changed, 44 insertions(+), 44 deletions(-) delete mode 100644 test/resources/config.properties delete mode 100644 test/resources/kitten.jpg rename {test => tests/api}/README.md (94%) rename {test => tests/api}/postman/environment.json (100%) rename {test => tests/api}/postman/https_test_collection.json (100%) rename {test => tests/api}/postman/inference_api_test_collection.json (100%) rename {test => tests/api}/postman/inference_data.json (95%) rename {test => tests/api}/postman/management_api_test_collection.json (100%) rename {test => tests/api}/regression_tests.sh (76%) mode change 100644 => 100755 rename {test => tests/api}/resources/certs.pem (100%) create mode 100644 tests/api/resources/config.properties rename {test => tests/api}/resources/key.pem (100%) rename {test => tests/api}/screenshot/postman.png (100%) diff --git a/.circleci/scripts/linux_test_api.sh b/.circleci/scripts/linux_test_api.sh index 23f908abb..a9aeba588 100755 --- a/.circleci/scripts/linux_test_api.sh +++ b/.circleci/scripts/linux_test_api.sh @@ -7,11 +7,11 @@ MMS_LOG_FILE_INFERENCE='mms_inference.log' MMS_LOG_FILE_HTTPS='mms_https.log' MMS_CONFIG_FILE_HTTPS='test/resources/config.properties' -POSTMAN_ENV_FILE='test/postman/environment.json' -POSTMAN_COLLECTION_MANAGEMENT='test/postman/management_api_test_collection.json' -POSTMAN_COLLECTION_INFERENCE='test/postman/inference_api_test_collection.json' -POSTMAN_COLLECTION_HTTPS='test/postman/https_test_collection.json' -POSTMAN_DATA_FILE_INFERENCE='test/postman/inference_data.json' +POSTMAN_ENV_FILE='tests/api/postman/environment.json' +POSTMAN_COLLECTION_MANAGEMENT='tests/api/postman/management_api_test_collection.json' +POSTMAN_COLLECTION_INFERENCE='tests/api/postman/inference_api_test_collection.json' +POSTMAN_COLLECTION_HTTPS='tests/api/postman/https_test_collection.json' +POSTMAN_DATA_FILE_INFERENCE='tests/api/postman/inference_data.json' REPORT_FILE_MANAGEMENT='test/management-api-report.html' REPORT_FILE_INFERENCE='test/inference-api-report.html' diff --git a/test/resources/config.properties b/test/resources/config.properties deleted file mode 100644 index ce6e2f3b9..000000000 --- a/test/resources/config.properties +++ /dev/null @@ -1,4 +0,0 @@ -inference_address=https://127.0.0.1:8443 -management_address=https://127.0.0.1:8444 -private_key_file=test/resources/key.pem -certificate_file=test/resources/certs.pem diff --git a/test/resources/kitten.jpg b/test/resources/kitten.jpg deleted file mode 100644 index ffcd2be2c674fddc4886117c12f3b29e4367aba8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110969 zcmb5VcUY4_vo{=iuY!VvW@w=(-9YFiK#%|dsftwTCG;X4M37EEg@j%Oq(}fokRnwD z450`}7YH4s34$*^=R4e-{AEaF`wpKypRmB;o+T z-))jgBNuOfZ$B4rA3mAek^qQ?o&h;I;EG6+fh1M{GA=uj~Ml`T+!8{QZ3t@7(dU3-8h z{zoFI%UEPz8L7bZSc` zmSbApr)s;zbVXyiE1kDM91J%TuHXL{3(26dP%uz`P`Gd{IlLP2%DGe6u!Y^SZJG^T zO#?M;)PjBaIdLeSw|#OI>0G(pkwX`0+DM{cCEl_3F9Kd z2_jFPyfMp=0x4a%L}yYJ%E^6?&dHyena;(dM%XkHyFgLk8r$86Mx6YRqcW)ieM z=|3!Uv}IMUYPWzEoSWmEaT;cNSYs$=S5LUzr}7%c9BTBQ^g5$_AE7X4KG(yqSKQVp zjJmd6)oKm@fyu7dKMWwVt@|yFJoWrBTCEQNDUEGbo5iP2Da^&h$oPkm5g&5yZraSA z9M_R-8k&mP@2>}*jYZiy;*s*(4d*6_^M0H6;=P86GaPSDZGY(-woW0Vx>oiPe1nKJ z4|63I)rkT=zuur8fU&ecKe&P{g!`gO&m*M)QO0F^v0gaU<#BTP!CXU~3bRZ8yK2Z`AD5xc45F}G2BIHMvH>Z_#CNLhhz$7ziXAQs#(o|Xo!Tac-6th3m z?#2*P6UsE4hlJhbGG-KTh8&kaQ|MFOwvd;qfXsw0ApQbQDs)HORxzO_MKvBIn}{Ol z$(!irS!7mJK&T0S7K5X0xXAjcfa8Ln@z*;k&FuWZH7Z$+Hlali5(9%C{}|E~^Tj)g ze$C3S-9-0-YQ6_IMu=U!J+6A`tJd|^%p!=K;WZ3;I_03WRN=(-+*%sP=nBu245}3R zS*7ywr0k8InYuu<^}T>w>b?m9OvCT1tTNS9vo-&8i3(hz_ktbvF9YuCdO1HbG6t#M z8%+h7Q|7e9B=SGVh1H~_h345Scz8r*sDF6!=~q6%?P<_g#w7&x%(jVdqi^7|wYLQ* zT@LQbgiNe8Eq5>+!L+H{toNQOE1tIQD_9KbJm!{*UgTRXa{QeWRAS~gWJbEMVzj{l zH1_fwCDPw z1!TsP)XQ8K;6`{F8_w1%_zfeZ)R#G15NgPh@g}@tZFtW>PklG38WCf zdcv^}EOr2uEeCoj8v}jHsE2y9L8aI7=dF-IN)y#n;y$FxVB4_p>O_EdJ4D%~l`8BcNZ)d(zc3q; z%$_vxd@Q8`=fh-&6K9-BgUbr03OINY)k5(ky@6OjfXcj^`An`SEeA(mWEtcxq=v*5 zune%raM9ygrACPGJ{NMM`Oro6K0;|J_wh*?GwQO*GUqAv2(O#fwRRD{Y%egOCABX~ zTM$I%jgtUVHIxZ{sIWU6s9>EsntuB&y*qaR`Z(9b0-C&h_U%2TOe&Zb76e7k++4q= z!(E+=&^s(vQa+vgz_N{VqvZr~l(JyY{j}JG)7(6-EE$6o$4n_^Wnqb#7#{FX>WP z`k(S6tx$J(t@0v;em0s8%mbEXn?7b_li{?B{v7xrf)}JC-2X^>P>t)Z7tmrt#)^;ov=q#(aKYze(8Wm>V?nma4bs^S+2rhGEy0FJl zHHdOeHe_Hohd?`ET|`(!cCH-e{L*?7iP=sI_2K)Kw4pXbQ%ZKNLN*Kg7a+a-AbB{DKD@7HwZ%4DUw8boC?v{X#YQPoYnqEYN* zt9^By&higmQgfFYl9Ec!0M}|%s4TkZ;qTcl_E=SI(w*ZwX?*Ae^^aR`XrE<5X4u3= zM@c&CM$y_HBRkhKV$sRt(!ZT(X>Y>(A?N&3hQ_yiG^VmC~xuuI5>hMgu0H@r=)O@{ELH1B6rE zoQ|8;+)_f?FSI#cJKR#%4BZ2bIp|xd|G97#p!YQA1Vx=(D7(*kh*d$LlYzshRRgRV zlDeEqp74HKSL?uSJ*8j9IiJ#-wjgDWEJw@jf}k0;RhiBzy^M6}Zz-FVG zi^@I?ELEf%N7AdUlENj<(?u*Om6ZDk^^;u2 zJRHCzBQ-KilB1aIB`@@*vFwn3;QCE4rE(^5f)*vB{X`0 zF|e>yvdbHql{`u3knCrevDwQP1s${eKo2g2Mg(-v&Cv2OLw9#|jj14Oc-$+J8lce* z#usXw3J4;N4G|rl&o=hp+njGG&&dqAa9zrGf3NKNo?1R8sMk-I?JVzTd)FYnXa+Q$ zu*FsMw!T+bPuOoR2Nv|EOyGDfcU?t%({?*oAv%gGl$~t;S234>52WTRr!imm{_MGN zO{=Ix8PB1$7}Oz2oseky9*RF2HF&q16e2c{6XTRjU*vj>)&ImjOb}}5bMKI+Qu5R7 ztDoltd7!4>JJS0Ze~#olIV%6WmhR>uuYZ@1p>uf-!%CtcRGjx@JF6YS3yQ_*&Bs3K z5^!AhxHKJ_qsq#qA0SY!=9$d+ipbK@TK5YJ9~I6;a+c4AfoIwoaha;aN%tE)ldOBS z#%|YrEFbR<+tx$Ay`RLAEWV5RwQa!NKY{fNb;P!dR&gvEL5}L&t^y#&K)D}EN#nZx zlp9p|XPyd6c>w2JiKnSL9(Wmx`nexpgJo3+4yeI{rTlw{sVaF=2#7SZd?-~})WBGN zS)uevJyoopKj_wTu4T@{y6fyjrC$EUU1%$h}JhN)K(jT`}m65=;!JvoD&32)G1R)0?ppk7RJ-l7CkDKuP-VMsviIP zWi7OPj+$TRj35pBrd9A*pDn#<22J zUX#Ci)8k=*Qi8%3q4S2($+;}vu$(%Fk~rjiMmEyKwnj>fIE;;Bh5I$M~O;h7Dwzq z(GoW^vBH^RLOi#ash*f@XEXqB+L4))$!d_e>KQ5Q$jndPdL|N1HM-+UI6o;3(fCj@ z6vS%?52*)R7;Wm2FN|%sv`(kvp(KaJqm)yZ+49}Pf*eyx!FSUsr5>tLdF&dJ$Ihr) zVWQH9qR7(lnLd#ITBX(bi98amG&h=Ld7TcFLG`-G#BxTfVM9oqK$(;gO;mSm?^W0sCrJ$m!nUr+7C%-2HpX~MmKF^Q^yroQ0ppanQz(ec$%*5u{~Qw%9JJ> z8x%71%9o=V2m20xg^&TmtHx+m;g={;ww{A)o4smUZTt@VNe>b`*CInNq!alEGoyx& z$mGq`Iak}{7o_#fRgNBEQB#Yi9LOo)_Ha%O=9qUz?roy)Qz>i1E%752&|Ei`)i5 z=jioTRt)O*Bzdt7*ywZh%!P<>cDk?YBpd8|5m0GE7N`Cq&r)fNa{Bc z1ICw(qG!*URXf3-ra}}W;Yxs|FT}uXR0-SAHk`NHUH5(h)%i#KOTUU=e|WRKNcSIm zwOO6wbw*9>Cst26zq)w&6Gu+`zY%96duls}g+kl>qHIY;0y=%X3%9L6&5W0vhtsj4 z-8z3{T3SMtEIzoD!V);%8_SJ1Whzzg;mU;nsD(7_Nb3|;UH7PVI+XomDqgvvuFJ^( zX>Fe`Mow5MdBt?P2Dsr7yFuv&`|9*^Iq~Ty?>tX^h@FVlcugvjl{Timb3Qi1QpZWE z{D*YIoidD8j6-wzL7>LlmeJ)jqS`z6%x@?QB$Q!Q+xe z?At@LH$viL+PXX%)ZA4Q(-xh8rCL96xZBRp`mRu=nv8|_(|N{x3{k4ReS{f#&%Wk? zVVKlatmU$pe}n2?Y-$3lFH#rEvLTY*{WdOnc?M_L;vj67oTMTb?QX=w*>${avM}`l z-iCT8?3kOG;DtH>T;;YqJ!)qx z+fFho)axoI<+%-@VJ9eb%#QXxtkN@snz^WGrWawdPKxvV=5td$wXT*B{J?Z}Zcj|= z3Nj$0`6}eaB_mDS-}V}mSnNM?)AR9_`i-P*su(SJ1c!adpGC%n^>$68Ur0(uS~;NB z%wWpeD6MoFvkks5{CK<~-DKs);nKUT*+{H(P045Mul_O#F%Mn^Vczb7N40lYYK)J7 z=IR2=N@pheD63~k|8sM?1die?vg=E2`?;K6Bm-pa zZW1McLykt$pO^g&6A@{^7bT)wIZG;PkTV?aMo*fae4ui;VJX~9_+>!vOm0k4a}x%* zLq>3r#eeS$th&L~H`l&y&I;3T)v5+PbZ%IRv9?q~A>Wihm*Pzva@|V8pY?MJ3me|b zGjY6ji4L8fdu2tr*6vHq+AwqWA(@HI zq2uW6QR~aOnG$Vm0GWc>yj9(U+ovOWs-bZ&2sA!!ny&^bX%+~Ce$fl}96t#TirF2-XvMz(tBQw$8{@6JgB8L* z{0ijEooD6P?W`w-V3x4x3caDkxgtk#c0WVCOd8OwC~NHbxw&|f$hv0I@b{tzARol( zE&}3*{ngG`tF=Inm*Ib*I-H&|K*ftyEFV

    J66l>z7T20v&BrMUK^shcTlD$)2!& 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

    hPAkbbTr(^R-tMK2 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%0DL;n=L?~l?ng&I8MPm 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 z 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%0D>0jKMROl@?0Zb`>KdeJYDW6roP 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 NZ`_ZLr78E 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 +`./tests/api/regression_tests.sh ` You can view the logs for Test execution & the Multi-model-server in the /tmp dir. diff --git a/test/postman/environment.json b/tests/api/postman/environment.json similarity index 100% rename from test/postman/environment.json rename to tests/api/postman/environment.json diff --git a/test/postman/https_test_collection.json b/tests/api/postman/https_test_collection.json similarity index 100% rename from test/postman/https_test_collection.json rename to tests/api/postman/https_test_collection.json diff --git a/test/postman/inference_api_test_collection.json b/tests/api/postman/inference_api_test_collection.json similarity index 100% rename from test/postman/inference_api_test_collection.json rename to tests/api/postman/inference_api_test_collection.json diff --git a/test/postman/inference_data.json b/tests/api/postman/inference_data.json similarity index 95% rename from test/postman/inference_data.json rename to tests/api/postman/inference_data.json index b4eb7fb8a..f71b38028 100644 --- a/test/postman/inference_data.json +++ b/tests/api/postman/inference_data.json @@ -4,7 +4,7 @@ "model_name":"alexnet", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -36,7 +36,7 @@ "model_name":"caffenet", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -68,7 +68,7 @@ "model_name":"inception_v1", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -100,7 +100,7 @@ "model_name":"inception-bn", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -132,7 +132,7 @@ "model_name":"mobilenet", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -164,7 +164,7 @@ "model_name":"nin", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -196,7 +196,7 @@ "model_name":"resnet-152", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -228,7 +228,7 @@ "model_name":"resnet-18", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -260,7 +260,7 @@ "model_name":"resnext101", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -292,7 +292,7 @@ "model_name":"resnet18-v1", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -324,7 +324,7 @@ "model_name":"resnet34-v1", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -356,7 +356,7 @@ "model_name":"resnet50-v1", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -388,7 +388,7 @@ "model_name":"resnet101-v1", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -420,7 +420,7 @@ "model_name":"resnet152-v1", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -452,7 +452,7 @@ "model_name":"resnet18-v2", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -484,7 +484,7 @@ "model_name":"resnet34-v2", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -516,7 +516,7 @@ "model_name":"resnet50-v2", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -548,7 +548,7 @@ "model_name":"resnet101-v2", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -580,7 +580,7 @@ "model_name":"resnet152-v2", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -612,7 +612,7 @@ "model_name":"shufflenet", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -644,7 +644,7 @@ "model_name":"onnx-squeezenet", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -676,7 +676,7 @@ "model_name":"squeezenet_v1.1", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -708,7 +708,7 @@ "model_name":"vgg16", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -740,7 +740,7 @@ "model_name":"onnx-vgg16", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -772,7 +772,7 @@ "model_name":"onnx-vgg16_bn", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -804,7 +804,7 @@ "model_name":"vgg19", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -836,7 +836,7 @@ "model_name":"onnx-vgg19", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -868,7 +868,7 @@ "model_name":"onnx-vgg19_bn", "worker":1, "synchronous":"true", - "file":"test/resources/kitten.jpg", + "file":"tests/api/resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ diff --git a/test/postman/management_api_test_collection.json b/tests/api/postman/management_api_test_collection.json similarity index 100% rename from test/postman/management_api_test_collection.json rename to tests/api/postman/management_api_test_collection.json diff --git a/test/regression_tests.sh b/tests/api/regression_tests.sh old mode 100644 new mode 100755 similarity index 76% rename from test/regression_tests.sh rename to tests/api/regression_tests.sh index 8825ee6ad..1fc112be9 --- a/test/regression_tests.sh +++ b/tests/api/regression_tests.sh @@ -48,7 +48,7 @@ stop_mms_serve() { start_secure_mms() { # Start MMS with Model Store - multi-model-server --start --mms-config test/resources/config.properties --model-store $1 &>> $2 + multi-model-server --start --mms-config tests/api/resources/config.properties --model-store $1 &>> $2 sleep 10 curl --insecure -X GET https://127.0.0.1:8444/models } @@ -62,20 +62,20 @@ run_postman_test() { # Run Management API Tests stop_mms_serve start_mms $MODEL_STORE $MMS_LOG_FILE - newman run -e test/postman/environment.json --bail --verbose test/postman/management_api_test_collection.json \ + newman run -e tests/api/postman/environment.json --bail --verbose tests/api/postman/management_api_test_collection.json \ -r cli,html --reporter-html-export $ROOT_DIR/report/management_report.html >>$1 2>&1 # Run Inference API Tests after Restart stop_mms_serve start_mms $MODEL_STORE $MMS_LOG_FILE - newman run -e test/postman/environment.json --bail --verbose test/postman/inference_api_test_collection.json \ - -d test/postman/inference_data.json -r cli,html --reporter-html-export $ROOT_DIR/report/inference_report.html >>$1 2>&1 + newman run -e tests/api/postman/environment.json --bail --verbose tests/api/postman/inference_api_test_collection.json \ + -d tests/api/postman/inference_data.json -r cli,html --reporter-html-export $ROOT_DIR/report/inference_report.html >>$1 2>&1 # Run Https test cases stop_mms_serve start_secure_mms $MODEL_STORE $MMS_LOG_FILE - newman run --insecure -e test/postman/environment.json --bail --verbose test/postman/https_test_collection.json \ + newman run --insecure -e tests/api/postman/environment.json --bail --verbose tests/api/postman/https_test_collection.json \ -r cli,html --reporter-html-export $ROOT_DIR/report/MMS_https_test_report.html >>$1 2>&1 stop_mms_serve diff --git a/test/resources/certs.pem b/tests/api/resources/certs.pem similarity index 100% rename from test/resources/certs.pem rename to tests/api/resources/certs.pem diff --git a/tests/api/resources/config.properties b/tests/api/resources/config.properties new file mode 100644 index 000000000..6cb3b3299 --- /dev/null +++ b/tests/api/resources/config.properties @@ -0,0 +1,4 @@ +inference_address=https://127.0.0.1:8443 +management_address=https://127.0.0.1:8444 +private_key_file=tests/api/resources/key.pem +certificate_file=tests/api/resources/certs.pem diff --git a/test/resources/key.pem b/tests/api/resources/key.pem similarity index 100% rename from test/resources/key.pem rename to tests/api/resources/key.pem diff --git a/test/screenshot/postman.png b/tests/api/screenshot/postman.png similarity index 100% rename from test/screenshot/postman.png rename to tests/api/screenshot/postman.png From 3052a6e089c67b3157cf231602cd46821640cd7e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 21:17:25 +0530 Subject: [PATCH 286/329] updated config.yml for newman suite movement --- .circleci/config.yml | 85 ++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a9bb7e8c5..025521ec9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -45,7 +45,7 @@ commands: path: mms_<< parameters.collection >>.log - store_artifacts: name: Store << parameters.collection >> API test results - path: test/<< parameters.collection >>-api-report.html + path: tests/api/<< parameters.collection >>-api-report.html jobs: @@ -163,37 +163,44 @@ workflows: matrix: &matrix parameters: executor: ["py27", "py36"] - - &modelarchiver - modelarchiver: - name: modelarchiver-<< matrix.executor >> - matrix: *matrix - - &python-tests - python-tests: - name: python-tests-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix - - nightly: - triggers: - - schedule: - cron: "0 0 * * *" - filters: - branches: - only: - - master - jobs: - - *build - - *modelarchiver - - *python-tests - - &api-tests - api-tests: +# - &modelarchiver +# modelarchiver: +# name: modelarchiver-<< matrix.executor >> +# matrix: *matrix +# - &python-tests +# python-tests: +# name: python-tests-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix + ############ Remove after Dev ############ + - api-tests: name: api-tests-<< matrix.executor >> requires: - build-<< matrix.executor >> matrix: *matrix + ########################################## - weekly: +# nightly: +# triggers: +# - schedule: +# cron: "0 0 * * *" +# filters: +# branches: +# only: +# - master +# jobs: +# - *build +# - *modelarchiver +# - *python-tests +# - &api-tests +# api-tests: +# name: api-tests-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix + +# weekly: # triggers: # - schedule: # cron: "0 0 * * 0" @@ -201,15 +208,15 @@ workflows: # branches: # only: # - master - jobs: - - *build - - benchmark: - name: benchmark-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix - - performance-regression: - name: performance-regression-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix +# jobs: +# - *build +# - benchmark: +# name: benchmark-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix +# - performance-regression: +# name: performance-regression-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix From 3a1fcc8bb5c1bb441f77d872e31eab1ed5d595ca Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 21:39:35 +0530 Subject: [PATCH 287/329] tests/api path correction --- .circleci/scripts/linux_test_api.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/scripts/linux_test_api.sh b/.circleci/scripts/linux_test_api.sh index a9aeba588..28d49f25d 100755 --- a/.circleci/scripts/linux_test_api.sh +++ b/.circleci/scripts/linux_test_api.sh @@ -1,11 +1,11 @@ #!/bin/bash -MODEL_STORE_DIR='test/model_store' +MODEL_STORE_DIR='tests/api/model_store' MMS_LOG_FILE_MANAGEMENT='mms_management.log' MMS_LOG_FILE_INFERENCE='mms_inference.log' MMS_LOG_FILE_HTTPS='mms_https.log' -MMS_CONFIG_FILE_HTTPS='test/resources/config.properties' +MMS_CONFIG_FILE_HTTPS='tests/api/resources/config.properties' POSTMAN_ENV_FILE='tests/api/postman/environment.json' POSTMAN_COLLECTION_MANAGEMENT='tests/api/postman/management_api_test_collection.json' @@ -13,9 +13,9 @@ POSTMAN_COLLECTION_INFERENCE='tests/api/postman/inference_api_test_collection.js POSTMAN_COLLECTION_HTTPS='tests/api/postman/https_test_collection.json' POSTMAN_DATA_FILE_INFERENCE='tests/api/postman/inference_data.json' -REPORT_FILE_MANAGEMENT='test/management-api-report.html' -REPORT_FILE_INFERENCE='test/inference-api-report.html' -REPORT_FILE_HTTPS='test/https-api-report.html' +REPORT_FILE_MANAGEMENT='tests/api/management-api-report.html' +REPORT_FILE_INFERENCE='tests/api/inference-api-report.html' +REPORT_FILE_HTTPS='tests/api/https-api-report.html' start_mms_server() { multi-model-server --start --model-store $1 >> $2 2>&1 From 44362d1de0f46df50d9dfa0bcfab79ef69b346d3 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Tue, 30 Jun 2020 22:14:02 +0530 Subject: [PATCH 288/329] added ignored file --- tests/api/resources/kitten.jpg | Bin 0 -> 110969 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/api/resources/kitten.jpg diff --git a/tests/api/resources/kitten.jpg b/tests/api/resources/kitten.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ffcd2be2c674fddc4886117c12f3b29e4367aba8 GIT binary patch literal 110969 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

    J66l>z7T20v&BrMUK^shcTlD$)2!& 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

    hPAkbbTr(^R-tMK2 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%0DL;n=L?~l?ng&I8MPm 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 z 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%0D>0jKMROl@?0Zb`>KdeJYDW6roP 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 NZ`_ZLr78E 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 Date: Wed, 1 Jul 2020 12:03:53 +0530 Subject: [PATCH 289/329] [skip ci] readme udpated for performance-regression suite --- .circleci/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/README.md b/.circleci/README.md index 1d5022579..20483a0d6 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -16,6 +16,7 @@ Following _jobs_ are executed under each workflow - 3. **python-tests** : Executes pytests from _mms/tests/unit_tests/_ 4. **benchmark** : Executes latency benchmark using resnet-18 model 5. (NEW!) **api-tests** : Executes newman test suite for API testing +6. (NEW!) **performance-regression** : Executes performance regression suite Following _executors_ are available for job execution - 1. py27 From 7c3e6d838cde427aaf806f0b70435432d7faa610 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 1 Jul 2020 15:03:38 +0530 Subject: [PATCH 290/329] shell scripts exit with error on main command fail save all mms logs --- .circleci/config.yml | 92 ++++++++++--------- .circleci/scripts/linux_test_api.sh | 62 +++++++++---- .circleci/scripts/linux_test_benchmark.sh | 14 ++- .circleci/scripts/linux_test_modelarchiver.sh | 11 ++- .../scripts/linux_test_perf_regression.sh | 2 +- .circleci/scripts/linux_test_python.sh | 9 +- 6 files changed, 122 insertions(+), 68 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 025521ec9..946df0368 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,12 +40,6 @@ commands: - run: name: Start MMS, Execute << parameters.collection >> API Tests, Stop MMS command: .circleci/scripts/linux_test_api.sh << parameters.collection >> - - store_artifacts: - name: Store server logs from << parameters.collection >> API tests - path: mms_<< parameters.collection >>.log - - store_artifacts: - name: Store << parameters.collection >> API test results - path: tests/api/<< parameters.collection >>-api-report.html jobs: @@ -95,6 +89,9 @@ jobs: collection: inference - exeucute-api-tests: collection: https + - store_artifacts: + name: Store server logs and test results + path: tests/api/artifacts/ benchmark: parameters: @@ -109,7 +106,7 @@ jobs: command: .circleci/scripts/linux_test_benchmark.sh - store_artifacts: name: Store server logs from benchmark tests - path: mms.log + path: logs/ - store_artifacts: name: Store Benchmark Latency resnet-18 results path: /tmp/MMSBenchmark/out/latency/resnet-18/report/ @@ -174,6 +171,11 @@ workflows: # - build-<< matrix.executor >> # matrix: *matrix ############ Remove after Dev ############ + - benchmark: + name: benchmark-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix - api-tests: name: api-tests-<< matrix.executor >> requires: @@ -181,42 +183,42 @@ workflows: matrix: *matrix ########################################## -# nightly: -# triggers: -# - schedule: -# cron: "0 0 * * *" -# filters: -# branches: -# only: -# - master -# jobs: -# - *build -# - *modelarchiver -# - *python-tests -# - &api-tests -# api-tests: -# name: api-tests-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix + nightly: + triggers: + - schedule: + cron: "0 0 * * *" + filters: + branches: + only: + - master + jobs: + - *build + - *modelarchiver + - *python-tests + - &api-tests + api-tests: + name: api-tests-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix -# weekly: -# triggers: -# - schedule: -# cron: "0 0 * * 0" -# filters: -# branches: -# only: -# - master -# jobs: -# - *build -# - benchmark: -# name: benchmark-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix -# - performance-regression: -# name: performance-regression-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix + weekly: + triggers: + - schedule: + cron: "0 0 * * 0" + filters: + branches: + only: + - master + jobs: + - *build + - benchmark: + name: benchmark-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix + - performance-regression: + name: performance-regression-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix diff --git a/.circleci/scripts/linux_test_api.sh b/.circleci/scripts/linux_test_api.sh index 28d49f25d..bed13d43b 100755 --- a/.circleci/scripts/linux_test_api.sh +++ b/.circleci/scripts/linux_test_api.sh @@ -1,21 +1,25 @@ #!/bin/bash -MODEL_STORE_DIR='tests/api/model_store' +BASE_DIR="tests/api" +MODEL_STORE_DIR="$BASE_DIR/model_store" -MMS_LOG_FILE_MANAGEMENT='mms_management.log' -MMS_LOG_FILE_INFERENCE='mms_inference.log' -MMS_LOG_FILE_HTTPS='mms_https.log' -MMS_CONFIG_FILE_HTTPS='tests/api/resources/config.properties' +ARTIFACTS_BASE_DIR="$BASE_DIR/artifacts" +MANAGEMENT_API_ARTIFACTS_DIR="$ARTIFACTS_BASE_DIR/management" +INFERENCE_API_ARTIFACTS_DIR="$ARTIFACTS_BASE_DIR/inference" +HTTPS_API_ARTIFACTS_DIR="$ARTIFACTS_BASE_DIR/https" -POSTMAN_ENV_FILE='tests/api/postman/environment.json' -POSTMAN_COLLECTION_MANAGEMENT='tests/api/postman/management_api_test_collection.json' -POSTMAN_COLLECTION_INFERENCE='tests/api/postman/inference_api_test_collection.json' -POSTMAN_COLLECTION_HTTPS='tests/api/postman/https_test_collection.json' -POSTMAN_DATA_FILE_INFERENCE='tests/api/postman/inference_data.json' +MMS_CONSOLE_LOG_FILE="mms_console.log" +MMS_CONFIG_FILE_HTTPS="$BASE_DIR/resources/config.properties" -REPORT_FILE_MANAGEMENT='tests/api/management-api-report.html' -REPORT_FILE_INFERENCE='tests/api/inference-api-report.html' -REPORT_FILE_HTTPS='tests/api/https-api-report.html' +POSTMAN_ENV_FILE="$BASE_DIR/postman/environment.json" +POSTMAN_COLLECTION_MANAGEMENT="$BASE_DIR/postman/management_api_test_collection.json" +POSTMAN_COLLECTION_INFERENCE="$BASE_DIR/postman/inference_api_test_collection.json" +POSTMAN_COLLECTION_HTTPS="$BASE_DIR/postman/https_test_collection.json" +POSTMAN_DATA_FILE_INFERENCE="$BASE_DIR/postman/inference_data.json" + +REPORT_FILE_MANAGEMENT="$MANAGEMENT_API_ARTIFACTS_DIR/management-api-report.html" +REPORT_FILE_INFERENCE="$INFERENCE_API_ARTIFACTS_DIR/inference-api-report.html" +REPORT_FILE_HTTPS="$HTTPS_API_ARTIFACTS_DIR/https-api-report.html" start_mms_server() { multi-model-server --start --model-store $1 >> $2 2>&1 @@ -31,43 +35,67 @@ stop_mms_server() { multi-model-server --stop } +move_logs(){ + mv $1 logs/ + mv logs/ $2 +} + trigger_management_tests(){ - start_mms_server $MODEL_STORE_DIR $MMS_LOG_FILE_MANAGEMENT + start_mms_server $MODEL_STORE_DIR $MMS_CONSOLE_LOG_FILE newman run -e $POSTMAN_ENV_FILE $POSTMAN_COLLECTION_MANAGEMENT \ -r cli,html --reporter-html-export $REPORT_FILE_MANAGEMENT --verbose + local EXIT_CODE=$? stop_mms_server + move_logs $MMS_CONSOLE_LOG_FILE $MANAGEMENT_API_ARTIFACTS_DIR + return $EXIT_CODE } trigger_inference_tests(){ - start_mms_server $MODEL_STORE_DIR $MMS_LOG_FILE_INFERENCE + start_mms_server $MODEL_STORE_DIR $MMS_CONSOLE_LOG_FILE newman run -e $POSTMAN_ENV_FILE $POSTMAN_COLLECTION_INFERENCE -d $POSTMAN_DATA_FILE_INFERENCE \ -r cli,html --reporter-html-export $REPORT_FILE_INFERENCE --verbose + local EXIT_CODE=$? stop_mms_server + move_logs $MMS_CONSOLE_LOG_FILE $INFERENCE_API_ARTIFACTS_DIR + return $EXIT_CODE } trigger_https_tests(){ - start_mms_secure_server $MODEL_STORE_DIR $MMS_LOG_FILE_HTTPS + start_mms_secure_server $MODEL_STORE_DIR $MMS_CONSOLE_LOG_FILE newman run --insecure -e $POSTMAN_ENV_FILE $POSTMAN_COLLECTION_HTTPS \ -r cli,html --reporter-html-export $REPORT_FILE_HTTPS --verbose + local EXIT_CODE=$? stop_mms_server + move_logs $MMS_CONSOLE_LOG_FILE $HTTPS_API_ARTIFACTS_DIR + return $EXIT_CODE } -mkdir -p $MODEL_STORE_DIR +mkdir -p $MODEL_STORE_DIR $MANAGEMENT_API_ARTIFACTS_DIR $INFERENCE_API_ARTIFACTS_DIR $HTTPS_API_ARTIFACTS_DIR case $1 in 'management') trigger_management_tests + exit $? ;; 'inference') trigger_inference_tests + exit $? ;; 'https') trigger_https_tests + exit $? ;; 'ALL') trigger_management_tests + MGMT_EXIT_CODE=$? trigger_inference_tests + INFR_EXIT_CODE=$? trigger_https_tests + HTTPS_EXIT_CODE=$? + # If any one of the tests fail, exit with error + if [ "$MGMT_EXIT_CODE" -ne 0 ] || [ "$INFR_EXIT_CODE" -ne 0 ] || [ "$HTTPS_EXIT_CODE" -ne 0 ] + then exit 1 + fi ;; *) echo $1 'Invalid' diff --git a/.circleci/scripts/linux_test_benchmark.sh b/.circleci/scripts/linux_test_benchmark.sh index 73c66f58e..d4f45832f 100755 --- a/.circleci/scripts/linux_test_benchmark.sh +++ b/.circleci/scripts/linux_test_benchmark.sh @@ -5,10 +5,20 @@ mkdir -p /home/ubuntu/.linuxbrew/Cellar/jmeter/5.3/libexec/bin/ ln -s /opt/apache-jmeter-5.3/bin/jmeter /home/ubuntu/.linuxbrew/Cellar/jmeter/5.3/libexec/bin/jmeter -multi-model-server --start >> mms.log 2>&1 +# Start MMS and redirect console ouptut and errors to a log file +multi-model-server --start >> mms_console.log 2>&1 sleep 30 cd benchmarks python benchmark.py latency +EXIT_CODE=$? -multi-model-server --stop \ No newline at end of file +multi-model-server --stop + +# Moving MMS console log file to logs directory +# Just a convenience for CircleCI to pick up logs from one directory +cd ../ +mv mms_console.log logs/ + +# Exit with the same error code as that of benchmark script +exit $EXIT_CODE \ No newline at end of file diff --git a/.circleci/scripts/linux_test_modelarchiver.sh b/.circleci/scripts/linux_test_modelarchiver.sh index bdae61a65..f37c4b55e 100755 --- a/.circleci/scripts/linux_test_modelarchiver.sh +++ b/.circleci/scripts/linux_test_modelarchiver.sh @@ -4,14 +4,21 @@ cd model-archiver/ # Lint test pylint -rn --rcfile=./model_archiver/tests/pylintrc model_archiver/. +PY_LINT_EXIT_CODE=$? # Execute python unit tests python -m pytest --cov-report html:results_units --cov=./ model_archiver/tests/unit_tests +PY_UNITS_EXIT_CODE=$? # Install model archiver module pip install . # Execute integration tests -python -m pytest model_archiver/tests/integ_tests -# ToDo - Report for Integration tests ? \ No newline at end of file +python -m pytest model_archiver/tests/integ_tests # ToDo - Report for Integration tests ? +PY_INTEG_EXIT_CODE=$? + +# If any one of the tests fail, exit with error +if [ "$PY_LINT_EXIT_CODE" -ne 0 ] || [ "$PY_UNITS_EXIT_CODE" -ne 0 ] || [ "$PY_INTEG_EXIT_CODE" -ne 0 ] +then exit 1 +fi \ No newline at end of file diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 5d3766dce..5b7eacad5 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -28,4 +28,4 @@ mkdir -p $RESULT_DIR cp $ARTIFACTS_DIR/**/performance_results.xml $RESULT_DIR # Exit with the same error code as that of test execution -exit EXIT_CODE +exit $EXIT_CODE diff --git a/.circleci/scripts/linux_test_python.sh b/.circleci/scripts/linux_test_python.sh index 9af6d1e5b..fa50aee9b 100755 --- a/.circleci/scripts/linux_test_python.sh +++ b/.circleci/scripts/linux_test_python.sh @@ -2,6 +2,13 @@ # Lint Test pylint -rn --rcfile=./mms/tests/pylintrc mms/. +PY_LINT_EXIT_CODE=$? # Execute python tests -python -m pytest --cov-report html:htmlcov --cov=mms/ mms/tests/unit_tests/ \ No newline at end of file +python -m pytest --cov-report html:htmlcov --cov=mms/ mms/tests/unit_tests/ +PYTEST_EXIT_CODE=$? + +# If any one of the tests fail, exit with error +if [ "$PY_LINT_EXIT_CODE" -ne 0 ] || [ "$PYTEST_EXIT_CODE" -ne 0 ] +then exit 1 +fi \ No newline at end of file From 6c51dd91618644d6b0d8e390d6227d6f18a1f8d2 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 1 Jul 2020 15:08:45 +0530 Subject: [PATCH 291/329] corrected config.yml --- .circleci/config.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 946df0368..ed61fd08a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -160,16 +160,16 @@ workflows: matrix: &matrix parameters: executor: ["py27", "py36"] -# - &modelarchiver -# modelarchiver: -# name: modelarchiver-<< matrix.executor >> -# matrix: *matrix -# - &python-tests -# python-tests: -# name: python-tests-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix + - &modelarchiver + modelarchiver: + name: modelarchiver-<< matrix.executor >> + matrix: *matrix + - &python-tests + python-tests: + name: python-tests-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix ############ Remove after Dev ############ - benchmark: name: benchmark-<< matrix.executor >> From 13494786ad3924ae15ed6ba24a8336d0de9a655a Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 1 Jul 2020 15:08:58 +0530 Subject: [PATCH 292/329] exit on build fail --- .circleci/scripts/linux_test_modelarchiver.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.circleci/scripts/linux_test_modelarchiver.sh b/.circleci/scripts/linux_test_modelarchiver.sh index f37c4b55e..544a717a1 100755 --- a/.circleci/scripts/linux_test_modelarchiver.sh +++ b/.circleci/scripts/linux_test_modelarchiver.sh @@ -13,12 +13,13 @@ PY_UNITS_EXIT_CODE=$? # Install model archiver module pip install . +MODL_ARCHVR_EXIT_CODE=$? # Execute integration tests python -m pytest model_archiver/tests/integ_tests # ToDo - Report for Integration tests ? PY_INTEG_EXIT_CODE=$? -# If any one of the tests fail, exit with error -if [ "$PY_LINT_EXIT_CODE" -ne 0 ] || [ "$PY_UNITS_EXIT_CODE" -ne 0 ] || [ "$PY_INTEG_EXIT_CODE" -ne 0 ] +# If any one of the steps fail, exit with error +if [ "$PY_LINT_EXIT_CODE" -ne 0 ] || [ "$PY_UNITS_EXIT_CODE" -ne 0 ] || [ "$MODL_ARCHVR_EXIT_CODE" -ne 0 ] || [ "$PY_INTEG_EXIT_CODE" -ne 0 ] then exit 1 fi \ No newline at end of file From 5c33ca8e86cee9da2874c1ef52b64cad1bf0f88c Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 1 Jul 2020 15:37:27 +0530 Subject: [PATCH 293/329] proper names to pytest results directory reduced variables in api tests, simple report name --- .circleci/config.yml | 9 +++---- .circleci/scripts/linux_test_api.sh | 24 +++++++++---------- .circleci/scripts/linux_test_modelarchiver.sh | 2 +- .circleci/scripts/linux_test_python.sh | 2 +- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ed61fd08a..b1d471fd4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -69,11 +69,12 @@ jobs: steps: - attach-mms-workspace - run: - name: Execute python unit tests + name: Execute python lint and unit tests command: .circleci/scripts/linux_test_python.sh - store_artifacts: name: Store python Test results - path: htmlcov + path: result_units + destination: units api-tests: parameters: @@ -141,11 +142,11 @@ jobs: steps: - checkout - run: - name: Execute lint, unit and integration tests + name: Execute python lint, unit and integration tests command: .circleci/scripts/linux_test_modelarchiver.sh - store_artifacts: name: Store unit tests results from model archiver tests - path: model-archiver/results_units + path: model-archiver/result_units destination: units diff --git a/.circleci/scripts/linux_test_api.sh b/.circleci/scripts/linux_test_api.sh index bed13d43b..837fce735 100755 --- a/.circleci/scripts/linux_test_api.sh +++ b/.circleci/scripts/linux_test_api.sh @@ -4,9 +4,9 @@ BASE_DIR="tests/api" MODEL_STORE_DIR="$BASE_DIR/model_store" ARTIFACTS_BASE_DIR="$BASE_DIR/artifacts" -MANAGEMENT_API_ARTIFACTS_DIR="$ARTIFACTS_BASE_DIR/management" -INFERENCE_API_ARTIFACTS_DIR="$ARTIFACTS_BASE_DIR/inference" -HTTPS_API_ARTIFACTS_DIR="$ARTIFACTS_BASE_DIR/https" +ARTIFACTS_MANAGEMENT_DIR="$ARTIFACTS_BASE_DIR/management" +ARTIFACTS_INFERENCE_DIR="$ARTIFACTS_BASE_DIR/inference" +ARTIFACTS_HTTPS_DIR="$ARTIFACTS_BASE_DIR/https" MMS_CONSOLE_LOG_FILE="mms_console.log" MMS_CONFIG_FILE_HTTPS="$BASE_DIR/resources/config.properties" @@ -17,9 +17,7 @@ POSTMAN_COLLECTION_INFERENCE="$BASE_DIR/postman/inference_api_test_collection.js POSTMAN_COLLECTION_HTTPS="$BASE_DIR/postman/https_test_collection.json" POSTMAN_DATA_FILE_INFERENCE="$BASE_DIR/postman/inference_data.json" -REPORT_FILE_MANAGEMENT="$MANAGEMENT_API_ARTIFACTS_DIR/management-api-report.html" -REPORT_FILE_INFERENCE="$INFERENCE_API_ARTIFACTS_DIR/inference-api-report.html" -REPORT_FILE_HTTPS="$HTTPS_API_ARTIFACTS_DIR/https-api-report.html" +REPORT_FILE="report.html" start_mms_server() { multi-model-server --start --model-store $1 >> $2 2>&1 @@ -43,34 +41,34 @@ move_logs(){ trigger_management_tests(){ start_mms_server $MODEL_STORE_DIR $MMS_CONSOLE_LOG_FILE newman run -e $POSTMAN_ENV_FILE $POSTMAN_COLLECTION_MANAGEMENT \ - -r cli,html --reporter-html-export $REPORT_FILE_MANAGEMENT --verbose + -r cli,html --reporter-html-export $ARTIFACTS_MANAGEMENT_DIR/$REPORT_FILE --verbose local EXIT_CODE=$? stop_mms_server - move_logs $MMS_CONSOLE_LOG_FILE $MANAGEMENT_API_ARTIFACTS_DIR + move_logs $MMS_CONSOLE_LOG_FILE $ARTIFACTS_MANAGEMENT_DIR return $EXIT_CODE } trigger_inference_tests(){ start_mms_server $MODEL_STORE_DIR $MMS_CONSOLE_LOG_FILE newman run -e $POSTMAN_ENV_FILE $POSTMAN_COLLECTION_INFERENCE -d $POSTMAN_DATA_FILE_INFERENCE \ - -r cli,html --reporter-html-export $REPORT_FILE_INFERENCE --verbose + -r cli,html --reporter-html-export $ARTIFACTS_INFERENCE_DIR/$REPORT_FILE --verbose local EXIT_CODE=$? stop_mms_server - move_logs $MMS_CONSOLE_LOG_FILE $INFERENCE_API_ARTIFACTS_DIR + move_logs $MMS_CONSOLE_LOG_FILE $ARTIFACTS_INFERENCE_DIR return $EXIT_CODE } trigger_https_tests(){ start_mms_secure_server $MODEL_STORE_DIR $MMS_CONSOLE_LOG_FILE newman run --insecure -e $POSTMAN_ENV_FILE $POSTMAN_COLLECTION_HTTPS \ - -r cli,html --reporter-html-export $REPORT_FILE_HTTPS --verbose + -r cli,html --reporter-html-export $ARTIFACTS_HTTPS_DIR/$REPORT_FILE --verbose local EXIT_CODE=$? stop_mms_server - move_logs $MMS_CONSOLE_LOG_FILE $HTTPS_API_ARTIFACTS_DIR + move_logs $MMS_CONSOLE_LOG_FILE $ARTIFACTS_HTTPS_DIR return $EXIT_CODE } -mkdir -p $MODEL_STORE_DIR $MANAGEMENT_API_ARTIFACTS_DIR $INFERENCE_API_ARTIFACTS_DIR $HTTPS_API_ARTIFACTS_DIR +mkdir -p $MODEL_STORE_DIR $ARTIFACTS_MANAGEMENT_DIR $ARTIFACTS_INFERENCE_DIR $ARTIFACTS_HTTPS_DIR case $1 in 'management') diff --git a/.circleci/scripts/linux_test_modelarchiver.sh b/.circleci/scripts/linux_test_modelarchiver.sh index 544a717a1..4bd4eb252 100755 --- a/.circleci/scripts/linux_test_modelarchiver.sh +++ b/.circleci/scripts/linux_test_modelarchiver.sh @@ -7,7 +7,7 @@ pylint -rn --rcfile=./model_archiver/tests/pylintrc model_archiver/. PY_LINT_EXIT_CODE=$? # Execute python unit tests -python -m pytest --cov-report html:results_units --cov=./ model_archiver/tests/unit_tests +python -m pytest --cov-report html:result_units --cov=./ model_archiver/tests/unit_tests PY_UNITS_EXIT_CODE=$? diff --git a/.circleci/scripts/linux_test_python.sh b/.circleci/scripts/linux_test_python.sh index fa50aee9b..5bcedce95 100755 --- a/.circleci/scripts/linux_test_python.sh +++ b/.circleci/scripts/linux_test_python.sh @@ -5,7 +5,7 @@ pylint -rn --rcfile=./mms/tests/pylintrc mms/. PY_LINT_EXIT_CODE=$? # Execute python tests -python -m pytest --cov-report html:htmlcov --cov=mms/ mms/tests/unit_tests/ +python -m pytest --cov-report html:result_units --cov=mms/ mms/tests/unit_tests/ PYTEST_EXIT_CODE=$? # If any one of the tests fail, exit with error From 957b668c282784f7c33f45f1eb67d52c7961baa9 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 1 Jul 2020 15:54:30 +0530 Subject: [PATCH 294/329] [skip ci] removed dev block --- .circleci/config.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b1d471fd4..ab75c61c5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -171,18 +171,6 @@ workflows: requires: - build-<< matrix.executor >> matrix: *matrix - ############ Remove after Dev ############ - - benchmark: - name: benchmark-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix - - api-tests: - name: api-tests-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix - ########################################## nightly: triggers: From e972390aa6be073e855dc861af19bedf042ab74e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 1 Jul 2020 19:02:09 +0530 Subject: [PATCH 295/329] add scenario for unregister when inference is in progress --- .../custom_model_for_slow_inference.sh | 71 ++++++++++ .../environments/xlarge.yaml | 11 ++ .../unregister_while_inferencing.jmx | 132 ++++++++++++++++++ .../unregister_while_inferencing.yaml | 74 ++++++++++ 4 files changed, 288 insertions(+) create mode 100755 tests/performance/tests/unregister_while_inferencing/custom_model_for_slow_inference.sh create mode 100644 tests/performance/tests/unregister_while_inferencing/environments/xlarge.yaml create mode 100644 tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.jmx create mode 100644 tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.yaml diff --git a/tests/performance/tests/unregister_while_inferencing/custom_model_for_slow_inference.sh b/tests/performance/tests/unregister_while_inferencing/custom_model_for_slow_inference.sh new file mode 100755 index 000000000..abc1142cf --- /dev/null +++ b/tests/performance/tests/unregister_while_inferencing/custom_model_for_slow_inference.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +# 1. This script downloads a model(squeezenet_v1.1.mar) from model zoo +# 2. Extracts(unzip) the model +# 3. Modifies the handle function to have a 20 second sleep at start +# 4. Repackages the model + +BASE_DIR=$(pwd) +STORE_DIR="tmp_store" +MODEL_NAME="squeezenet_v1.1" +MODEL_URL="https://s3.amazonaws.com/model-server/model_archive_1.0/$MODEL_NAME.mar" +HANDLER_FILE_NAME="mxnet_vision_service" + +build(){ + # Create a temporary model store and a workspace inside it + mkdir -p $STORE_DIR/$MODEL_NAME + + # Move to workspace + cd $STORE_DIR/$MODEL_NAME + + # Download the model from model zoo + curl -s -O $MODEL_URL + + # Extract the model, Once extracted remove the downloaded .mar + unzip $MODEL_NAME.mar + rm $MODEL_NAME.mar + + #### Adds a 20 sec wait in the handle function #### + sed -i'' -e '/import logging/a\ + import time' $HANDLER_FILE_NAME.py + + sed -i'' -e '/def handle(data, context):/a\ + \ \ \ \ \time.sleep(20) + ' $HANDLER_FILE_NAME.py + ################################################## + + # Install model-archiver + pip -q install model-archiver > /dev/null 2>&1 + + # Move to model store + cd ../ + + # Create a new model using model archiver + model-archiver --model-name $MODEL_NAME --model-path ./$MODEL_NAME --handler $HANDLER_FILE_NAME:handle + + # Remove workspace + rm -rf $MODEL_NAME + + # Move back to base dir + cd $BASE_DIR +} + +clean(){ + rm -rf $STORE_DIR +} + +case $1 in + 'build') + build + exit $? + ;; + 'clean') + clean + exit $? + ;; + *) + echo $1 'Invalid' + echo 'Please specify any one of - build | clean' + exit 1 + ;; +esac diff --git a/tests/performance/tests/unregister_while_inferencing/environments/xlarge.yaml b/tests/performance/tests/unregister_while_inferencing/environments/xlarge.yaml new file mode 100644 index 000000000..f4c5be87b --- /dev/null +++ b/tests/performance/tests/unregister_while_inferencing/environments/xlarge.yaml @@ -0,0 +1,11 @@ +--- +settings: + env: + INFR_SUCC : 100% + UNREG_SUCC: 100% + INFR_RT : 22s + UNREG_RT: 25ms + MIN_TOTAL_PROCS : 1 + MAX_TOTAL_PROCS : 3 + TOTAL_FDS : 74 + FRNTEND_MEM : 250000000 #250MB \ No newline at end of file diff --git a/tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.jmx b/tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.jmx new file mode 100644 index 000000000..876424545 --- /dev/null +++ b/tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.jmx @@ -0,0 +1,132 @@ + + + + + + false + true + false + + + + + + + + + + model + squeezenet_v1.1 + = + Model Name + + + + + + + + + ${__P(hostname,127.0.0.1)} + ${__P(port,8443)} + ${__P(protocol,https)} + + + 6 + + + + + + continue + + false + ${__P(loops,4)} + + ${__P(threads,10)} + ${__P(rampup,10)} + false + + + true + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + + + + /predictions/${model} + POST + true + false + true + true + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + 5000 + + + + + + + ${__P(input_filepath)} + data + image/jpeg + + + + + + + + ${__P(management_port,8444)} + + + /models/${model} + DELETE + true + false + true + true + + + + + + + + + diff --git a/tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.yaml b/tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.yaml new file mode 100644 index 000000000..a934285bb --- /dev/null +++ b/tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.yaml @@ -0,0 +1,74 @@ +--- +execution: +- concurrency: 1 + ramp-up: 0 + iterations: 1 + scenario: unregister_while_inferencing + +scenarios: + unregister_while_inferencing: + script: unregister_while_inferencing.jmx + +modules: + server_local_monitoring: + class : metrics_monitoring_inproc.Monitor + +services: + - module: shellexec + prepare: + - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" + - "./tests/unregister_while_inferencing/custom_model_for_slow_inference.sh build" + - "multi-model-server --start --model-store=tmp_store > /dev/null 2>&1" + - "sleep 20s" + - "curl -s -X POST http://localhost:8081/models?url=squeezenet_v1.1.mar" + - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true" + post-process: + - "multi-model-server --stop > /dev/null 2>&1" + - "./tests/unregister_while_inferencing/custom_model_for_slow_inference.sh clean" + - "rm kitten.jpg" + - module: server_local_monitoring + ServerLocalClient: + - interval: 1s + logging : True + metrics: + - total_processes + - sum_all_file_descriptors + - sum_all_memory_rss + +reporting: +- module: passfail + criteria: + # Inbuilt Criteria + - success of Inference<${INFR_SUCC}, stop as failed + - success of Unregister<${UNREG_SUCC}, stop as failed + - avg-rt of Inference>${INFR_RT}, stop as failed + - avg-rt of Unregister>${UNREG_RT}, stop as failed + # Custom Criteria + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_processes + condition: '>' + threshold: ${MAX_TOTAL_PROCS} + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_processes + condition: '<' + threshold: ${MIN_TOTAL_PROCS} + timeframe: 1s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/sum_all_file_descriptors + condition: '>' + threshold: ${TOTAL_FDS} + timeframe: 1s + stop : true + fail : true +# - class: bzt.modules.monitoring.MonitoringCriteria +# subject: ServerLocalClient/sum_all_memory_rss +# condition: '>' +# threshold: ${FRNTEND_MEM} +# timeframe: 5s +# stop : true +# fail : true From 9b6e22fe78b218f9a904c7766c264838a8ce7e3a Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 1 Jul 2020 19:19:47 +0530 Subject: [PATCH 296/329] rename xlarge to macos-xlarge --- .circleci/scripts/linux_test_perf_regression.sh | 2 +- .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../list_models/environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 .../environments/{xlarge.yaml => macos-xlarge.yaml} | 0 18 files changed, 1 insertion(+), 1 deletion(-) rename tests/performance/tests/api_description/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/batch_and_single_inference/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/batch_inference/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/examples_local_criteria/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/examples_remote_criteria/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/health_check/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/inference_multiple_models/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/inference_multiple_worker/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/inference_single_worker/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/list_models/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/model_description/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/multiple_inference_and_scaling/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/register_unregister/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/register_unregister_multiple/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/scale_down_workers/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/scale_up_workers/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) rename tests/performance/tests/unregister_while_inferencing/environments/{xlarge.yaml => macos-xlarge.yaml} (100%) diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 5b7eacad5..5bc5d7442 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -20,7 +20,7 @@ pip install -r requirements.txt pip install bzt # Execute performance test suite and store exit code -./run_performance_suite.py -j $JMETER_PATH -e xlarge --no-compare-local +./run_performance_suite.py -j $JMETER_PATH -e macos-xlarge --no-compare-local EXIT_CODE=$? # Collect and store test results in result directory to be picked up by CircleCI diff --git a/tests/performance/tests/api_description/environments/xlarge.yaml b/tests/performance/tests/api_description/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/api_description/environments/xlarge.yaml rename to tests/performance/tests/api_description/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml b/tests/performance/tests/batch_and_single_inference/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml rename to tests/performance/tests/batch_and_single_inference/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/batch_inference/environments/xlarge.yaml b/tests/performance/tests/batch_inference/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/batch_inference/environments/xlarge.yaml rename to tests/performance/tests/batch_inference/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/examples_local_criteria/environments/xlarge.yaml b/tests/performance/tests/examples_local_criteria/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/examples_local_criteria/environments/xlarge.yaml rename to tests/performance/tests/examples_local_criteria/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/examples_remote_criteria/environments/xlarge.yaml b/tests/performance/tests/examples_remote_criteria/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/examples_remote_criteria/environments/xlarge.yaml rename to tests/performance/tests/examples_remote_criteria/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/health_check/environments/xlarge.yaml b/tests/performance/tests/health_check/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/health_check/environments/xlarge.yaml rename to tests/performance/tests/health_check/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/inference_multiple_models/environments/xlarge.yaml b/tests/performance/tests/inference_multiple_models/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_multiple_models/environments/xlarge.yaml rename to tests/performance/tests/inference_multiple_models/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml b/tests/performance/tests/inference_multiple_worker/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml rename to tests/performance/tests/inference_multiple_worker/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/inference_single_worker/environments/xlarge.yaml b/tests/performance/tests/inference_single_worker/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_single_worker/environments/xlarge.yaml rename to tests/performance/tests/inference_single_worker/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/list_models/environments/xlarge.yaml b/tests/performance/tests/list_models/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/list_models/environments/xlarge.yaml rename to tests/performance/tests/list_models/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/model_description/environments/xlarge.yaml b/tests/performance/tests/model_description/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/model_description/environments/xlarge.yaml rename to tests/performance/tests/model_description/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml rename to tests/performance/tests/multiple_inference_and_scaling/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/register_unregister/environments/xlarge.yaml b/tests/performance/tests/register_unregister/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/register_unregister/environments/xlarge.yaml rename to tests/performance/tests/register_unregister/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml b/tests/performance/tests/register_unregister_multiple/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml rename to tests/performance/tests/register_unregister_multiple/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/scale_down_workers/environments/xlarge.yaml b/tests/performance/tests/scale_down_workers/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/scale_down_workers/environments/xlarge.yaml rename to tests/performance/tests/scale_down_workers/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/scale_up_workers/environments/xlarge.yaml b/tests/performance/tests/scale_up_workers/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/scale_up_workers/environments/xlarge.yaml rename to tests/performance/tests/scale_up_workers/environments/macos-xlarge.yaml diff --git a/tests/performance/tests/unregister_while_inferencing/environments/xlarge.yaml b/tests/performance/tests/unregister_while_inferencing/environments/macos-xlarge.yaml similarity index 100% rename from tests/performance/tests/unregister_while_inferencing/environments/xlarge.yaml rename to tests/performance/tests/unregister_while_inferencing/environments/macos-xlarge.yaml From 452beff02409c6d49c64c2c8f76e2b2f507b2364 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 1 Jul 2020 19:20:58 +0530 Subject: [PATCH 297/329] Trigger all builds --- .circleci/config.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index ab75c61c5..f29af183f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -171,6 +171,24 @@ workflows: requires: - build-<< matrix.executor >> matrix: *matrix + ########### Remove after DEV ############### + - &api-tests + api-tests: + name: api-tests-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix + - benchmark: + name: benchmark-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix + - performance-regression: + name: performance-regression-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix + ############################################### nightly: triggers: From a8fdd33da5294d764f98cc2b38502816e360339b Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 1 Jul 2020 20:25:54 +0530 Subject: [PATCH 298/329] renamed to macos_xlarge --- .circleci/config.yml | 122 +++++++++--------- .../scripts/linux_test_perf_regression.sh | 2 +- .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 .../{macos-xlarge.yaml => macos_xlarge.yaml} | 0 19 files changed, 62 insertions(+), 62 deletions(-) rename tests/performance/tests/api_description/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/batch_and_single_inference/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/batch_inference/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/examples_local_criteria/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/examples_remote_criteria/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/health_check/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/inference_multiple_models/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/inference_multiple_worker/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/inference_single_worker/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/list_models/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/model_description/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/multiple_inference_and_scaling/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/register_unregister/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/register_unregister_multiple/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/scale_down_workers/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/scale_up_workers/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) rename tests/performance/tests/unregister_while_inferencing/environments/{macos-xlarge.yaml => macos_xlarge.yaml} (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index f29af183f..2d5d36d3f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -161,28 +161,28 @@ workflows: matrix: &matrix parameters: executor: ["py27", "py36"] - - &modelarchiver - modelarchiver: - name: modelarchiver-<< matrix.executor >> - matrix: *matrix - - &python-tests - python-tests: - name: python-tests-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix - ########### Remove after DEV ############### - - &api-tests - api-tests: - name: api-tests-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix - - benchmark: - name: benchmark-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix +# - &modelarchiver +# modelarchiver: +# name: modelarchiver-<< matrix.executor >> +# matrix: *matrix +# - &python-tests +# python-tests: +# name: python-tests-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix +# ########### Remove after DEV ############### +# - &api-tests +# api-tests: +# name: api-tests-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix +# - benchmark: +# name: benchmark-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix - performance-regression: name: performance-regression-<< matrix.executor >> requires: @@ -190,42 +190,42 @@ workflows: matrix: *matrix ############################################### - nightly: - triggers: - - schedule: - cron: "0 0 * * *" - filters: - branches: - only: - - master - jobs: - - *build - - *modelarchiver - - *python-tests - - &api-tests - api-tests: - name: api-tests-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix - - weekly: - triggers: - - schedule: - cron: "0 0 * * 0" - filters: - branches: - only: - - master - jobs: - - *build - - benchmark: - name: benchmark-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix - - performance-regression: - name: performance-regression-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix +# nightly: +# triggers: +# - schedule: +# cron: "0 0 * * *" +# filters: +# branches: +# only: +# - master +# jobs: +# - *build +# - *modelarchiver +# - *python-tests +# - &api-tests +# api-tests: +# name: api-tests-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix +# +# weekly: +# triggers: +# - schedule: +# cron: "0 0 * * 0" +# filters: +# branches: +# only: +# - master +# jobs: +# - *build +# - benchmark: +# name: benchmark-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix +# - performance-regression: +# name: performance-regression-<< matrix.executor >> +# requires: +# - build-<< matrix.executor >> +# matrix: *matrix diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 5bc5d7442..0a548b9d9 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -20,7 +20,7 @@ pip install -r requirements.txt pip install bzt # Execute performance test suite and store exit code -./run_performance_suite.py -j $JMETER_PATH -e macos-xlarge --no-compare-local +./run_performance_suite.py -j $JMETER_PATH -e macos_xlarge --no-compare-local EXIT_CODE=$? # Collect and store test results in result directory to be picked up by CircleCI diff --git a/tests/performance/tests/api_description/environments/macos-xlarge.yaml b/tests/performance/tests/api_description/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/api_description/environments/macos-xlarge.yaml rename to tests/performance/tests/api_description/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/batch_and_single_inference/environments/macos-xlarge.yaml b/tests/performance/tests/batch_and_single_inference/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/batch_and_single_inference/environments/macos-xlarge.yaml rename to tests/performance/tests/batch_and_single_inference/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/batch_inference/environments/macos-xlarge.yaml b/tests/performance/tests/batch_inference/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/batch_inference/environments/macos-xlarge.yaml rename to tests/performance/tests/batch_inference/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/examples_local_criteria/environments/macos-xlarge.yaml b/tests/performance/tests/examples_local_criteria/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/examples_local_criteria/environments/macos-xlarge.yaml rename to tests/performance/tests/examples_local_criteria/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/examples_remote_criteria/environments/macos-xlarge.yaml b/tests/performance/tests/examples_remote_criteria/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/examples_remote_criteria/environments/macos-xlarge.yaml rename to tests/performance/tests/examples_remote_criteria/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/health_check/environments/macos-xlarge.yaml b/tests/performance/tests/health_check/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/health_check/environments/macos-xlarge.yaml rename to tests/performance/tests/health_check/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/inference_multiple_models/environments/macos-xlarge.yaml b/tests/performance/tests/inference_multiple_models/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_multiple_models/environments/macos-xlarge.yaml rename to tests/performance/tests/inference_multiple_models/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/inference_multiple_worker/environments/macos-xlarge.yaml b/tests/performance/tests/inference_multiple_worker/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_multiple_worker/environments/macos-xlarge.yaml rename to tests/performance/tests/inference_multiple_worker/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/inference_single_worker/environments/macos-xlarge.yaml b/tests/performance/tests/inference_single_worker/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/inference_single_worker/environments/macos-xlarge.yaml rename to tests/performance/tests/inference_single_worker/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/list_models/environments/macos-xlarge.yaml b/tests/performance/tests/list_models/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/list_models/environments/macos-xlarge.yaml rename to tests/performance/tests/list_models/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/model_description/environments/macos-xlarge.yaml b/tests/performance/tests/model_description/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/model_description/environments/macos-xlarge.yaml rename to tests/performance/tests/model_description/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/macos-xlarge.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/multiple_inference_and_scaling/environments/macos-xlarge.yaml rename to tests/performance/tests/multiple_inference_and_scaling/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/register_unregister/environments/macos-xlarge.yaml b/tests/performance/tests/register_unregister/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/register_unregister/environments/macos-xlarge.yaml rename to tests/performance/tests/register_unregister/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/register_unregister_multiple/environments/macos-xlarge.yaml b/tests/performance/tests/register_unregister_multiple/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/register_unregister_multiple/environments/macos-xlarge.yaml rename to tests/performance/tests/register_unregister_multiple/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/scale_down_workers/environments/macos-xlarge.yaml b/tests/performance/tests/scale_down_workers/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/scale_down_workers/environments/macos-xlarge.yaml rename to tests/performance/tests/scale_down_workers/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/scale_up_workers/environments/macos-xlarge.yaml b/tests/performance/tests/scale_up_workers/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/scale_up_workers/environments/macos-xlarge.yaml rename to tests/performance/tests/scale_up_workers/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/unregister_while_inferencing/environments/macos-xlarge.yaml b/tests/performance/tests/unregister_while_inferencing/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/unregister_while_inferencing/environments/macos-xlarge.yaml rename to tests/performance/tests/unregister_while_inferencing/environments/macos_xlarge.yaml From 45bbff2a190390c52ccdd559808037c62771fde6 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 1 Jul 2020 22:01:31 +0530 Subject: [PATCH 299/329] ci_linux_medium environment --- .../environments/ci_linux_medium.yaml | 10 ++++++++++ .../environments/ci_linux_medium.yaml | 9 +++++++++ .../environments/ci_linux_medium.yaml | 8 ++++++++ .../environments/ci_linux_medium.yaml | 8 ++++++++ .../environments/ci_linux_medium.yaml | 7 +++++++ .../environments/ci_linux_medium.yaml | 8 ++++++++ .../environments/ci_linux_medium.yaml | 10 ++++++++++ .../environments/ci_linux_medium.yaml | 8 ++++++++ .../environments/ci_linux_medium.yaml | 8 ++++++++ .../list_models/environments/ci_linux_medium.yaml | 8 ++++++++ .../environments/ci_linux_medium.yaml | 8 ++++++++ .../environments/ci_linux_medium.yaml | 12 ++++++++++++ .../environments/ci_linux_medium.yaml | 11 +++++++++++ .../environments/ci_linux_medium.yaml | 12 ++++++++++++ .../environments/ci_linux_medium.yaml | 15 +++++++++++++++ .../environments/ci_linux_medium.yaml | 15 +++++++++++++++ .../environments/ci_linux_medium.yaml | 11 +++++++++++ 17 files changed, 168 insertions(+) create mode 100644 tests/performance/tests/api_description/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/examples_local_criteria/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/examples_remote_criteria/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/health_check/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/list_models/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/model_description/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/scale_up_workers/environments/ci_linux_medium.yaml create mode 100644 tests/performance/tests/unregister_while_inferencing/environments/ci_linux_medium.yaml diff --git a/tests/performance/tests/api_description/environments/ci_linux_medium.yaml b/tests/performance/tests/api_description/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..cd97694c1 --- /dev/null +++ b/tests/performance/tests/api_description/environments/ci_linux_medium.yaml @@ -0,0 +1,10 @@ +--- +settings: + env: + MGMT_DESC_SUCC: 100% + INFR_DESC_SUCC: 100% + MGMT_DESC_RT : 18ms + INFR_DESC_RT : 18ms + TOTAL_PROCS : 1 + TOTAL_FDS : 82 + TOTAL_MEM: 100000000 #100MB diff --git a/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml b/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..97307b690 --- /dev/null +++ b/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml @@ -0,0 +1,9 @@ +--- +settings: + env: + INFR1_RT : 6s + INFR2_RT : 0.08s + TOTAL_WORKERS_MEM : 4000000000 #4GB + TOTAL_WORKERS : 9 + TOTAL_ORPHANS : 0 + TOTAL_WORKERS_FDS : 78 diff --git a/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml b/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..23a443aaf --- /dev/null +++ b/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + INFR_RT : 1.5s + TOTAL_WORKERS_MEM : 3000000000 #3GB + TOTAL_WORKERS : 4 + TOTAL_ORPHANS : 0 + TOTAL_WORKERS_FDS : 38 \ No newline at end of file diff --git a/tests/performance/tests/examples_local_criteria/environments/ci_linux_medium.yaml b/tests/performance/tests/examples_local_criteria/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..6c3835292 --- /dev/null +++ b/tests/performance/tests/examples_local_criteria/environments/ci_linux_medium.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + FAIL : 100% + P90 : 290ms + AVG_RT : 1s + TOTAL_WORKERS_MEM : 132000000 + PERCENT_DIFF_TOTAL_WORKERS_MEM : 5 diff --git a/tests/performance/tests/examples_remote_criteria/environments/ci_linux_medium.yaml b/tests/performance/tests/examples_remote_criteria/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..674a6c1ff --- /dev/null +++ b/tests/performance/tests/examples_remote_criteria/environments/ci_linux_medium.yaml @@ -0,0 +1,7 @@ +--- +settings: + env: + FAIL : 50% + P90 : 250ms + AVG_RT : 1s + TOTAL_WORKERS_FDS : 80 diff --git a/tests/performance/tests/health_check/environments/ci_linux_medium.yaml b/tests/performance/tests/health_check/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..689a5d66b --- /dev/null +++ b/tests/performance/tests/health_check/environments/ci_linux_medium.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + HLTH_CHK_SUCC : 100% + HLTH_CHK_RT : 14ms + TOTAL_PROCS : 1 + TOTAL_FDS : 67 + TOTAL_MEM : 750000000 #750MB \ No newline at end of file diff --git a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..36b7dc0ad --- /dev/null +++ b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml @@ -0,0 +1,10 @@ +--- +settings: + env: + INFR1_SUCC : 100% + INFR2_SUCC: 100% + INFR1_RT : 290ms + INFR2_RT: 450ms + TOTAL_PROCS : 5 + TOTAL_FDS : 107 + TOTAL_MEM : 600000000 #600MB \ No newline at end of file diff --git a/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..5b9fd6c0a --- /dev/null +++ b/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + INFR_SUCC : 100% + INFR_RT : 140ms + TOTAL_PROCS : 6 + TOTAL_FDS : 126 + TOTAL_MEM : 750000000 #750MB \ No newline at end of file diff --git a/tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..c945e1f91 --- /dev/null +++ b/tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + INFR_SUCC : 100% + INFR_RT : 290ms + TOTAL_PROCS : 3 + TOTAL_FDS : 90 + TOTAL_MEM : 290000000 #290MB \ No newline at end of file diff --git a/tests/performance/tests/list_models/environments/ci_linux_medium.yaml b/tests/performance/tests/list_models/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..611624824 --- /dev/null +++ b/tests/performance/tests/list_models/environments/ci_linux_medium.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + LST_MODLS_SUCC : 100% + LST_MODLS_RT : 14ms + TOTAL_PROCS : 3 + TOTAL_FDS : 86 + TOTAL_MEM : 185000000 #185MB \ No newline at end of file diff --git a/tests/performance/tests/model_description/environments/ci_linux_medium.yaml b/tests/performance/tests/model_description/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..00e0aac87 --- /dev/null +++ b/tests/performance/tests/model_description/environments/ci_linux_medium.yaml @@ -0,0 +1,8 @@ +--- +settings: + env: + MODL_DESC_SUCC : 100% + MODL_DESC_RT : 14ms + TOTAL_PROCS : 3 + TOTAL_FDS : 90 + TOTAL_MEM : 300000000 #300MB \ No newline at end of file diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..1671213d1 --- /dev/null +++ b/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml @@ -0,0 +1,12 @@ +--- +settings: + env: + INFR1_SUCC : 100% + INFR2_SUCC: 100% + INFR1_RT : 290ms + INFR2_RT: 450ms + TOTAL_PROCS : 14 + TOTAL_FDS : 300 + TOTAL_MEM : 2000000000 #~2GB + TOTAL_ORPHANS : 0 + FRNTEND_MEM : 1000000000 #~1GB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml b/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..0e099afed --- /dev/null +++ b/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml @@ -0,0 +1,11 @@ +--- +settings: + env: + REG_SUCC : 100% + UNREG_SUCC: 100% + REG_RT : 15s + UNREG_RT: 10ms + TOTAL_PROCS : 1 + TOTAL_FDS : 66 + TOTAL_ORPHANS : 0 + FRNTEND_MEM : 75000000 #75MB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml b/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..24c07f5cf --- /dev/null +++ b/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml @@ -0,0 +1,12 @@ +--- +settings: + env: + REG_SUCC : 100% + SCL_UP_SUCC: 100% + UNREG_SUCC: 100% + REG_RT : 15s + SCL_UP_RT: 1.5s + UNREG_RT: 18ms + TOTAL_PROCS : 2 + TOTAL_FDS : 73 + FRNTEND_MEM : 120000000 #120MB \ No newline at end of file diff --git a/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..6d43899b3 --- /dev/null +++ b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml @@ -0,0 +1,15 @@ +--- +settings: + env: + SCL_DWN_SUCC : 100% + SCL_DWN_RT : 10ms + TOTAL_PROCS_B4_SCL_DWN : 6 + TOTAL_PROCS_AFTR_SCL_DWN : 4 + TOTAL_WRKRS_B4_SCL_DWN : 4 + TOTAL_WRKRS_AFTR_SCL_DWN : 2 + FRNTEND_FDS : 78 + TOTAL_WRKRS_FDS_B4_SCL_DWN: 38 + TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 23 + FRNTEND_MEM : 290000000 #290MB + TOTAL_WRKRS_MEM_B4_SCL_DWN : 450000000 #450MB + TOTAL_WRKRS_MEM_AFTR_SCL_DWN : 210000000 #210MB \ No newline at end of file diff --git a/tests/performance/tests/scale_up_workers/environments/ci_linux_medium.yaml b/tests/performance/tests/scale_up_workers/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..9e3182c3e --- /dev/null +++ b/tests/performance/tests/scale_up_workers/environments/ci_linux_medium.yaml @@ -0,0 +1,15 @@ +--- +settings: + env: + SCL_UP_SUCC : 100% + SCL_UP_RT : 10ms + TOTAL_PROCS_AFTR_SCL_UP : 6 + TOTAL_PROCS_B4_SCL_UP : 3 + TOTAL_WRKRS_AFTR_SCL_UP : 4 + TOTAL_WRKRS_B4_SCL_UP : 1 + FRNTEND_FDS : 88 + TOTAL_WRKRS_FDS_AFTR_SCL_UP : 38 + TOTAL_WRKRS_FDS_B4_SCL_UP : 11 + FRNTEND_MEM : 290000000 #290MB + TOTAL_WRKRS_MEM_AFTR_SCL_UP : 450000000 #450MB + TOTAL_WRKRS_MEM_B4_SCL_UP : 115000000 #115MB \ No newline at end of file diff --git a/tests/performance/tests/unregister_while_inferencing/environments/ci_linux_medium.yaml b/tests/performance/tests/unregister_while_inferencing/environments/ci_linux_medium.yaml new file mode 100644 index 000000000..f4c5be87b --- /dev/null +++ b/tests/performance/tests/unregister_while_inferencing/environments/ci_linux_medium.yaml @@ -0,0 +1,11 @@ +--- +settings: + env: + INFR_SUCC : 100% + UNREG_SUCC: 100% + INFR_RT : 22s + UNREG_RT: 25ms + MIN_TOTAL_PROCS : 1 + MAX_TOTAL_PROCS : 3 + TOTAL_FDS : 74 + FRNTEND_MEM : 250000000 #250MB \ No newline at end of file From 680aafc5bf28a9f7c7966f4fa733607af5cdf08c Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 1 Jul 2020 22:26:24 +0530 Subject: [PATCH 300/329] handled space issues --- .../custom_model_for_slow_inference.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/performance/tests/unregister_while_inferencing/custom_model_for_slow_inference.sh b/tests/performance/tests/unregister_while_inferencing/custom_model_for_slow_inference.sh index abc1142cf..ec8a0f264 100755 --- a/tests/performance/tests/unregister_while_inferencing/custom_model_for_slow_inference.sh +++ b/tests/performance/tests/unregister_while_inferencing/custom_model_for_slow_inference.sh @@ -26,11 +26,12 @@ build(){ rm $MODEL_NAME.mar #### Adds a 20 sec wait in the handle function #### + # DO NOT - change any spacing and\or try to format the below code sed -i'' -e '/import logging/a\ - import time' $HANDLER_FILE_NAME.py +import time' $HANDLER_FILE_NAME.py sed -i'' -e '/def handle(data, context):/a\ - \ \ \ \ \time.sleep(20) +\ \ \ \ time.sleep(20) ' $HANDLER_FILE_NAME.py ################################################## From 7081cdf3bd8c8e9b6d6b92049408a92d327b807e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 2 Jul 2020 17:20:38 +0530 Subject: [PATCH 301/329] [skip ci] updated perf regression command for ci --- .circleci/scripts/linux_test_perf_regression.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/scripts/linux_test_perf_regression.sh b/.circleci/scripts/linux_test_perf_regression.sh index 0a548b9d9..3698ab9c2 100755 --- a/.circleci/scripts/linux_test_perf_regression.sh +++ b/.circleci/scripts/linux_test_perf_regression.sh @@ -20,7 +20,7 @@ pip install -r requirements.txt pip install bzt # Execute performance test suite and store exit code -./run_performance_suite.py -j $JMETER_PATH -e macos_xlarge --no-compare-local +./run_performance_suite.py -j $JMETER_PATH -e ci_linux_medium -x example* --no-compare-local --no-monit EXIT_CODE=$? # Collect and store test results in result directory to be picked up by CircleCI From beb4302c36cdc04ee83f981c1ae2a4e18249b961 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 2 Jul 2020 17:21:12 +0530 Subject: [PATCH 302/329] api and bactch single scenario updated --- .../tests/api_description/environments/ci_linux_medium.yaml | 6 +++--- .../environments/ci_linux_medium.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/performance/tests/api_description/environments/ci_linux_medium.yaml b/tests/performance/tests/api_description/environments/ci_linux_medium.yaml index cd97694c1..0e54fc7bf 100644 --- a/tests/performance/tests/api_description/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/api_description/environments/ci_linux_medium.yaml @@ -3,8 +3,8 @@ settings: env: MGMT_DESC_SUCC: 100% INFR_DESC_SUCC: 100% - MGMT_DESC_RT : 18ms - INFR_DESC_RT : 18ms + MGMT_DESC_RT : 20ms + INFR_DESC_RT : 20ms TOTAL_PROCS : 1 - TOTAL_FDS : 82 + TOTAL_FDS : 487 TOTAL_MEM: 100000000 #100MB diff --git a/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml b/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml index 97307b690..049f11f03 100644 --- a/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml @@ -1,9 +1,9 @@ --- settings: env: - INFR1_RT : 6s + INFR1_RT : 5s INFR2_RT : 0.08s TOTAL_WORKERS_MEM : 4000000000 #4GB - TOTAL_WORKERS : 9 + TOTAL_WORKERS : 6 TOTAL_ORPHANS : 0 TOTAL_WORKERS_FDS : 78 From d5aeba093bde27c9dbff45c7f2e60b8fd215cbb1 Mon Sep 17 00:00:00 2001 From: maheshambule Date: Thu, 2 Jul 2020 17:23:19 +0530 Subject: [PATCH 303/329] remove comp_data from artifacts dir --- tests/performance/run_performance_suite.py | 2 +- tests/performance/runs/storage.py | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/performance/run_performance_suite.py b/tests/performance/run_performance_suite.py index 6ab1b4e3c..45948dff4 100755 --- a/tests/performance/run_performance_suite.py +++ b/tests/performance/run_performance_suite.py @@ -79,7 +79,7 @@ def run_test_suite(artifacts_dir, test_dir, pattern, exclude_pattern, test_dirs = get_sub_dirs(test_dir, exclude_list=[], include_pattern=pattern, exclude_pattern=exclude_pattern) if not test_dirs: - logger.info("No test cases are collected...Existing.") + logger.info("No test cases are collected...Exiting.") sys.exit(3) else: logger.info("Collected tests %s", test_dirs) diff --git a/tests/performance/runs/storage.py b/tests/performance/runs/storage.py index 9d015bc1a..6db69716c 100644 --- a/tests/performance/runs/storage.py +++ b/tests/performance/runs/storage.py @@ -19,6 +19,7 @@ import logging import os import sys +import shutil import boto3 import pathlib @@ -86,7 +87,7 @@ def get_dir_to_compare(self): """Get latest run result artifacts directory for same env_name from S3 bucket and store it locally for further comparison """ - tgt_path = os.path.join(self.artifacts_dir, "comp_data") + comp_data_path = os.path.join(self.artifacts_dir, "comp_data") s3 = boto3.resource('s3') bucket = s3.Bucket(S3_BUCKET) result = bucket.meta.client.list_objects(Bucket=bucket.name, @@ -100,15 +101,19 @@ def get_dir_to_compare(self): logger.info("No run found for env_id %s", self.env_name) return '', '' - if not os.path.exists(tgt_path): - os.makedirs(tgt_path) + if not os.path.exists(comp_data_path): + os.makedirs(comp_data_path) - tgt_path = os.path.join(tgt_path, latest_run) + tgt_path = os.path.join(comp_data_path, latest_run) run_process("aws s3 cp s3://{}/{} {} --recursive".format(bucket.name, latest_run, tgt_path)) return tgt_path, latest_run def store_results(self): """Store the run results back to S3""" + comp_data_path = os.path.join(self.artifacts_dir, "comp_data") + if os.path.exists(comp_data_path): + shutil.rmtree(comp_data_path) + run_process("aws s3 cp {} s3://{}/{} --recursive".format(self.artifacts_dir, S3_BUCKET, self.current_run_name)) From 6b29ed65cb7f932304a4b9916f657cf05aba7539 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 2 Jul 2020 17:43:24 +0530 Subject: [PATCH 304/329] tweaked thresholds for cci --- .../api_description/environments/ci_linux_medium.yaml | 2 +- .../environments/ci_linux_medium.yaml | 8 ++++---- .../batch_inference/environments/ci_linux_medium.yaml | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/performance/tests/api_description/environments/ci_linux_medium.yaml b/tests/performance/tests/api_description/environments/ci_linux_medium.yaml index 0e54fc7bf..3cb71f9ca 100644 --- a/tests/performance/tests/api_description/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/api_description/environments/ci_linux_medium.yaml @@ -7,4 +7,4 @@ settings: INFR_DESC_RT : 20ms TOTAL_PROCS : 1 TOTAL_FDS : 487 - TOTAL_MEM: 100000000 #100MB + TOTAL_MEM: 888000000 #888MB diff --git a/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml b/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml index 049f11f03..1edde91c4 100644 --- a/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml @@ -2,8 +2,8 @@ settings: env: INFR1_RT : 5s - INFR2_RT : 0.08s - TOTAL_WORKERS_MEM : 4000000000 #4GB - TOTAL_WORKERS : 6 + INFR2_RT : 110ms + TOTAL_WORKERS_MEM : 1630000000 #1.63GB + TOTAL_WORKERS : 5 TOTAL_ORPHANS : 0 - TOTAL_WORKERS_FDS : 78 + TOTAL_WORKERS_FDS : 44 diff --git a/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml b/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml index 23a443aaf..72cb42cc1 100644 --- a/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml @@ -1,8 +1,8 @@ --- settings: env: - INFR_RT : 1.5s - TOTAL_WORKERS_MEM : 3000000000 #3GB - TOTAL_WORKERS : 4 + INFR_RT : 3.5s + TOTAL_WORKERS_MEM : 1300000000 #1.3GB + TOTAL_WORKERS : 2 TOTAL_ORPHANS : 0 - TOTAL_WORKERS_FDS : 38 \ No newline at end of file + TOTAL_WORKERS_FDS : 22 \ No newline at end of file From bc96274aa085a6ab3b69068c62c3f35a8256ddc0 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 2 Jul 2020 18:45:25 +0530 Subject: [PATCH 305/329] tweaked thresholds for cci --- .../health_check/environments/ci_linux_medium.yaml | 6 +++--- .../environments/ci_linux_medium.yaml | 4 ++-- .../environments/ci_linux_medium.yaml | 4 ++-- .../environments/ci_linux_medium.yaml | 6 +++--- .../list_models/environments/ci_linux_medium.yaml | 4 ++-- .../environments/ci_linux_medium.yaml | 4 ++-- .../environments/ci_linux_medium.yaml | 10 +++++----- .../environments/ci_linux_medium.yaml | 8 ++++---- .../environments/ci_linux_medium.yaml | 6 +++--- .../environments/ci_linux_medium.yaml | 12 ++++++------ .../environments/ci_linux_medium.yaml | 12 ++++++------ 11 files changed, 38 insertions(+), 38 deletions(-) diff --git a/tests/performance/tests/health_check/environments/ci_linux_medium.yaml b/tests/performance/tests/health_check/environments/ci_linux_medium.yaml index 689a5d66b..34a376f72 100644 --- a/tests/performance/tests/health_check/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/health_check/environments/ci_linux_medium.yaml @@ -2,7 +2,7 @@ settings: env: HLTH_CHK_SUCC : 100% - HLTH_CHK_RT : 14ms + HLTH_CHK_RT : 20ms TOTAL_PROCS : 1 - TOTAL_FDS : 67 - TOTAL_MEM : 750000000 #750MB \ No newline at end of file + TOTAL_FDS : 480 + TOTAL_MEM : 500000000 #500MB \ No newline at end of file diff --git a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml index 36b7dc0ad..8ee6d158b 100644 --- a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml @@ -6,5 +6,5 @@ settings: INFR1_RT : 290ms INFR2_RT: 450ms TOTAL_PROCS : 5 - TOTAL_FDS : 107 - TOTAL_MEM : 600000000 #600MB \ No newline at end of file + TOTAL_FDS : 520 + TOTAL_MEM : 810000000 #810MB \ No newline at end of file diff --git a/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml index 5b9fd6c0a..708d81a58 100644 --- a/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml @@ -4,5 +4,5 @@ settings: INFR_SUCC : 100% INFR_RT : 140ms TOTAL_PROCS : 6 - TOTAL_FDS : 126 - TOTAL_MEM : 750000000 #750MB \ No newline at end of file + TOTAL_FDS : 540 + TOTAL_MEM : 890000000 #890MB \ No newline at end of file diff --git a/tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml index c945e1f91..6aee882e9 100644 --- a/tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml @@ -2,7 +2,7 @@ settings: env: INFR_SUCC : 100% - INFR_RT : 290ms + INFR_RT : 500ms TOTAL_PROCS : 3 - TOTAL_FDS : 90 - TOTAL_MEM : 290000000 #290MB \ No newline at end of file + TOTAL_FDS : 503 + TOTAL_MEM : 500000000 #500MB \ No newline at end of file diff --git a/tests/performance/tests/list_models/environments/ci_linux_medium.yaml b/tests/performance/tests/list_models/environments/ci_linux_medium.yaml index 611624824..2f4ef1cfd 100644 --- a/tests/performance/tests/list_models/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/list_models/environments/ci_linux_medium.yaml @@ -4,5 +4,5 @@ settings: LST_MODLS_SUCC : 100% LST_MODLS_RT : 14ms TOTAL_PROCS : 3 - TOTAL_FDS : 86 - TOTAL_MEM : 185000000 #185MB \ No newline at end of file + TOTAL_FDS : 498 + TOTAL_MEM : 570000000 #570MB \ No newline at end of file diff --git a/tests/performance/tests/model_description/environments/ci_linux_medium.yaml b/tests/performance/tests/model_description/environments/ci_linux_medium.yaml index 00e0aac87..2dd7720d3 100644 --- a/tests/performance/tests/model_description/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/model_description/environments/ci_linux_medium.yaml @@ -4,5 +4,5 @@ settings: MODL_DESC_SUCC : 100% MODL_DESC_RT : 14ms TOTAL_PROCS : 3 - TOTAL_FDS : 90 - TOTAL_MEM : 300000000 #300MB \ No newline at end of file + TOTAL_FDS : 503 + TOTAL_MEM : 685000000 #685MB \ No newline at end of file diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml index 1671213d1..c5e1de924 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml @@ -3,10 +3,10 @@ settings: env: INFR1_SUCC : 100% INFR2_SUCC: 100% - INFR1_RT : 290ms - INFR2_RT: 450ms - TOTAL_PROCS : 14 - TOTAL_FDS : 300 + INFR1_RT : 100ms + INFR2_RT: 250ms + TOTAL_PROCS : 11 + TOTAL_FDS : 608 TOTAL_MEM : 2000000000 #~2GB TOTAL_ORPHANS : 0 - FRNTEND_MEM : 1000000000 #~1GB \ No newline at end of file + FRNTEND_MEM : 630000000 #~630MB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml b/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml index 0e099afed..a5b84eecc 100644 --- a/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml @@ -3,9 +3,9 @@ settings: env: REG_SUCC : 100% UNREG_SUCC: 100% - REG_RT : 15s - UNREG_RT: 10ms + REG_RT : 2.5s + UNREG_RT: 60ms TOTAL_PROCS : 1 - TOTAL_FDS : 66 + TOTAL_FDS : 475 TOTAL_ORPHANS : 0 - FRNTEND_MEM : 75000000 #75MB \ No newline at end of file + FRNTEND_MEM : 100000000 #100MB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml b/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml index 24c07f5cf..6fe795e3e 100644 --- a/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml @@ -4,9 +4,9 @@ settings: REG_SUCC : 100% SCL_UP_SUCC: 100% UNREG_SUCC: 100% - REG_RT : 15s + REG_RT : 2.5s SCL_UP_RT: 1.5s UNREG_RT: 18ms TOTAL_PROCS : 2 - TOTAL_FDS : 73 - FRNTEND_MEM : 120000000 #120MB \ No newline at end of file + TOTAL_FDS : 509 + FRNTEND_MEM : 425000000 #425MB \ No newline at end of file diff --git a/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml index 6d43899b3..cae603809 100644 --- a/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml @@ -7,9 +7,9 @@ settings: TOTAL_PROCS_AFTR_SCL_DWN : 4 TOTAL_WRKRS_B4_SCL_DWN : 4 TOTAL_WRKRS_AFTR_SCL_DWN : 2 - FRNTEND_FDS : 78 - TOTAL_WRKRS_FDS_B4_SCL_DWN: 38 - TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 23 - FRNTEND_MEM : 290000000 #290MB - TOTAL_WRKRS_MEM_B4_SCL_DWN : 450000000 #450MB - TOTAL_WRKRS_MEM_AFTR_SCL_DWN : 210000000 #210MB \ No newline at end of file + FRNTEND_FDS : 494 + TOTAL_WRKRS_FDS_B4_SCL_DWN: 43 + TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 27 + FRNTEND_MEM : 770000000 #770MB + TOTAL_WRKRS_MEM_B4_SCL_DWN : 430000000 #430MB + TOTAL_WRKRS_MEM_AFTR_SCL_DWN : 220000000 #220MB \ No newline at end of file diff --git a/tests/performance/tests/scale_up_workers/environments/ci_linux_medium.yaml b/tests/performance/tests/scale_up_workers/environments/ci_linux_medium.yaml index 9e3182c3e..cc098d358 100644 --- a/tests/performance/tests/scale_up_workers/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/scale_up_workers/environments/ci_linux_medium.yaml @@ -7,9 +7,9 @@ settings: TOTAL_PROCS_B4_SCL_UP : 3 TOTAL_WRKRS_AFTR_SCL_UP : 4 TOTAL_WRKRS_B4_SCL_UP : 1 - FRNTEND_FDS : 88 - TOTAL_WRKRS_FDS_AFTR_SCL_UP : 38 - TOTAL_WRKRS_FDS_B4_SCL_UP : 11 - FRNTEND_MEM : 290000000 #290MB - TOTAL_WRKRS_MEM_AFTR_SCL_UP : 450000000 #450MB - TOTAL_WRKRS_MEM_B4_SCL_UP : 115000000 #115MB \ No newline at end of file + FRNTEND_FDS : 499 + TOTAL_WRKRS_FDS_AFTR_SCL_UP : 43 + TOTAL_WRKRS_FDS_B4_SCL_UP : 13 + FRNTEND_MEM : 800000000 #800MB + TOTAL_WRKRS_MEM_AFTR_SCL_UP : 430000000 #430MB + TOTAL_WRKRS_MEM_B4_SCL_UP : 12000000 #120MB \ No newline at end of file From dab2d5008be3e93f8c0839b4c77c02516548a8fd Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 2 Jul 2020 19:37:25 +0530 Subject: [PATCH 306/329] tweaked thresholds for circleci environment --- .../tests/api_description/environments/ci_linux_medium.yaml | 2 +- .../inference_multiple_models/environments/ci_linux_medium.yaml | 2 +- .../environments/ci_linux_medium.yaml | 2 +- .../register_unregister_multiple.yaml | 2 +- .../tests/scale_down_workers/environments/ci_linux_medium.yaml | 2 +- .../environments/ci_linux_medium.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/performance/tests/api_description/environments/ci_linux_medium.yaml b/tests/performance/tests/api_description/environments/ci_linux_medium.yaml index 3cb71f9ca..73bbcb0bd 100644 --- a/tests/performance/tests/api_description/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/api_description/environments/ci_linux_medium.yaml @@ -6,5 +6,5 @@ settings: MGMT_DESC_RT : 20ms INFR_DESC_RT : 20ms TOTAL_PROCS : 1 - TOTAL_FDS : 487 + TOTAL_FDS : 488 TOTAL_MEM: 888000000 #888MB diff --git a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml index 8ee6d158b..d1dfe832a 100644 --- a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml @@ -4,7 +4,7 @@ settings: INFR1_SUCC : 100% INFR2_SUCC: 100% INFR1_RT : 290ms - INFR2_RT: 450ms + INFR2_RT: 630ms TOTAL_PROCS : 5 TOTAL_FDS : 520 TOTAL_MEM : 810000000 #810MB \ No newline at end of file diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml index c5e1de924..f09c58c9a 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml @@ -9,4 +9,4 @@ settings: TOTAL_FDS : 608 TOTAL_MEM : 2000000000 #~2GB TOTAL_ORPHANS : 0 - FRNTEND_MEM : 630000000 #~630MB \ No newline at end of file + FRNTEND_MEM : 650000000 #~650MB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml index 5c8fcb85e..5f4bd2b7f 100644 --- a/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml +++ b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml @@ -47,7 +47,7 @@ reporting: subject: ServerLocalClient/total_processes condition: '>' threshold: ${TOTAL_PROCS} - timeframe: 5s + timeframe: 7s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria diff --git a/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml index cae603809..d948515ce 100644 --- a/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml @@ -9,7 +9,7 @@ settings: TOTAL_WRKRS_AFTR_SCL_DWN : 2 FRNTEND_FDS : 494 TOTAL_WRKRS_FDS_B4_SCL_DWN: 43 - TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 27 + TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 26 FRNTEND_MEM : 770000000 #770MB TOTAL_WRKRS_MEM_B4_SCL_DWN : 430000000 #430MB TOTAL_WRKRS_MEM_AFTR_SCL_DWN : 220000000 #220MB \ No newline at end of file diff --git a/tests/performance/tests/unregister_while_inferencing/environments/ci_linux_medium.yaml b/tests/performance/tests/unregister_while_inferencing/environments/ci_linux_medium.yaml index f4c5be87b..f44ae571c 100644 --- a/tests/performance/tests/unregister_while_inferencing/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/unregister_while_inferencing/environments/ci_linux_medium.yaml @@ -7,5 +7,5 @@ settings: UNREG_RT: 25ms MIN_TOTAL_PROCS : 1 MAX_TOTAL_PROCS : 3 - TOTAL_FDS : 74 + TOTAL_FDS : 498 FRNTEND_MEM : 250000000 #250MB \ No newline at end of file From 99687a3e8cc29f9866651367ffa818ac3730abee Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 2 Jul 2020 20:16:11 +0530 Subject: [PATCH 307/329] performance regression only on py36 --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2d5d36d3f..1764bb5ca 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -188,6 +188,8 @@ workflows: requires: - build-<< matrix.executor >> matrix: *matrix + exclude: + - executor: "py27" ############################################### # nightly: From 96e40bd19f941bdad3fd26014fc70f597b05eb21 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 2 Jul 2020 20:19:48 +0530 Subject: [PATCH 308/329] performance regression only on py36 --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1764bb5ca..f0b7bd69f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -187,7 +187,8 @@ workflows: name: performance-regression-<< matrix.executor >> requires: - build-<< matrix.executor >> - matrix: *matrix + matrix: + <<: *matrix exclude: - executor: "py27" ############################################### From b41186e306938cc63f70895031d4ad6828bddd4e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 2 Jul 2020 20:52:13 +0530 Subject: [PATCH 309/329] unregister while inferencing moved to examples --- .../custom_model_for_slow_inference.sh | 0 .../environments/ci_linux_medium.yaml | 0 .../environments/macos_xlarge.yaml | 0 .../examples_unregister_while_inferencing.jmx} | 0 .../examples_unregister_while_inferencing.yaml} | 6 +++--- 5 files changed, 3 insertions(+), 3 deletions(-) rename tests/performance/tests/{unregister_while_inferencing => examples_unregister_while_inferencing}/custom_model_for_slow_inference.sh (100%) rename tests/performance/tests/{unregister_while_inferencing => examples_unregister_while_inferencing}/environments/ci_linux_medium.yaml (100%) rename tests/performance/tests/{unregister_while_inferencing => examples_unregister_while_inferencing}/environments/macos_xlarge.yaml (100%) rename tests/performance/tests/{unregister_while_inferencing/unregister_while_inferencing.jmx => examples_unregister_while_inferencing/examples_unregister_while_inferencing.jmx} (100%) rename tests/performance/tests/{unregister_while_inferencing/unregister_while_inferencing.yaml => examples_unregister_while_inferencing/examples_unregister_while_inferencing.yaml} (93%) diff --git a/tests/performance/tests/unregister_while_inferencing/custom_model_for_slow_inference.sh b/tests/performance/tests/examples_unregister_while_inferencing/custom_model_for_slow_inference.sh similarity index 100% rename from tests/performance/tests/unregister_while_inferencing/custom_model_for_slow_inference.sh rename to tests/performance/tests/examples_unregister_while_inferencing/custom_model_for_slow_inference.sh diff --git a/tests/performance/tests/unregister_while_inferencing/environments/ci_linux_medium.yaml b/tests/performance/tests/examples_unregister_while_inferencing/environments/ci_linux_medium.yaml similarity index 100% rename from tests/performance/tests/unregister_while_inferencing/environments/ci_linux_medium.yaml rename to tests/performance/tests/examples_unregister_while_inferencing/environments/ci_linux_medium.yaml diff --git a/tests/performance/tests/unregister_while_inferencing/environments/macos_xlarge.yaml b/tests/performance/tests/examples_unregister_while_inferencing/environments/macos_xlarge.yaml similarity index 100% rename from tests/performance/tests/unregister_while_inferencing/environments/macos_xlarge.yaml rename to tests/performance/tests/examples_unregister_while_inferencing/environments/macos_xlarge.yaml diff --git a/tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.jmx b/tests/performance/tests/examples_unregister_while_inferencing/examples_unregister_while_inferencing.jmx similarity index 100% rename from tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.jmx rename to tests/performance/tests/examples_unregister_while_inferencing/examples_unregister_while_inferencing.jmx diff --git a/tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.yaml b/tests/performance/tests/examples_unregister_while_inferencing/examples_unregister_while_inferencing.yaml similarity index 93% rename from tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.yaml rename to tests/performance/tests/examples_unregister_while_inferencing/examples_unregister_while_inferencing.yaml index a934285bb..43d3a6271 100644 --- a/tests/performance/tests/unregister_while_inferencing/unregister_while_inferencing.yaml +++ b/tests/performance/tests/examples_unregister_while_inferencing/examples_unregister_while_inferencing.yaml @@ -3,11 +3,11 @@ execution: - concurrency: 1 ramp-up: 0 iterations: 1 - scenario: unregister_while_inferencing + scenario: examples_unregister_while_inferencing scenarios: - unregister_while_inferencing: - script: unregister_while_inferencing.jmx + examples_unregister_while_inferencing: + script: examples_unregister_while_inferencing.jmx modules: server_local_monitoring: From 8cf7620be0ab83b0a0400c223d72995a2b9b78fe Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 2 Jul 2020 21:23:29 +0530 Subject: [PATCH 310/329] tweaked thresholds for ci_linux_medium --- .../environments/ci_linux_medium.yaml | 2 +- .../tests/scale_down_workers/environments/ci_linux_medium.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml index f09c58c9a..45ee35dbb 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml @@ -9,4 +9,4 @@ settings: TOTAL_FDS : 608 TOTAL_MEM : 2000000000 #~2GB TOTAL_ORPHANS : 0 - FRNTEND_MEM : 650000000 #~650MB \ No newline at end of file + FRNTEND_MEM : 700000000 #~700MB \ No newline at end of file diff --git a/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml index d948515ce..9828f5f52 100644 --- a/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml @@ -9,7 +9,7 @@ settings: TOTAL_WRKRS_AFTR_SCL_DWN : 2 FRNTEND_FDS : 494 TOTAL_WRKRS_FDS_B4_SCL_DWN: 43 - TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 26 + TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 25 FRNTEND_MEM : 770000000 #770MB TOTAL_WRKRS_MEM_B4_SCL_DWN : 430000000 #430MB TOTAL_WRKRS_MEM_AFTR_SCL_DWN : 220000000 #220MB \ No newline at end of file From 9d9cc6a830944569b9ff041e05dd0929ed48087c Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 2 Jul 2020 22:28:01 +0530 Subject: [PATCH 311/329] tweaked TCs --- .../inference_multiple_models/environments/ci_linux_medium.yaml | 2 +- .../tests/list_models/environments/ci_linux_medium.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml index d1dfe832a..33a4e3cb3 100644 --- a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml @@ -6,5 +6,5 @@ settings: INFR1_RT : 290ms INFR2_RT: 630ms TOTAL_PROCS : 5 - TOTAL_FDS : 520 + TOTAL_FDS : 521 TOTAL_MEM : 810000000 #810MB \ No newline at end of file diff --git a/tests/performance/tests/list_models/environments/ci_linux_medium.yaml b/tests/performance/tests/list_models/environments/ci_linux_medium.yaml index 2f4ef1cfd..0bfae217c 100644 --- a/tests/performance/tests/list_models/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/list_models/environments/ci_linux_medium.yaml @@ -2,7 +2,7 @@ settings: env: LST_MODLS_SUCC : 100% - LST_MODLS_RT : 14ms + LST_MODLS_RT : 40ms TOTAL_PROCS : 3 TOTAL_FDS : 498 TOTAL_MEM : 570000000 #570MB \ No newline at end of file From c0be394fd4f593e6917699452601cedb9f14396e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 2 Jul 2020 23:41:08 +0530 Subject: [PATCH 312/329] tweaked thresholds for circleci runs --- .../environments/ci_linux_medium.yaml | 2 +- .../environments/ci_linux_medium.yaml | 2 +- .../environments/ci_linux_medium.yaml | 2 +- .../environments/ci_linux_medium.yaml | 5 +++-- .../register_unregister_multiple.yaml | 11 +++++++++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml index 33a4e3cb3..d4b16c275 100644 --- a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml @@ -4,7 +4,7 @@ settings: INFR1_SUCC : 100% INFR2_SUCC: 100% INFR1_RT : 290ms - INFR2_RT: 630ms + INFR2_RT: 800ms TOTAL_PROCS : 5 TOTAL_FDS : 521 TOTAL_MEM : 810000000 #810MB \ No newline at end of file diff --git a/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml index 708d81a58..c88d70b6c 100644 --- a/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml @@ -2,7 +2,7 @@ settings: env: INFR_SUCC : 100% - INFR_RT : 140ms + INFR_RT : 200ms TOTAL_PROCS : 6 TOTAL_FDS : 540 TOTAL_MEM : 890000000 #890MB \ No newline at end of file diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml index 45ee35dbb..646727d2c 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/environments/ci_linux_medium.yaml @@ -9,4 +9,4 @@ settings: TOTAL_FDS : 608 TOTAL_MEM : 2000000000 #~2GB TOTAL_ORPHANS : 0 - FRNTEND_MEM : 700000000 #~700MB \ No newline at end of file + FRNTEND_MEM : 800000000 #~800MB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml b/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml index 6fe795e3e..22753ef05 100644 --- a/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml @@ -6,7 +6,8 @@ settings: UNREG_SUCC: 100% REG_RT : 2.5s SCL_UP_RT: 1.5s - UNREG_RT: 18ms - TOTAL_PROCS : 2 + UNREG_RT: 150ms + TOTAL_PROCS_WITH_EXTRA_MODEL_AND_WORKERS : 5 + TOTAL_PROCS_WITHOUT_EXTRA_MODEL : 2 TOTAL_FDS : 509 FRNTEND_MEM : 425000000 #425MB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml index 5f4bd2b7f..29dc4c755 100644 --- a/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml +++ b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml @@ -46,8 +46,15 @@ reporting: - class: bzt.modules.monitoring.MonitoringCriteria subject: ServerLocalClient/total_processes condition: '>' - threshold: ${TOTAL_PROCS} - timeframe: 7s + threshold: ${TOTAL_PROCS_WITH_EXTRA_MODEL_AND_WORKERS} + timeframe: 5s + stop : true + fail : true + - class: bzt.modules.monitoring.MonitoringCriteria + subject: ServerLocalClient/total_processes + condition: '<' + threshold: ${TOTAL_PROCS_WITHOUT_EXTRA_MODEL} + timeframe: 5s stop : true fail : true - class: bzt.modules.monitoring.MonitoringCriteria From a97d9de62492e0898b0f96c3b9e2370b6402a226 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 3 Jul 2020 00:42:18 +0530 Subject: [PATCH 313/329] tweaked thresholds for circleci runs --- .../tests/batch_inference/environments/ci_linux_medium.yaml | 2 +- .../tests/health_check/environments/ci_linux_medium.yaml | 2 +- .../inference_multiple_worker/environments/ci_linux_medium.yaml | 2 +- .../inference_single_worker/environments/ci_linux_medium.yaml | 2 +- .../tests/list_models/environments/ci_linux_medium.yaml | 2 +- .../tests/model_description/environments/ci_linux_medium.yaml | 2 +- .../tests/scale_down_workers/environments/ci_linux_medium.yaml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml b/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml index 72cb42cc1..5d35bc8a1 100644 --- a/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml @@ -1,7 +1,7 @@ --- settings: env: - INFR_RT : 3.5s + INFR_RT : 4s TOTAL_WORKERS_MEM : 1300000000 #1.3GB TOTAL_WORKERS : 2 TOTAL_ORPHANS : 0 diff --git a/tests/performance/tests/health_check/environments/ci_linux_medium.yaml b/tests/performance/tests/health_check/environments/ci_linux_medium.yaml index 34a376f72..d5e421edb 100644 --- a/tests/performance/tests/health_check/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/health_check/environments/ci_linux_medium.yaml @@ -2,7 +2,7 @@ settings: env: HLTH_CHK_SUCC : 100% - HLTH_CHK_RT : 20ms + HLTH_CHK_RT : 40ms TOTAL_PROCS : 1 TOTAL_FDS : 480 TOTAL_MEM : 500000000 #500MB \ No newline at end of file diff --git a/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml index c88d70b6c..edb6cf462 100644 --- a/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/inference_multiple_worker/environments/ci_linux_medium.yaml @@ -2,7 +2,7 @@ settings: env: INFR_SUCC : 100% - INFR_RT : 200ms + INFR_RT : 400ms TOTAL_PROCS : 6 TOTAL_FDS : 540 TOTAL_MEM : 890000000 #890MB \ No newline at end of file diff --git a/tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml index 6aee882e9..0768b2218 100644 --- a/tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/inference_single_worker/environments/ci_linux_medium.yaml @@ -2,7 +2,7 @@ settings: env: INFR_SUCC : 100% - INFR_RT : 500ms + INFR_RT : 800ms TOTAL_PROCS : 3 TOTAL_FDS : 503 TOTAL_MEM : 500000000 #500MB \ No newline at end of file diff --git a/tests/performance/tests/list_models/environments/ci_linux_medium.yaml b/tests/performance/tests/list_models/environments/ci_linux_medium.yaml index 0bfae217c..a24d1187b 100644 --- a/tests/performance/tests/list_models/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/list_models/environments/ci_linux_medium.yaml @@ -4,5 +4,5 @@ settings: LST_MODLS_SUCC : 100% LST_MODLS_RT : 40ms TOTAL_PROCS : 3 - TOTAL_FDS : 498 + TOTAL_FDS : 500 TOTAL_MEM : 570000000 #570MB \ No newline at end of file diff --git a/tests/performance/tests/model_description/environments/ci_linux_medium.yaml b/tests/performance/tests/model_description/environments/ci_linux_medium.yaml index 2dd7720d3..2dc2a63f0 100644 --- a/tests/performance/tests/model_description/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/model_description/environments/ci_linux_medium.yaml @@ -2,7 +2,7 @@ settings: env: MODL_DESC_SUCC : 100% - MODL_DESC_RT : 14ms + MODL_DESC_RT : 40ms TOTAL_PROCS : 3 TOTAL_FDS : 503 TOTAL_MEM : 685000000 #685MB \ No newline at end of file diff --git a/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml index 9828f5f52..cedc9e9c3 100644 --- a/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml @@ -9,7 +9,7 @@ settings: TOTAL_WRKRS_AFTR_SCL_DWN : 2 FRNTEND_FDS : 494 TOTAL_WRKRS_FDS_B4_SCL_DWN: 43 - TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 25 + TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 24 FRNTEND_MEM : 770000000 #770MB TOTAL_WRKRS_MEM_B4_SCL_DWN : 430000000 #430MB TOTAL_WRKRS_MEM_AFTR_SCL_DWN : 220000000 #220MB \ No newline at end of file From 450bde2594cbdda92cf8f33d43c8c17f196f974c Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 3 Jul 2020 01:37:02 +0530 Subject: [PATCH 314/329] Tweaked metric thresholds for circleci --- .../tests/batch_inference/environments/ci_linux_medium.yaml | 2 +- .../environments/ci_linux_medium.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml b/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml index 5d35bc8a1..623067af3 100644 --- a/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/batch_inference/environments/ci_linux_medium.yaml @@ -1,7 +1,7 @@ --- settings: env: - INFR_RT : 4s + INFR_RT : 5s TOTAL_WORKERS_MEM : 1300000000 #1.3GB TOTAL_WORKERS : 2 TOTAL_ORPHANS : 0 diff --git a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml index d4b16c275..5074e7808 100644 --- a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml @@ -3,8 +3,8 @@ settings: env: INFR1_SUCC : 100% INFR2_SUCC: 100% - INFR1_RT : 290ms - INFR2_RT: 800ms + INFR1_RT : 350ms + INFR2_RT: 1.5s TOTAL_PROCS : 5 TOTAL_FDS : 521 TOTAL_MEM : 810000000 #810MB \ No newline at end of file From 1b5a219d4a16828f1ff2808dc1b0fd9beacbe28a Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 3 Jul 2020 02:13:37 +0530 Subject: [PATCH 315/329] revert config.yaml to optimal config --- .circleci/config.yml | 108 ++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 63 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f0b7bd69f..3b9571afe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -161,28 +161,51 @@ workflows: matrix: &matrix parameters: executor: ["py27", "py36"] -# - &modelarchiver -# modelarchiver: -# name: modelarchiver-<< matrix.executor >> -# matrix: *matrix -# - &python-tests -# python-tests: -# name: python-tests-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix -# ########### Remove after DEV ############### -# - &api-tests -# api-tests: -# name: api-tests-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix -# - benchmark: -# name: benchmark-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix + - &modelarchiver + modelarchiver: + name: modelarchiver-<< matrix.executor >> + matrix: *matrix + - &python-tests + python-tests: + name: python-tests-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix + + nightly: + triggers: + - schedule: + cron: "0 0 * * *" + filters: + branches: + only: + - master + jobs: + - *build + - *modelarchiver + - *python-tests + - &api-tests + api-tests: + name: api-tests-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix + + weekly: + triggers: + - schedule: + cron: "0 0 * * 0" + filters: + branches: + only: + - master + jobs: + - *build + - benchmark: + name: benchmark-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix - performance-regression: name: performance-regression-<< matrix.executor >> requires: @@ -191,44 +214,3 @@ workflows: <<: *matrix exclude: - executor: "py27" - ############################################### - -# nightly: -# triggers: -# - schedule: -# cron: "0 0 * * *" -# filters: -# branches: -# only: -# - master -# jobs: -# - *build -# - *modelarchiver -# - *python-tests -# - &api-tests -# api-tests: -# name: api-tests-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix -# -# weekly: -# triggers: -# - schedule: -# cron: "0 0 * * 0" -# filters: -# branches: -# only: -# - master -# jobs: -# - *build -# - benchmark: -# name: benchmark-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix -# - performance-regression: -# name: performance-regression-<< matrix.executor >> -# requires: -# - build-<< matrix.executor >> -# matrix: *matrix From ca92cd199ee4d610de075f5a21ca3f5c3c5bad5f Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Mon, 6 Jul 2020 17:21:54 +0530 Subject: [PATCH 316/329] Ps ci perf (#13) tweaks to run on py27 environment --- .circleci/config.yml | 5 +---- tests/performance/tests/api_description/api_description.yaml | 2 ++ .../batch_and_single_inference.yaml | 2 ++ tests/performance/tests/batch_inference/batch_inference.yaml | 2 ++ .../examples_local_criteria/examples_local_criteria.yaml | 2 ++ .../examples_local_monitoring/examples_local_monitoring.yaml | 2 ++ .../examples_remote_criteria/examples_remote_criteria.yaml | 2 ++ .../examples_remote_monitoring.yaml | 2 ++ .../performance/tests/examples_starter/examples_starter.yaml | 2 ++ .../examples_unregister_while_inferencing.yaml | 2 ++ tests/performance/tests/health_check/health_check.yaml | 2 ++ .../inference_multiple_models/inference_multiple_models.yaml | 2 ++ .../inference_multiple_worker/inference_multiple_worker.yaml | 2 ++ .../inference_single_worker/inference_single_worker.yaml | 2 ++ tests/performance/tests/list_models/list_models.yaml | 2 ++ .../tests/model_description/model_description.yaml | 2 ++ .../multiple_inference_and_scaling.yaml | 2 ++ .../tests/register_unregister/register_unregister.yaml | 2 ++ .../environments/ci_linux_medium.yaml | 2 +- .../register_unregister_multiple.yaml | 2 ++ .../scale_down_workers/environments/ci_linux_medium.yaml | 4 ++-- .../tests/scale_down_workers/scale_down_workers.yaml | 2 ++ .../performance/tests/scale_up_workers/scale_up_workers.yaml | 2 ++ 23 files changed, 44 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3b9571afe..ab75c61c5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -210,7 +210,4 @@ workflows: name: performance-regression-<< matrix.executor >> requires: - build-<< matrix.executor >> - matrix: - <<: *matrix - exclude: - - executor: "py27" + matrix: *matrix diff --git a/tests/performance/tests/api_description/api_description.yaml b/tests/performance/tests/api_description/api_description.yaml index da3316228..9a13aae05 100644 --- a/tests/performance/tests/api_description/api_description.yaml +++ b/tests/performance/tests/api_description/api_description.yaml @@ -16,10 +16,12 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "multi-model-server --start > /dev/null 2>&1" - "sleep 10s" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10s" - module: server_local_monitoring ServerLocalClient: - interval: 1s diff --git a/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml b/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml index 73e9ab957..4e81be8d7 100644 --- a/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml +++ b/tests/performance/tests/batch_and_single_inference/batch_and_single_inference.yaml @@ -17,6 +17,7 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" @@ -28,6 +29,7 @@ services: - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=2&synchronous=true" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_inproc.Monitor diff --git a/tests/performance/tests/batch_inference/batch_inference.yaml b/tests/performance/tests/batch_inference/batch_inference.yaml index 7c4485c06..123083c1b 100644 --- a/tests/performance/tests/batch_inference/batch_inference.yaml +++ b/tests/performance/tests/batch_inference/batch_inference.yaml @@ -17,6 +17,7 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" @@ -26,6 +27,7 @@ services: - "curl -s -X PUT http://localhost:8081/models/resnet-152?min_worker=2&synchronous=true" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_inproc.Monitor diff --git a/tests/performance/tests/examples_local_criteria/examples_local_criteria.yaml b/tests/performance/tests/examples_local_criteria/examples_local_criteria.yaml index 9d8b87907..039a31684 100644 --- a/tests/performance/tests/examples_local_criteria/examples_local_criteria.yaml +++ b/tests/performance/tests/examples_local_criteria/examples_local_criteria.yaml @@ -17,11 +17,13 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 10s" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_inproc.Monitor diff --git a/tests/performance/tests/examples_local_monitoring/examples_local_monitoring.yaml b/tests/performance/tests/examples_local_monitoring/examples_local_monitoring.yaml index d00226470..2d01cf64f 100644 --- a/tests/performance/tests/examples_local_monitoring/examples_local_monitoring.yaml +++ b/tests/performance/tests/examples_local_monitoring/examples_local_monitoring.yaml @@ -16,11 +16,13 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 10s" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: server_local_monitoring # should be added in modules section ServerLocalClient: # keyword from metrics_monitoring_inproc.Monitor diff --git a/tests/performance/tests/examples_remote_criteria/examples_remote_criteria.yaml b/tests/performance/tests/examples_remote_criteria/examples_remote_criteria.yaml index 0c3c206d1..44684d905 100644 --- a/tests/performance/tests/examples_remote_criteria/examples_remote_criteria.yaml +++ b/tests/performance/tests/examples_remote_criteria/examples_remote_criteria.yaml @@ -12,11 +12,13 @@ scenarios: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 10s" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: monitoring server-agent: diff --git a/tests/performance/tests/examples_remote_monitoring/examples_remote_monitoring.yaml b/tests/performance/tests/examples_remote_monitoring/examples_remote_monitoring.yaml index 235c3b803..890455eee 100644 --- a/tests/performance/tests/examples_remote_monitoring/examples_remote_monitoring.yaml +++ b/tests/performance/tests/examples_remote_monitoring/examples_remote_monitoring.yaml @@ -14,11 +14,13 @@ scenarios: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 10s" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: monitoring server-agent: diff --git a/tests/performance/tests/examples_starter/examples_starter.yaml b/tests/performance/tests/examples_starter/examples_starter.yaml index ac6aaa50b..0eb98f09a 100644 --- a/tests/performance/tests/examples_starter/examples_starter.yaml +++ b/tests/performance/tests/examples_starter/examples_starter.yaml @@ -11,10 +11,12 @@ scenarios: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 10s" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" diff --git a/tests/performance/tests/examples_unregister_while_inferencing/examples_unregister_while_inferencing.yaml b/tests/performance/tests/examples_unregister_while_inferencing/examples_unregister_while_inferencing.yaml index 43d3a6271..4a972e354 100644 --- a/tests/performance/tests/examples_unregister_while_inferencing/examples_unregister_while_inferencing.yaml +++ b/tests/performance/tests/examples_unregister_while_inferencing/examples_unregister_while_inferencing.yaml @@ -16,6 +16,7 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "./tests/unregister_while_inferencing/custom_model_for_slow_inference.sh build" - "multi-model-server --start --model-store=tmp_store > /dev/null 2>&1" @@ -24,6 +25,7 @@ services: - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "./tests/unregister_while_inferencing/custom_model_for_slow_inference.sh clean" - "rm kitten.jpg" - module: server_local_monitoring diff --git a/tests/performance/tests/health_check/health_check.yaml b/tests/performance/tests/health_check/health_check.yaml index 2c8785e3c..c1a269773 100644 --- a/tests/performance/tests/health_check/health_check.yaml +++ b/tests/performance/tests/health_check/health_check.yaml @@ -16,10 +16,12 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "multi-model-server --start > /dev/null 2>&1" - "sleep 10s" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - module: server_local_monitoring ServerLocalClient: - interval: 1s diff --git a/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml b/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml index 3244c4d8f..cbffe2b68 100644 --- a/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml +++ b/tests/performance/tests/inference_multiple_models/inference_multiple_models.yaml @@ -16,6 +16,7 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" @@ -25,6 +26,7 @@ services: - "curl -s -X PUT http://localhost:8081/models/resnet-18?min_worker=1&synchronous=true" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: server_local_monitoring ServerLocalClient: diff --git a/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml b/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml index 5d73624a6..b5fa23c01 100644 --- a/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml +++ b/tests/performance/tests/inference_multiple_worker/inference_multiple_worker.yaml @@ -17,6 +17,7 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" @@ -24,6 +25,7 @@ services: - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: server_local_monitoring ServerLocalClient: diff --git a/tests/performance/tests/inference_single_worker/inference_single_worker.yaml b/tests/performance/tests/inference_single_worker/inference_single_worker.yaml index ece9e5b74..2797a69aa 100644 --- a/tests/performance/tests/inference_single_worker/inference_single_worker.yaml +++ b/tests/performance/tests/inference_single_worker/inference_single_worker.yaml @@ -17,6 +17,7 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" @@ -24,6 +25,7 @@ services: - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: server_local_monitoring ServerLocalClient: diff --git a/tests/performance/tests/list_models/list_models.yaml b/tests/performance/tests/list_models/list_models.yaml index 81dd8ada7..d571dcdcd 100644 --- a/tests/performance/tests/list_models/list_models.yaml +++ b/tests/performance/tests/list_models/list_models.yaml @@ -16,12 +16,14 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/shufflenet.mar" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - module: server_local_monitoring ServerLocalClient: - interval: 1s diff --git a/tests/performance/tests/model_description/model_description.yaml b/tests/performance/tests/model_description/model_description.yaml index 05358a53c..7a9a28a35 100644 --- a/tests/performance/tests/model_description/model_description.yaml +++ b/tests/performance/tests/model_description/model_description.yaml @@ -16,12 +16,14 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - module: server_local_monitoring ServerLocalClient: - interval: 1s diff --git a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml index 8f3324f2a..6056e9a90 100644 --- a/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml +++ b/tests/performance/tests/multiple_inference_and_scaling/multiple_inference_and_scaling.yaml @@ -16,6 +16,7 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" @@ -25,6 +26,7 @@ services: - "curl -s -X PUT http://localhost:8081/models/resnet-18?min_worker=1&synchronous=true" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: server_local_monitoring ServerLocalClient: diff --git a/tests/performance/tests/register_unregister/register_unregister.yaml b/tests/performance/tests/register_unregister/register_unregister.yaml index 1feb22487..295010d69 100644 --- a/tests/performance/tests/register_unregister/register_unregister.yaml +++ b/tests/performance/tests/register_unregister/register_unregister.yaml @@ -17,11 +17,13 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 10s" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: server_local_monitoring ServerLocalClient: diff --git a/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml b/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml index 22753ef05..276dc1dde 100644 --- a/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml @@ -9,5 +9,5 @@ settings: UNREG_RT: 150ms TOTAL_PROCS_WITH_EXTRA_MODEL_AND_WORKERS : 5 TOTAL_PROCS_WITHOUT_EXTRA_MODEL : 2 - TOTAL_FDS : 509 + TOTAL_FDS : 524 FRNTEND_MEM : 425000000 #425MB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml index 29dc4c755..bc00761c6 100644 --- a/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml +++ b/tests/performance/tests/register_unregister_multiple/register_unregister_multiple.yaml @@ -16,12 +16,14 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "curl -s -O https://s3.amazonaws.com/model-server/inputs/kitten.jpg" - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - "rm kitten.jpg" - module: server_local_monitoring ServerLocalClient: diff --git a/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml index cedc9e9c3..f65c32c11 100644 --- a/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/scale_down_workers/environments/ci_linux_medium.yaml @@ -9,7 +9,7 @@ settings: TOTAL_WRKRS_AFTR_SCL_DWN : 2 FRNTEND_FDS : 494 TOTAL_WRKRS_FDS_B4_SCL_DWN: 43 - TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 24 + TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 17 FRNTEND_MEM : 770000000 #770MB TOTAL_WRKRS_MEM_B4_SCL_DWN : 430000000 #430MB - TOTAL_WRKRS_MEM_AFTR_SCL_DWN : 220000000 #220MB \ No newline at end of file + TOTAL_WRKRS_MEM_AFTR_SCL_DWN : 200000000 #200MB \ No newline at end of file diff --git a/tests/performance/tests/scale_down_workers/scale_down_workers.yaml b/tests/performance/tests/scale_down_workers/scale_down_workers.yaml index dc8cc1382..b9f92ffbc 100644 --- a/tests/performance/tests/scale_down_workers/scale_down_workers.yaml +++ b/tests/performance/tests/scale_down_workers/scale_down_workers.yaml @@ -16,12 +16,14 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=4&synchronous=true" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - module: server_local_monitoring ServerLocalClient: - interval: 1s diff --git a/tests/performance/tests/scale_up_workers/scale_up_workers.yaml b/tests/performance/tests/scale_up_workers/scale_up_workers.yaml index 125aff830..a973eca52 100644 --- a/tests/performance/tests/scale_up_workers/scale_up_workers.yaml +++ b/tests/performance/tests/scale_up_workers/scale_up_workers.yaml @@ -16,12 +16,14 @@ modules: services: - module: shellexec prepare: + - "ps aux | grep '[c]om.amazonaws.ml.mms.ModelServer' | awk '{print $2}' | xargs kill -9" - "multi-model-server --start > /dev/null 2>&1" - "sleep 20s" - "curl -s -X POST http://localhost:8081/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" - "curl -s -X PUT http://localhost:8081/models/squeezenet_v1.1?min_worker=1&synchronous=true" post-process: - "multi-model-server --stop > /dev/null 2>&1" + - "sleep 10" - module: server_local_monitoring ServerLocalClient: - interval: 1s From ece60b1a4df7d086046f48f1aa8d566b7788c6a2 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 8 Jul 2020 21:28:28 +0530 Subject: [PATCH 317/329] API tests execute from tests/api directory Execute all 3 collectiosn from API suite irrespective of faliure spelling corrections modelarchiver build from .whl benchmark symlink simplified benchmark store the entire output directory updated build step name --- .circleci/config.yml | 6 ++--- .circleci/scripts/linux_test_api.sh | 25 +++++++++++++------ .circleci/scripts/linux_test_benchmark.sh | 5 ++-- .circleci/scripts/linux_test_modelarchiver.sh | 3 ++- run_circleci_tests.py | 4 +-- 5 files changed, 27 insertions(+), 16 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ab75c61c5..298c6532d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,6 +40,7 @@ commands: - run: name: Start MMS, Execute << parameters.collection >> API Tests, Stop MMS command: .circleci/scripts/linux_test_api.sh << parameters.collection >> + when: always jobs: @@ -51,7 +52,7 @@ jobs: steps: - checkout - run: - name: Build frontend + name: Build MMS command: .circleci/scripts/linux_build.sh - store_artifacts: name: Store gradle testng results @@ -110,8 +111,7 @@ jobs: path: logs/ - store_artifacts: name: Store Benchmark Latency resnet-18 results - path: /tmp/MMSBenchmark/out/latency/resnet-18/report/ - destination: benchmark-latency-resnet-18 + path: /tmp/MMSBenchmark performance-regression: parameters: diff --git a/.circleci/scripts/linux_test_api.sh b/.circleci/scripts/linux_test_api.sh index 837fce735..d8d128a02 100755 --- a/.circleci/scripts/linux_test_api.sh +++ b/.circleci/scripts/linux_test_api.sh @@ -1,21 +1,21 @@ #!/bin/bash BASE_DIR="tests/api" -MODEL_STORE_DIR="$BASE_DIR/model_store" +MODEL_STORE_DIR="model_store" -ARTIFACTS_BASE_DIR="$BASE_DIR/artifacts" +ARTIFACTS_BASE_DIR="artifacts" ARTIFACTS_MANAGEMENT_DIR="$ARTIFACTS_BASE_DIR/management" ARTIFACTS_INFERENCE_DIR="$ARTIFACTS_BASE_DIR/inference" ARTIFACTS_HTTPS_DIR="$ARTIFACTS_BASE_DIR/https" MMS_CONSOLE_LOG_FILE="mms_console.log" -MMS_CONFIG_FILE_HTTPS="$BASE_DIR/resources/config.properties" +MMS_CONFIG_FILE_HTTPS="resources/config.properties" -POSTMAN_ENV_FILE="$BASE_DIR/postman/environment.json" -POSTMAN_COLLECTION_MANAGEMENT="$BASE_DIR/postman/management_api_test_collection.json" -POSTMAN_COLLECTION_INFERENCE="$BASE_DIR/postman/inference_api_test_collection.json" -POSTMAN_COLLECTION_HTTPS="$BASE_DIR/postman/https_test_collection.json" -POSTMAN_DATA_FILE_INFERENCE="$BASE_DIR/postman/inference_data.json" +POSTMAN_ENV_FILE="postman/environment.json" +POSTMAN_COLLECTION_MANAGEMENT="postman/management_api_test_collection.json" +POSTMAN_COLLECTION_INFERENCE="postman/inference_api_test_collection.json" +POSTMAN_COLLECTION_HTTPS="postman/https_test_collection.json" +POSTMAN_DATA_FILE_INFERENCE="postman/inference_data.json" REPORT_FILE="report.html" @@ -31,6 +31,11 @@ start_mms_secure_server() { stop_mms_server() { multi-model-server --stop + sleep 10 +} + +cleanup_model_store(){ + rm -rf $MODEL_STORE_DIR/* } move_logs(){ @@ -45,6 +50,7 @@ trigger_management_tests(){ local EXIT_CODE=$? stop_mms_server move_logs $MMS_CONSOLE_LOG_FILE $ARTIFACTS_MANAGEMENT_DIR + cleanup_model_store return $EXIT_CODE } @@ -55,6 +61,7 @@ trigger_inference_tests(){ local EXIT_CODE=$? stop_mms_server move_logs $MMS_CONSOLE_LOG_FILE $ARTIFACTS_INFERENCE_DIR + cleanup_model_store return $EXIT_CODE } @@ -65,9 +72,11 @@ trigger_https_tests(){ local EXIT_CODE=$? stop_mms_server move_logs $MMS_CONSOLE_LOG_FILE $ARTIFACTS_HTTPS_DIR + cleanup_model_store return $EXIT_CODE } +cd $BASE_DIR mkdir -p $MODEL_STORE_DIR $ARTIFACTS_MANAGEMENT_DIR $ARTIFACTS_INFERENCE_DIR $ARTIFACTS_HTTPS_DIR case $1 in diff --git a/.circleci/scripts/linux_test_benchmark.sh b/.circleci/scripts/linux_test_benchmark.sh index d4f45832f..a14585a5f 100755 --- a/.circleci/scripts/linux_test_benchmark.sh +++ b/.circleci/scripts/linux_test_benchmark.sh @@ -2,8 +2,9 @@ # Hack needed to make it work with existing benchmark.py # benchmark.py expects jmeter to be present at a very specific location -mkdir -p /home/ubuntu/.linuxbrew/Cellar/jmeter/5.3/libexec/bin/ -ln -s /opt/apache-jmeter-5.3/bin/jmeter /home/ubuntu/.linuxbrew/Cellar/jmeter/5.3/libexec/bin/jmeter +mkdir -p /home/linuxbrew/.linuxbrew/Homebrew/Cellar/jmeter/5.3/libexec/ +ln -s /opt/apache-jmeter-5.3/lib/ /home/linuxbrew/.linuxbrew/Homebrew/Cellar/jmeter/5.3/libexec/lib +ln -s /opt/apache-jmeter-5.3/bin/ /home/linuxbrew/.linuxbrew/Homebrew/Cellar/jmeter/5.3/libexec/bin # Start MMS and redirect console ouptut and errors to a log file multi-model-server --start >> mms_console.log 2>&1 diff --git a/.circleci/scripts/linux_test_modelarchiver.sh b/.circleci/scripts/linux_test_modelarchiver.sh index 4bd4eb252..d08ad476d 100755 --- a/.circleci/scripts/linux_test_modelarchiver.sh +++ b/.circleci/scripts/linux_test_modelarchiver.sh @@ -12,7 +12,8 @@ PY_UNITS_EXIT_CODE=$? # Install model archiver module -pip install . +python setup.py bdist_wheel --universal && \ +pip install dist/*.whl MODL_ARCHVR_EXIT_CODE=$? # Execute integration tests diff --git a/run_circleci_tests.py b/run_circleci_tests.py index 5001c0006..1eb6196de 100755 --- a/run_circleci_tests.py +++ b/run_circleci_tests.py @@ -3,9 +3,9 @@ - This script helps to execute circleci jobs in a container on developer's local machine. - The script accepts workflow(mandatory), job(optional) and executor(optional) arguments. - The script used circleci cli's process command to generate a processed yaml. -- The processed yaml, is parsed and twekaed to generate a new transformed yaml. +- The processed yaml, is parsed and tweaked to generate a new transformed yaml. - The transformed yaml contains a single job, which is merged and ordered list of job steps -from the specfied and requird parent jobs. +from the specified and required parent jobs. """ # Make sure you have following dependencies installed on your local machine From 6de300ad251703f9493a189bdab6956c660db3af Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Wed, 8 Jul 2020 22:56:17 +0530 Subject: [PATCH 318/329] Psail test ci perf reg (#14) * benchmark symlinks corrected * api tests relative paths updated --- .circleci/scripts/linux_test_benchmark.sh | 6 +-- tests/api/postman/https_test_collection.json | 2 +- tests/api/postman/inference_data.json | 56 ++++++++++---------- tests/api/regression_tests.sh | 2 +- tests/api/resources/config.properties | 4 +- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.circleci/scripts/linux_test_benchmark.sh b/.circleci/scripts/linux_test_benchmark.sh index a14585a5f..98e028ee2 100755 --- a/.circleci/scripts/linux_test_benchmark.sh +++ b/.circleci/scripts/linux_test_benchmark.sh @@ -2,9 +2,9 @@ # Hack needed to make it work with existing benchmark.py # benchmark.py expects jmeter to be present at a very specific location -mkdir -p /home/linuxbrew/.linuxbrew/Homebrew/Cellar/jmeter/5.3/libexec/ -ln -s /opt/apache-jmeter-5.3/lib/ /home/linuxbrew/.linuxbrew/Homebrew/Cellar/jmeter/5.3/libexec/lib -ln -s /opt/apache-jmeter-5.3/bin/ /home/linuxbrew/.linuxbrew/Homebrew/Cellar/jmeter/5.3/libexec/bin +mkdir -p /home/ubuntu/.linuxbrew/Cellar/jmeter/5.3/libexec/ +ln -s /opt/apache-jmeter-5.3/lib/ /home/ubuntu/.linuxbrew/Cellar/jmeter/5.3/libexec/lib +ln -s /opt/apache-jmeter-5.3/bin/ /home/ubuntu/.linuxbrew/Cellar/jmeter/5.3/libexec/bin # Start MMS and redirect console ouptut and errors to a log file multi-model-server --start >> mms_console.log 2>&1 diff --git a/tests/api/postman/https_test_collection.json b/tests/api/postman/https_test_collection.json index 175f1bc42..5e21dc48d 100644 --- a/tests/api/postman/https_test_collection.json +++ b/tests/api/postman/https_test_collection.json @@ -273,7 +273,7 @@ "body": { "mode": "file", "file": { - "src": "../examples/image_classifier/kitten.jpg" + "src": "resources/kitten.jpg" }, "options": { "raw": { diff --git a/tests/api/postman/inference_data.json b/tests/api/postman/inference_data.json index f71b38028..9cfd15ef5 100644 --- a/tests/api/postman/inference_data.json +++ b/tests/api/postman/inference_data.json @@ -4,7 +4,7 @@ "model_name":"alexnet", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -36,7 +36,7 @@ "model_name":"caffenet", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -68,7 +68,7 @@ "model_name":"inception_v1", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -100,7 +100,7 @@ "model_name":"inception-bn", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -132,7 +132,7 @@ "model_name":"mobilenet", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -164,7 +164,7 @@ "model_name":"nin", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -196,7 +196,7 @@ "model_name":"resnet-152", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -228,7 +228,7 @@ "model_name":"resnet-18", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -260,7 +260,7 @@ "model_name":"resnext101", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -292,7 +292,7 @@ "model_name":"resnet18-v1", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -324,7 +324,7 @@ "model_name":"resnet34-v1", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -356,7 +356,7 @@ "model_name":"resnet50-v1", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -388,7 +388,7 @@ "model_name":"resnet101-v1", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -420,7 +420,7 @@ "model_name":"resnet152-v1", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -452,7 +452,7 @@ "model_name":"resnet18-v2", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -484,7 +484,7 @@ "model_name":"resnet34-v2", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -516,7 +516,7 @@ "model_name":"resnet50-v2", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -548,7 +548,7 @@ "model_name":"resnet101-v2", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -580,7 +580,7 @@ "model_name":"resnet152-v2", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -612,7 +612,7 @@ "model_name":"shufflenet", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -644,7 +644,7 @@ "model_name":"onnx-squeezenet", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -676,7 +676,7 @@ "model_name":"squeezenet_v1.1", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -708,7 +708,7 @@ "model_name":"vgg16", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -740,7 +740,7 @@ "model_name":"onnx-vgg16", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -772,7 +772,7 @@ "model_name":"onnx-vgg16_bn", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -804,7 +804,7 @@ "model_name":"vgg19", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -836,7 +836,7 @@ "model_name":"onnx-vgg19", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ @@ -868,7 +868,7 @@ "model_name":"onnx-vgg19_bn", "worker":1, "synchronous":"true", - "file":"tests/api/resources/kitten.jpg", + "file":"resources/kitten.jpg", "content-type":"application/json", "validator":"image_classification", "expected":[ diff --git a/tests/api/regression_tests.sh b/tests/api/regression_tests.sh index 1fc112be9..d9eeee730 100755 --- a/tests/api/regression_tests.sh +++ b/tests/api/regression_tests.sh @@ -57,7 +57,7 @@ start_secure_mms() { run_postman_test() { # Run Postman Scripts mkdir $ROOT_DIR/report/ - cd $CODEBUILD_WD/ + cd $CODEBUILD_WD/tests/api/ set +e # Run Management API Tests stop_mms_serve diff --git a/tests/api/resources/config.properties b/tests/api/resources/config.properties index 6cb3b3299..ed79a84c2 100644 --- a/tests/api/resources/config.properties +++ b/tests/api/resources/config.properties @@ -1,4 +1,4 @@ inference_address=https://127.0.0.1:8443 management_address=https://127.0.0.1:8444 -private_key_file=tests/api/resources/key.pem -certificate_file=tests/api/resources/certs.pem +private_key_file=resources/key.pem +certificate_file=resources/certs.pem From 5687c2b76ca972e8a06c68a29d2e9fc466dc636e Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 9 Jul 2020 11:27:17 +0530 Subject: [PATCH 319/329] removed cruft --- .../imageInputModelPlan.jmx.yaml | 51 - test/README.md | 52 - test/postman/environment.json | 44 - test/postman/https_test_collection.json | 335 ------ .../inference_api_test_collection.json | 204 ---- test/postman/inference_data.json | 898 ----------------- .../management_api_test_collection.json | 952 ------------------ test/regression_tests.sh | 100 -- test/resources/certs.pem | 16 - test/resources/config.properties | 4 - test/resources/key.pem | 15 - test/resources/kitten.jpg | Bin 110969 -> 0 bytes test/screenshot/postman.png | Bin 135879 -> 0 bytes 13 files changed, 2671 deletions(-) delete mode 100644 performance_regression/imageInputModelPlan.jmx.yaml delete mode 100644 test/README.md delete mode 100644 test/postman/environment.json delete mode 100644 test/postman/https_test_collection.json delete mode 100644 test/postman/inference_api_test_collection.json delete mode 100644 test/postman/inference_data.json delete mode 100644 test/postman/management_api_test_collection.json delete mode 100644 test/regression_tests.sh delete mode 100644 test/resources/certs.pem delete mode 100644 test/resources/config.properties delete mode 100644 test/resources/key.pem delete mode 100644 test/resources/kitten.jpg delete mode 100644 test/screenshot/postman.png diff --git a/performance_regression/imageInputModelPlan.jmx.yaml b/performance_regression/imageInputModelPlan.jmx.yaml deleted file mode 100644 index c662874d0..000000000 --- a/performance_regression/imageInputModelPlan.jmx.yaml +++ /dev/null @@ -1,51 +0,0 @@ ---- -execution: -- concurrency: 1 - ramp-up: 5s - hold-for: 1m - scenario: Inference -scenarios: - Inference: - default-address: ${__P(protocol,https)}://${__P(hostname,127.0.0.1)}:${__P(port,8443)}/ - requests: - - follow-redirects: true - label: Inference Request - method: POST - upload-files: - - mime-type: image/jpeg - param: data - path: ${__P(input_filepath)} - url: ${__P(protocol,http)}://${__P(hostname,127.0.0.1)}:${__P(port,8080)}/predictions/${model} - store-cache: false - store-cookie: false - use-dns-cache-mgr: false - variables: - cnn_url: ${__P(url, https://s3.amazonaws.com/model-server/models/squeezenet_v1.1/squeezenet_v1.1.model)} - model: ${__P(model_name,squeezenet_v1.1)} - scale_down_workers: '0' - scale_up_workers: ${__P(min_workers,1)} -modules: - jmeter: - properties: - input_filepath : kitten.jpg - model_name : squeezenet -services: - - module: monitoring - local: - - interval: 2s - logging: True - metrics: - - cpu - - disk-space - - mem -reporting: -- module: passfail - criteria: - - class: bzt.modules.monitoring.MonitoringCriteria - subject: local/cpu - condition: '>' - threshold: 100 - timeframe: 6s -- module: junit-xml - filename: ${TAURUS_ARTIFACTS_DIR}/output/results.xml - data-source: pass-fail diff --git a/test/README.md b/test/README.md deleted file mode 100644 index cc30f24cd..000000000 --- a/test/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# MMS Regression Tests - -This folder contains regression tests executed against MMS master.These tests use [POSTMAN](https://www.postman.com/downloads/) for exercising all the Management & Inference APIs. - -### Running the test manually. - -Pull multi-model-server pre build docker image -``` -docker pull awsdeeplearningteam/multi-model-server -``` - -This would build a docker Image with a awsdeeplearningteam/multi-model-server:latest in which we would run our Regression Tests. - -``` -docker run -it --user root awsdeeplearningteam/multi-model-server:latest /bin/bash -``` - -In the Docker CLI execute the following cmds. - -``` -apt-get update -apt-get install -y git wget sudo -git clone https://github.com/awslabs/multi-model-server.git -cd multi-model-server -``` -To execute tests on master run: - -`./test/regression_tests.sh ` - -To execute tests on different run: - -`./test/regression_tests.sh ` - - -You can view the logs for Test execution & the Multi-model-server in the /tmp dir. - -``` -cat /tmp/test_exec.log -cat /tmp/mms.log -``` - -### Adding tests - -To add to the tests, import a collection (in /postman) to Postman and add new requests. -Specifically to test for inference against a new model -* Open /postman/inference_data.json -* Add new json object with the new model url and payload. - -![POSTMAN UI](screenshot/postman.png) - -Afterwards, export the collection as a v2.1 collection and replace the existing exported collection. -To add a new suite of tests, add a new collection to /postman and update regression_tests.sh to run the new collection and buldsepc.yml to keep track of the report. diff --git a/test/postman/environment.json b/test/postman/environment.json deleted file mode 100644 index 920aef558..000000000 --- a/test/postman/environment.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "id": "b9eb6b86-585b-4fca-abd0-972ec686e3e8", - "name": "Multi-Model-server", - "values": [ - { - "key": "hostname", - "value": "localhost", - "enabled": true - }, - { - "key": "protocol", - "value": "http", - "enabled": true - }, - { - "key": "mgmt-port", - "value": "8081", - "enabled": true - }, - { - "key": "model-name", - "value": "densenet161", - "enabled": true - }, - { - "key": "pred-port", - "value": "8080", - "enabled": true - }, - { - "key": "sec-mgmt-port", - "value": "8444", - "enabled": true - }, - { - "key": "sec-pred-port", - "value": "8443", - "enabled": true - } - ], - "_postman_variable_scope": "environment", - "_postman_exported_at": "2020-05-15T07:34:40.974Z", - "_postman_exported_using": "Postman/7.24.0" -} \ No newline at end of file diff --git a/test/postman/https_test_collection.json b/test/postman/https_test_collection.json deleted file mode 100644 index 175f1bc42..000000000 --- a/test/postman/https_test_collection.json +++ /dev/null @@ -1,335 +0,0 @@ -{ - "info": { - "_postman_id": "cd002524-ce12-4ff7-ab75-932a05403f83", - "name": "MMS - https_test_collection", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "HTTPS Inference API Description", - "event": [ - { - "listen": "test", - "script": { - "id": "638ec081-ebf4-4634-a9ea-f675613c2127", - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.have.status(200);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "OPTIONS", - "header": [], - "url": { - "raw": "https://{{hostname}}:{{sec-pred-port}}", - "protocol": "https", - "host": [ - "{{hostname}}" - ], - "port": "{{sec-pred-port}}" - } - }, - "response": [] - }, - { - "name": "HTTPS Management API Description", - "event": [ - { - "listen": "test", - "script": { - "id": "81b8730a-0b89-4569-b042-1076266563ba", - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.have.status(200);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "OPTIONS", - "header": [], - "url": { - "raw": "https://{{hostname}}:{{sec-pred-port}}", - "protocol": "https", - "host": [ - "{{hostname}}" - ], - "port": "{{sec-pred-port}}" - } - }, - "response": [] - }, - { - "name": "HTTPS Register Model - SqueezeNet", - "event": [ - { - "listen": "test", - "script": { - "id": "7e9a9528-e9f1-446c-b1ba-7dce112ffa30", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "https://{{hostname}}:{{sec-mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar&model_name=squeezenetv1_1&initial_workers=1&synchronous=true", - "protocol": "https", - "host": [ - "{{hostname}}" - ], - "port": "{{sec-mgmt-port}}", - "path": [ - "models" - ], - "query": [ - { - "key": "url", - "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" - }, - { - "key": "model_name", - "value": "squeezenetv1_1" - }, - { - "key": "initial_workers", - "value": "1" - }, - { - "key": "synchronous", - "value": "true" - } - ] - } - }, - "response": [] - }, - { - "name": "HTTPS Get SqueezeNet Model Description", - "event": [ - { - "listen": "test", - "script": { - "id": "cd11b9cc-335a-415a-8315-54ddda6f6d8a", - "exec": [ - "pm.test(\"Successful GET request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "https://{{hostname}}:{{sec-mgmt-port}}/models/squeezenetv1_1", - "protocol": "https", - "host": [ - "{{hostname}}" - ], - "port": "{{sec-mgmt-port}}", - "path": [ - "models", - "squeezenetv1_1" - ] - } - }, - "response": [] - }, - { - "name": "HTTPS Scale up Workers - Synchronous", - "event": [ - { - "listen": "test", - "script": { - "id": "ba06ca94-e630-4f72-adf4-49d1a80ded31", - "exec": [ - "pm.test(\"Successful PUT request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200, 201, 202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "url": { - "raw": "https://{{hostname}}:{{sec-mgmt-port}}/models/squeezenetv1_1?min_worker=5&max_worker=5&synchronous=true", - "protocol": "https", - "host": [ - "{{hostname}}" - ], - "port": "{{sec-mgmt-port}}", - "path": [ - "models", - "squeezenetv1_1" - ], - "query": [ - { - "key": "min_worker", - "value": "5" - }, - { - "key": "max_worker", - "value": "5" - }, - { - "key": "synchronous", - "value": "true" - } - ] - } - }, - "response": [] - }, - { - "name": "HTTPS Scale up Workers - Asynchronous", - "event": [ - { - "listen": "test", - "script": { - "id": "5a13e310-65a9-4982-84c7-d89f29d6c27a", - "exec": [ - "pm.test(\"Successful PUT request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "url": { - "raw": "https://{{hostname}}:{{sec-mgmt-port}}/models/squeezenetv1_1?min_worker=6&max_worker=6&synchronous=false", - "protocol": "https", - "host": [ - "{{hostname}}" - ], - "port": "{{sec-mgmt-port}}", - "path": [ - "models", - "squeezenetv1_1" - ], - "query": [ - { - "key": "min_worker", - "value": "6" - }, - { - "key": "max_worker", - "value": "6" - }, - { - "key": "synchronous", - "value": "false" - } - ] - } - }, - "response": [] - }, - { - "name": "HTTPS - Inference - SqueezeNet", - "event": [ - { - "listen": "test", - "script": { - "id": "7c1c4eaa-48f8-4734-8737-78b4b2766b29", - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.have.status(200);", - "});" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "content-type": true - } - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "file", - "file": { - "src": "../examples/image_classifier/kitten.jpg" - }, - "options": { - "raw": { - "language": "text" - } - } - }, - "url": { - "raw": "https://{{hostname}}:{{sec-pred-port}}/predictions/squeezenetv1_1", - "protocol": "https", - "host": [ - "{{hostname}}" - ], - "port": "{{sec-pred-port}}", - "path": [ - "predictions", - "squeezenetv1_1" - ] - } - }, - "response": [] - }, - { - "name": "HTTPS UnRegister Model SqueezeNet", - "event": [ - { - "listen": "test", - "script": { - "id": "de94e7b6-d4fa-4e10-8b54-4052753de19e", - "exec": [ - "pm.test(\"Successful DELETE request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "https://{{hostname}}:{{sec-mgmt-port}}/models/squeezenetv1_1", - "protocol": "https", - "host": [ - "{{hostname}}" - ], - "port": "{{sec-mgmt-port}}", - "path": [ - "models", - "squeezenetv1_1" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} -} \ No newline at end of file diff --git a/test/postman/inference_api_test_collection.json b/test/postman/inference_api_test_collection.json deleted file mode 100644 index 8226030d5..000000000 --- a/test/postman/inference_api_test_collection.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "info": { - "_postman_id": "e69000d9-d3c8-49bd-879a-ad42b95b042a", - "name": "MMS - inference_api_collection", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "Model Zoo - Register Model", - "event": [ - { - "listen": "test", - "script": { - "id": "80fa33ea-ff6a-4535-9328-ddffb980062a", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url={{url}}&model_name={{model_name}}&initial_workers={{worker}}&synchronous={{synchronous}}", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models" - ], - "query": [ - { - "key": "url", - "value": "{{url}}" - }, - { - "key": "model_name", - "value": "{{model_name}}" - }, - { - "key": "initial_workers", - "value": "{{worker}}" - }, - { - "key": "synchronous", - "value": "{{synchronous}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Model Zoo - Inference Model", - "event": [ - { - "listen": "test", - "script": { - "id": "d6b1f2cf-6ffb-4850-b276-108f7f65fbd9", - "exec": [ - "var type_response = pm.iterationData.get(\"content-type\");", - "validators = {", - " image_classification: validate_image_classification,", - " default_json: validate_default", - "};", - "", - "pm.test(\"Successful POST request\", function() {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});", - "", - "if (type_response === \"text/plain\") {", - " pm.test(\"Test expected TEXT response\", function() {", - " pm.response.to.have.body(pm.iterationData.get(\"expected\"));", - " });", - "", - "} else if (type_response === \"application/json\") {", - " if (pm.iterationData.has(\"validator\")) {", - " var validator = pm.iterationData.get(\"validator\"); ", - " } else {", - " var validator = \"default_json\";", - " }", - " pm.test(\"Test expected JSON response\", function() {", - " var actual_obj = pm.response.json();", - " var expected_obj = pm.iterationData.get(\"expected\");", - " pm.expect(validators[validator](actual_obj, expected_obj)).to.be.true;", - " });", - "", - "}", - "", - "function get_tolerance_value(expected_val) {", - " var tolerance_percent = pm.iterationData.get(\"tolerance\")", - " return (expected_val * tolerance_percent) / 100;", - " ", - "}", - "", - "function validate_image_classification(actual_obj,expected_obj) {", - " if (_.size(expected_obj) != _.size(actual_obj)) {", - " return false;", - " }", - "", - " for (i = 0; i < expected_obj.length; i += 1) {", - " if(actual_obj[i][\"class\"] !== expected_obj[i][\"class\"]){", - " return false", - " }", - "", - " expected_val = expected_obj[i][\"probability\"]", - " actual_val = actual_obj[i][\"probability\"]", - "", - " tolerance_value = get_tolerance_value(expected_val);", - " if (!(Math.abs(expected_val - actual_val) < tolerance_value)) {", - " return false;", - " }", - " }", - " return true;", - "}", - "", - "", - "", - "/* Simple and nested json object can be compared using validate_default when key and value are constant.", - "-Notes-", - "The order of keys within an object may change.", - "If the output is array of objects then the objects compared are positional and cannot change order.", - "*/", - "function validate_default(actual_obj, expected_obj) {", - " return _.isEqual(actual_obj, expected_obj);", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disabledSystemHeaders": { - "content-type": true - } - }, - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "file", - "file": { - "src": "{{file}}" - } - }, - "url": { - "raw": "{{protocol}}://{{hostname}}:{{pred-port}}/predictions/{{model_name}}", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{pred-port}}", - "path": [ - "predictions", - "{{model_name}}" - ] - } - }, - "response": [] - }, - { - "name": "Model Zoo - Unregister model", - "event": [ - { - "listen": "test", - "script": { - "id": "a14dd390-4176-45e7-af00-999676685f4a", - "exec": [ - "pm.test(\"Successful DELETE request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/{{model_name}}", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "{{model_name}}" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} -} \ No newline at end of file diff --git a/test/postman/inference_data.json b/test/postman/inference_data.json deleted file mode 100644 index b4eb7fb8a..000000000 --- a/test/postman/inference_data.json +++ /dev/null @@ -1,898 +0,0 @@ -[ - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/alexnet.mar", - "model_name":"alexnet", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.4766186773777008, - "class":"n02127052 lynx, catamount" - }, - { - "probability":0.20687074959278107, - "class":"n02128757 snow leopard, ounce, Panthera uncia" - }, - { - "probability":0.135288268327713, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.09019536525011063, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.04814659804105759, - "class":"n02123159 tiger cat" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/caffenet.mar", - "model_name":"caffenet", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.7166884541511536, - "class":"n02127052 lynx, catamount" - }, - { - "probability":0.09750154614448547, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.0745730996131897, - "class":"n02123159 tiger cat" - }, - { - "probability":0.06743090599775314, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.03200334310531616, - "class":"n02128757 snow leopard, ounce, Panthera uncia" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-inception_v1.mar", - "model_name":"inception_v1", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.3833286464214325, - "class":"n02123159 tiger cat" - }, - { - "probability":0.33825597167015076, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.17002010345458984, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.09881989657878876, - "class":"n02127052 lynx, catamount" - }, - { - "probability":0.001223195344209671, - "class":"n02123394 Persian cat" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/inception-bn.mar", - "model_name":"inception-bn", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.5972931981086731, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.30086880922317505, - "class":"n02123159 tiger cat" - }, - { - "probability":0.08586657792329788, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.009726772084832191, - "class":"n02127052 lynx, catamount" - }, - { - "probability":0.0008877559448592365, - "class":"n03598930 jigsaw puzzle" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-mobilenet.mar", - "model_name":"mobilenet", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":216.0907745361328, - "class":"n02948072 candle, taper, wax light" - }, - { - "probability":186.8531494140625, - "class":"n04456115 torch" - }, - { - "probability":178.81483459472656, - "class":"n03347037 fire screen, fireguard" - }, - { - "probability":169.5437469482422, - "class":"n03666591 lighter, light, igniter, ignitor" - }, - { - "probability":145.08824157714844, - "class":"n09472597 volcano" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/nin.mar", - "model_name":"nin", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.978486180305481, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.021282507106661797, - "class":"n02123159 tiger cat" - }, - { - "probability":9.809057519305497e-05, - "class":"n02124075 Egyptian cat" - }, - { - "probability":8.281067130155861e-05, - "class":"n02128385 leopard, Panthera pardus" - }, - { - "probability":2.5670484319562092e-05, - "class":"n02128757 snow leopard, ounce, Panthera uncia" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-152.mar", - "model_name":"resnet-152", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.7149006128311157, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.22877003252506256, - "class":"n02123159 tiger cat" - }, - { - "probability":0.040323738008737564, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.008370742201805115, - "class":"n02127052 lynx, catamount" - }, - { - "probability":0.00067278987262398, - "class":"n02129604 tiger, Panthera tigris" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar", - "model_name":"resnet-18", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.3633013665676117, - "class":"n02123159 tiger cat" - }, - { - "probability":0.2988913953304291, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.18073132634162903, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.07343611121177673, - "class":"n02127052 lynx, catamount" - }, - { - "probability":0.02035967819392681, - "class":"n02128757 snow leopard, ounce, Panthera uncia" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/resnext-101-64x4d.mar", - "model_name":"resnext101", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.5961099863052368, - "class":"n02123159 tiger cat" - }, - { - "probability":0.4005117118358612, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.0012956340797245502, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.0011538631515577435, - "class":"n04074963 remote control, remote" - }, - { - "probability":0.000405249185860157, - "class":"n04286575 spotlight, spot" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet18v1.mar", - "model_name":"resnet18-v1", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":16.35452651977539, - "class":"n02999410 chain" - }, - { - "probability":16.15776824951172, - "class":"n10148035 groom, bridegroom" - }, - { - "probability":15.857562065124512, - "class":"n04141076 sax, saxophone" - }, - { - "probability":15.083999633789062, - "class":"n04507155 umbrella" - }, - { - "probability":14.938633918762207, - "class":"n09229709 bubble" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet34v1.mar", - "model_name":"resnet34-v1", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":12.617600440979004, - "class":"n04286575 spotlight, spot" - }, - { - "probability":10.216148376464844, - "class":"n03637318 lampshade, lamp shade" - }, - { - "probability":9.676478385925293, - "class":"n03942813 ping-pong ball" - }, - { - "probability":9.529446601867676, - "class":"n02708093 analog clock" - }, - { - "probability":9.494606018066406, - "class":"n03691459 loudspeaker, speaker, speaker unit, loudspeaker system, speaker system" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet50v1.mar", - "model_name":"resnet50-v1", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":177.36087036132812, - "class":"n03982430 pool table, billiard table, snooker table" - }, - { - "probability":174.36256408691406, - "class":"n03942813 ping-pong ball" - }, - { - "probability":172.44488525390625, - "class":"n03661043 library" - }, - { - "probability":163.6439971923828, - "class":"n02788148 bannister, banister, balustrade, balusters, handrail" - }, - { - "probability":159.4976043701172, - "class":"n03065424 coil, spiral, volute, whorl, helix" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet101v1.mar", - "model_name":"resnet101-v1", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":107.36915588378906, - "class":"n02823428 beer bottle" - }, - { - "probability":99.51375579833984, - "class":"n04485082 tripod" - }, - { - "probability":95.99050903320312, - "class":"n04069434 reflex camera" - }, - { - "probability":84.3740463256836, - "class":"n04557648 water bottle" - }, - { - "probability":83.7496566772461, - "class":"n02841315 binoculars, field glasses, opera glasses" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet152v1.mar", - "model_name":"resnet152-v1", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":61.71363067626953, - "class":"n07930864 cup" - }, - { - "probability":60.88921356201172, - "class":"n03832673 notebook, notebook computer" - }, - { - "probability":60.11431121826172, - "class":"n03691459 loudspeaker, speaker, speaker unit, loudspeaker system, speaker system" - }, - { - "probability":53.656005859375, - "class":"n07920052 espresso" - }, - { - "probability":53.115779876708984, - "class":"n03492542 hard disc, hard disk, fixed disk" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet18v2.mar", - "model_name":"resnet18-v2", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":24.102333068847656, - "class":"n04553703 washbasin, handbasin, washbowl, lavabo, wash-hand basin" - }, - { - "probability":23.69866943359375, - "class":"n04254120 soap dispenser" - }, - { - "probability":19.214643478393555, - "class":"n02747177 ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin" - }, - { - "probability":18.93875503540039, - "class":"n03691459 loudspeaker, speaker, speaker unit, loudspeaker system, speaker system" - }, - { - "probability":18.90488052368164, - "class":"n04201297 shoji" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet34v2.mar", - "model_name":"resnet34-v2", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":9.2193603515625, - "class":"n02708093 analog clock" - }, - { - "probability":7.803028583526611, - "class":"n03782006 monitor" - }, - { - "probability":7.681037425994873, - "class":"n04286575 spotlight, spot" - }, - { - "probability":7.129834175109863, - "class":"n03028079 church, church building" - }, - { - "probability":7.0597100257873535, - "class":"n04152593 screen, CRT screen" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet50v2.mar", - "model_name":"resnet50-v2", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":15.76949691772461, - "class":"n06359193 web site, website, internet site, site" - }, - { - "probability":14.25102710723877, - "class":"n07565083 menu" - }, - { - "probability":13.321331024169922, - "class":"n03944341 pinwheel" - }, - { - "probability":11.99173641204834, - "class":"n06596364 comic book" - }, - { - "probability":11.768353462219238, - "class":"n03291819 envelope" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet101v2.mar", - "model_name":"resnet101-v2", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":7.887596130371094, - "class":"n04380533 table lamp" - }, - { - "probability":7.870771884918213, - "class":"n03627232 knot" - }, - { - "probability":7.605418682098389, - "class":"n02093859 Kerry blue terrier" - }, - { - "probability":7.55618143081665, - "class":"n04033995 quilt, comforter, comfort, puff" - }, - { - "probability":7.256267547607422, - "class":"n03884397 panpipe, pandean pipe, syrinx" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-resnet152v2.mar", - "model_name":"resnet152-v2", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":36.13174819946289, - "class":"n03970156 plunger, plumber's helper" - }, - { - "probability":29.110092163085938, - "class":"n02708093 analog clock" - }, - { - "probability":28.711875915527344, - "class":"n04152593 screen, CRT screen" - }, - { - "probability":28.073928833007812, - "class":"n01930112 nematode, nematode worm, roundworm" - }, - { - "probability":28.058176040649414, - "class":"n04404412 television, television system" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/shufflenet.mar", - "model_name":"shufflenet", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.0010000000474974513, - "class":"n03792972 mountain tent" - }, - { - "probability":0.0010000000474974513, - "class":"n03773504 missile" - }, - { - "probability":0.0010000000474974513, - "class":"n03775071 mitten" - }, - { - "probability":0.0010000000474974513, - "class":"n03775546 mixing bowl" - }, - { - "probability":0.0010000000474974513, - "class":"n03776460 mobile home, manufactured home" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-squeezenet.mar", - "model_name":"onnx-squeezenet", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.6482629179954529, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.2318260818719864, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.10045160353183746, - "class":"n02123159 tiger cat" - }, - { - "probability":0.013487360440194607, - "class":"n02127052 lynx, catamount" - }, - { - "probability":0.003135664388537407, - "class":"n02128757 snow leopard, ounce, Panthera uncia" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar", - "model_name":"squeezenet_v1.1", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.8582231402397156, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.09159984439611435, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.03748767822980881, - "class":"n02123159 tiger cat" - }, - { - "probability":0.006165081635117531, - "class":"n02128385 leopard, Panthera pardus" - }, - { - "probability":0.0031715999357402325, - "class":"n02127052 lynx, catamount" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/vgg16.mar", - "model_name":"vgg16", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.8178463578224182, - "class":"n02123159 tiger cat" - }, - { - "probability":0.12500879168510437, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.05412120372056961, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.0020657714921981096, - "class":"n02127052 lynx, catamount" - }, - { - "probability":0.0005614628316834569, - "class":"n02128757 snow leopard, ounce, Panthera uncia" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-vgg16.mar", - "model_name":"onnx-vgg16", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":101.13871765136719, - "class":"n02124075 Egyptian cat" - }, - { - "probability":89.77296447753906, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":88.40411376953125, - "class":"n02123159 tiger cat" - }, - { - "probability":76.17413330078125, - "class":"n02125311 cougar, puma, catamount, mountain lion, painter, panther, Felis concolor" - }, - { - "probability":74.09810638427734, - "class":"n07930864 cup" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-vgg16_bn.mar", - "model_name":"onnx-vgg16_bn", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":16.06477165222168, - "class":"n03196217 digital clock" - }, - { - "probability":13.64653491973877, - "class":"n04286575 spotlight, spot" - }, - { - "probability":13.565534591674805, - "class":"n03692522 loupe, jeweler's loupe" - }, - { - "probability":13.479013442993164, - "class":"n03388043 fountain" - }, - { - "probability":12.639715194702148, - "class":"n03666591 lighter, light, igniter, ignitor" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/vgg19.mar", - "model_name":"vgg19", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.5058671832084656, - "class":"n02123159 tiger cat" - }, - { - "probability":0.28164851665496826, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.20637290179729462, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.0046674045734107494, - "class":"n02127052 lynx, catamount" - }, - { - "probability":0.00011857607023557648, - "class":"n03598930 jigsaw puzzle" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-vgg19.mar", - "model_name":"onnx-vgg19", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":0.3949933350086212, - "class":"n02124075 Egyptian cat" - }, - { - "probability":0.34417903423309326, - "class":"n02123159 tiger cat" - }, - { - "probability":0.25566166639328003, - "class":"n02123045 tabby, tabby cat" - }, - { - "probability":0.0027325130067765713, - "class":"n02127052 lynx, catamount" - }, - { - "probability":0.0002417804644210264, - "class":"n07930864 cup" - } - ], - "tolerance":1 - }, - { - "url":"https://s3.amazonaws.com/model-server/model_archive_1.0/onnx-vgg19_bn.mar", - "model_name":"onnx-vgg19_bn", - "worker":1, - "synchronous":"true", - "file":"test/resources/kitten.jpg", - "content-type":"application/json", - "validator":"image_classification", - "expected":[ - { - "probability":17.26873016357422, - "class":"n04589890 window screen" - }, - { - "probability":16.25399398803711, - "class":"n03347037 fire screen, fireguard" - }, - { - "probability":16.093460083007812, - "class":"n04286575 spotlight, spot" - }, - { - "probability":16.02733039855957, - "class":"n04590129 window shade" - }, - { - "probability":15.910074234008789, - "class":"n03637318 lampshade, lamp shade" - } - ], - "tolerance":1 - } -] \ No newline at end of file diff --git a/test/postman/management_api_test_collection.json b/test/postman/management_api_test_collection.json deleted file mode 100644 index 35e22a625..000000000 --- a/test/postman/management_api_test_collection.json +++ /dev/null @@ -1,952 +0,0 @@ -{ - "info": { - "_postman_id": "5c45fe29-4bb1-4df4-9e13-a4fd410e4775", - "name": "MMS - management_api_collection", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "Register Model", - "event": [ - { - "listen": "test", - "script": { - "id": "4f706340-af84-431b-affd-6d6f53c89e80", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar&model_name=squeezenet1_1", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models" - ], - "query": [ - { - "key": "url", - "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" - }, - { - "key": "model_name", - "value": "squeezenet1_1" - } - ] - } - }, - "response": [] - }, - { - "name": "Get Valid Model", - "event": [ - { - "listen": "test", - "script": { - "id": "3d6c7158-f729-42cf-a2ec-4e62b7d764d0", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/squeezenet1_1", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "squeezenet1_1" - ] - } - }, - "response": [] - }, - { - "name": "List Models", - "event": [ - { - "listen": "test", - "script": { - "id": "54f1209e-4819-4f06-9d26-f39593a889d9", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models" - ] - } - }, - "response": [] - }, - { - "name": "Scale Min Workers - Asynchronous", - "event": [ - { - "listen": "test", - "script": { - "id": "47ac7b07-04c2-4128-9662-387e26446f7f", - "exec": [ - "pm.test(\"Successful PUT request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/squeezenet1_1?min_worker=3", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "squeezenet1_1" - ], - "query": [ - { - "key": "min_worker", - "value": "3" - } - ] - } - }, - "response": [] - }, - { - "name": "Scale Min Workers - Synchronous", - "event": [ - { - "listen": "test", - "script": { - "id": "3a8cf27e-b0d9-4df9-bdcf-9eadb239807d", - "exec": [ - "pm.test(\"Successful PUT request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/squeezenet1_1?min_worker=4&synchronous=true", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "squeezenet1_1" - ], - "query": [ - { - "key": "min_worker", - "value": "4" - }, - { - "key": "synchronous", - "value": "true" - } - ] - } - }, - "response": [] - }, - { - "name": "Scale Min Workers with GPU", - "event": [ - { - "listen": "test", - "script": { - "id": "b73619d3-3abc-4628-8e52-ecbcda561191", - "exec": [ - "pm.test(\"Successful PUT request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/squeezenet1_1?min_worker=6&number_gpu=1", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "squeezenet1_1" - ], - "query": [ - { - "key": "min_worker", - "value": "6" - }, - { - "key": "number_gpu", - "value": "1" - } - ] - } - }, - "response": [] - }, - { - "name": "UnRegister Model", - "event": [ - { - "listen": "test", - "script": { - "id": "0e10d089-7183-4adf-ba81-d14678f5ecc7", - "exec": [ - "pm.test(\"Successful DELETE request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/squeezenet1_1", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "squeezenet1_1" - ] - } - }, - "response": [] - }, - { - "name": "Register Model with Additional Params", - "event": [ - { - "listen": "test", - "script": { - "id": "5f79bffa-f260-440d-9d49-a3553972b8eb", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar&model_name=squeezenet1_1&initial_workers=1&synchronous=true", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models" - ], - "query": [ - { - "key": "url", - "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/squeezenet_v1.1.mar" - }, - { - "key": "model_name", - "value": "squeezenet1_1" - }, - { - "key": "initial_workers", - "value": "1" - }, - { - "key": "synchronous", - "value": "true" - } - ] - } - }, - "response": [] - }, - { - "name": "Register Model Synchronous", - "event": [ - { - "listen": "test", - "script": { - "id": "74b42a61-dda4-4d70-9543-e61a5be2dd9b", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar&model_name=resnet18&synchronous=true", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models" - ], - "query": [ - { - "key": "url", - "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar" - }, - { - "key": "model_name", - "value": "resnet18" - }, - { - "key": "synchronous", - "value": "true" - } - ] - } - }, - "response": [] - }, - { - "name": "UnRegister Model", - "event": [ - { - "listen": "test", - "script": { - "id": "d74f53dc-5aac-4afc-8acf-849a309054e5", - "exec": [ - "pm.test(\"Successful DELETE request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "resnet18" - ] - } - }, - "response": [] - }, - { - "name": "Register Resnet Model", - "event": [ - { - "listen": "test", - "script": { - "id": "11206673-aec5-4d26-a06c-96fab1fc81f3", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar&model_name=resnet18&synchronous=false", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models" - ], - "query": [ - { - "key": "url", - "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/resnet-18.mar" - }, - { - "key": "model_name", - "value": "resnet18" - }, - { - "key": "synchronous", - "value": "false" - } - ] - } - }, - "response": [] - }, - { - "name": "List with Limit", - "event": [ - { - "listen": "test", - "script": { - "id": "55acdbb9-cae8-4ad1-b2a4-b50a3549e8be", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?limit=1", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models" - ], - "query": [ - { - "key": "limit", - "value": "1" - } - ] - } - }, - "response": [] - }, - { - "name": "List with Pagination", - "event": [ - { - "listen": "test", - "script": { - "id": "d1b17cfc-90af-4b61-af07-081224ba7f81", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?limit=1&next_page_token=1", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models" - ], - "query": [ - { - "key": "limit", - "value": "1" - }, - { - "key": "next_page_token", - "value": "1" - } - ] - } - }, - "response": [] - }, - { - "name": "Update GPU Count", - "event": [ - { - "listen": "test", - "script": { - "id": "e00d8108-3ce9-4a02-9fe1-3e38907d7f7b", - "exec": [ - "pm.test(\"Successful PUT request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?number_gpu=10", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "resnet18" - ], - "query": [ - { - "key": "number_gpu", - "value": "10" - } - ] - } - }, - "response": [] - }, - { - "name": "Scale up Workers - Synchronous", - "event": [ - { - "listen": "test", - "script": { - "id": "f575937f-1665-4cf4-91a7-31c351eb9424", - "exec": [ - "pm.test(\"Successful PUT request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?min_worker=5&max_worker=5&synchronous=true", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "resnet18" - ], - "query": [ - { - "key": "min_worker", - "value": "5" - }, - { - "key": "max_worker", - "value": "5" - }, - { - "key": "synchronous", - "value": "true" - } - ] - } - }, - "response": [] - }, - { - "name": "Scale up Workers - Asynchronous", - "event": [ - { - "listen": "test", - "script": { - "id": "a65c2a28-de18-4ac5-a893-9aa2cf3cef77", - "exec": [ - "pm.test(\"Successful PUT request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?min_worker=6&max_worker=6&synchronous=false", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "resnet18" - ], - "query": [ - { - "key": "min_worker", - "value": "6" - }, - { - "key": "max_worker", - "value": "6" - }, - { - "key": "synchronous", - "value": "false" - } - ] - } - }, - "response": [] - }, - { - "name": "Update Timeout to -1", - "event": [ - { - "listen": "test", - "script": { - "id": "96e4be74-20d4-4343-b950-2738d6034341", - "exec": [ - "pm.test(\"Successful PUT request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?timeout=-1", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "resnet18" - ], - "query": [ - { - "key": "timeout", - "value": "-1" - } - ] - } - }, - "response": [] - }, - { - "name": "Update Timeout to 0", - "event": [ - { - "listen": "test", - "script": { - "id": "d2c89755-a457-48ef-8dea-fdd61e88d1e5", - "exec": [ - "pm.test(\"Successful PUT request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,201,202]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?timeout=0", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "resnet18" - ], - "query": [ - { - "key": "timeout", - "value": "0" - } - ] - } - }, - "response": [] - }, - { - "name": "Register Model - Invalid URL", - "event": [ - { - "listen": "test", - "script": { - "id": "427b68f9-de96-49b8-9477-450fa580e257", - "exec": [ - "pm.test(\"Invalid URL POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([400]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?url=https://s3.amazonaws.com/model-server/model_archive_1.0/invalid-resnet-18.mar&model_name=invalid-resnet18", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models" - ], - "query": [ - { - "key": "url", - "value": "https://s3.amazonaws.com/model-server/model_archive_1.0/invalid-resnet-18.mar" - }, - { - "key": "model_name", - "value": "invalid-resnet18" - } - ] - } - }, - "response": [] - }, - { - "name": "Get Model - Invalid Model", - "event": [ - { - "listen": "test", - "script": { - "id": "36836026-4477-493b-898a-b642628a51d3", - "exec": [ - "pm.test(\"Valid ERROR message\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([404]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/invalid_squeezenet1_1", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "invalid_squeezenet1_1" - ] - } - }, - "response": [] - }, - { - "name": "List Models - Invalid Next Page Token", - "event": [ - { - "listen": "test", - "script": { - "id": "ccc07f12-6f45-4213-b4f5-e37f7d0ceadb", - "exec": [ - "pm.test(\"Successful POST request\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models?next_page_token=12", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models" - ], - "query": [ - { - "key": "next_page_token", - "value": "12" - } - ] - } - }, - "response": [] - }, - { - "name": "Update Worker with Invalid Worker Count", - "event": [ - { - "listen": "test", - "script": { - "id": "ec7d233e-54bf-456a-a148-811385a34cc4", - "exec": [ - "pm.test(\"Valid ERROR message\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([400]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/resnet18?min_worker=10&max_worker=9", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "resnet18" - ], - "query": [ - { - "key": "min_worker", - "value": "10" - }, - { - "key": "max_worker", - "value": "9" - } - ] - } - }, - "response": [] - }, - { - "name": "UnRegister Invalid Model Name", - "event": [ - { - "listen": "test", - "script": { - "id": "08052d51-fd23-4249-a17f-933e557bdacc", - "exec": [ - "pm.test(\"Valid ERROR message\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([404]);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{protocol}}://{{hostname}}:{{mgmt-port}}/models/invalid_squeezenet1_1", - "protocol": "{{protocol}}", - "host": [ - "{{hostname}}" - ], - "port": "{{mgmt-port}}", - "path": [ - "models", - "invalid_squeezenet1_1" - ] - } - }, - "response": [] - } - ] -} \ No newline at end of file diff --git a/test/regression_tests.sh b/test/regression_tests.sh deleted file mode 100644 index 8825ee6ad..000000000 --- a/test/regression_tests.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash - -set -x -set -e - -MMS_REPO="https://github.com/awslabs/multi-model-server.git" -BRANCH=${1:-master} -ROOT_DIR="/workspace/" -CODEBUILD_WD=$(pwd) -MODEL_STORE=$ROOT_DIR"/model_store" -MMS_LOG_FILE="/tmp/mms.log" -TEST_EXECUTION_LOG_FILE="/tmp/test_exec.log" - -install_mms_from_source() { - echo "Cloning & Building Multi Model Server Repo from " $1 - - sudo apt-get -y install nodejs-dev node-gyp libssl1.0-dev - sudo apt-get -y install npm - sudo npm install -g n - sudo n latest - export PATH="$PATH" - sudo npm install -g newman newman-reporter-html - pip install mxnet-mkl - # Clone & Build MMS - echo "Installing MMS from source" - git clone -b $2 $1 - cd multi-model-server - pip install . - cd - - echo "MMS Succesfully installed" - -} - - -start_mms() { - - # Start MMS with Model Store - multi-model-server --start --model-store $1 &>> $2 - sleep 10 - curl http://127.0.0.1:8081/models - -} - -stop_mms_serve() { - multi-model-server --stop -} - -start_secure_mms() { - - # Start MMS with Model Store - multi-model-server --start --mms-config test/resources/config.properties --model-store $1 &>> $2 - sleep 10 - curl --insecure -X GET https://127.0.0.1:8444/models -} - - -run_postman_test() { - # Run Postman Scripts - mkdir $ROOT_DIR/report/ - cd $CODEBUILD_WD/ - set +e - # Run Management API Tests - stop_mms_serve - start_mms $MODEL_STORE $MMS_LOG_FILE - newman run -e test/postman/environment.json --bail --verbose test/postman/management_api_test_collection.json \ - -r cli,html --reporter-html-export $ROOT_DIR/report/management_report.html >>$1 2>&1 - - # Run Inference API Tests after Restart - stop_mms_serve - start_mms $MODEL_STORE $MMS_LOG_FILE - newman run -e test/postman/environment.json --bail --verbose test/postman/inference_api_test_collection.json \ - -d test/postman/inference_data.json -r cli,html --reporter-html-export $ROOT_DIR/report/inference_report.html >>$1 2>&1 - - - # Run Https test cases - stop_mms_serve - start_secure_mms $MODEL_STORE $MMS_LOG_FILE - newman run --insecure -e test/postman/environment.json --bail --verbose test/postman/https_test_collection.json \ - -r cli,html --reporter-html-export $ROOT_DIR/report/MMS_https_test_report.html >>$1 2>&1 - - stop_mms_serve - set -e - cd - -} - - -sudo rm -rf $ROOT_DIR && sudo mkdir $ROOT_DIR -sudo chown -R $USER:$USER $ROOT_DIR -cd $ROOT_DIR -mkdir $MODEL_STORE - -sudo rm -f $TEST_EXECUTION_LOG_FILE $MMS_LOG_FILE - -echo "** Execuing MMS Regression Test Suite executon for " $MMS_REPO " **" - -install_mms_from_source $MMS_REPO $BRANCH -run_postman_test $TEST_EXECUTION_LOG_FILE - -echo "** Tests Complete ** " -exit 0 diff --git a/test/resources/certs.pem b/test/resources/certs.pem deleted file mode 100644 index 63de5116d..000000000 --- a/test/resources/certs.pem +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICiDCCAfGgAwIBAgIEeC8zQzANBgkqhkiG9w0BAQsFADB2MQswCQYDVQQGEwJV -UzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJUGFsbyBBbHRvMRIwEAYD -VQQKEwlBbWF6b24gQUkxDjAMBgNVBAsTBU14TmV0MRowGAYDVQQDExFtbXMuYW1h -em9uYXdzLmNvbTAgFw0xODA2MjAwMjExMjhaGA8yMTE3MDExMjAyMTEyOFowdjEL -MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVBhbG8g -QWx0bzESMBAGA1UEChMJQW1hem9uIEFJMQ4wDAYDVQQLEwVNeE5ldDEaMBgGA1UE -AxMRbW1zLmFtYXpvbmF3cy5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB -AMcbCEP6kn9pUcap5+kYO/5xEl7SL965gSQ2TbFrVv+sLVkLSK8yTtcILr7RUINz -FsD151Q7VyQCvpVzkOFew2s2mAFWWxPJYmxo1j/R3IkJakrrTrMy1R3jsqOQMrxY -TLGR5LIe2pjdAnb9xWe2NB125619WDG7RrdHWZDfvSPxAgMBAAGjITAfMB0GA1Ud -DgQWBBRWjdEyNchYAkdPoyudKJY9YP3JPzANBgkqhkiG9w0BAQsFAAOBgQBMAvqG -cqvD3ColO2Ihgb/LCfCdV14e1YhusVFeKyZkSKFYyQR+MoBOxqMQqJ24gVzgqTU/ -h+LkMqZcxxJAME08BzPgP5b06DBM4K0o0XUfYUViFpYXB0qCG5CA/0S7ONldBGaZ -fv6JrnQ/a1NYBi92AaqXA4VmuaowWLVEFuPV1A== ------END CERTIFICATE----- diff --git a/test/resources/config.properties b/test/resources/config.properties deleted file mode 100644 index ce6e2f3b9..000000000 --- a/test/resources/config.properties +++ /dev/null @@ -1,4 +0,0 @@ -inference_address=https://127.0.0.1:8443 -management_address=https://127.0.0.1:8444 -private_key_file=test/resources/key.pem -certificate_file=test/resources/certs.pem diff --git a/test/resources/key.pem b/test/resources/key.pem deleted file mode 100644 index 20c5f68db..000000000 --- a/test/resources/key.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQDHGwhD+pJ/aVHGqefpGDv+cRJe0i/euYEkNk2xa1b/rC1ZC0iv -Mk7XCC6+0VCDcxbA9edUO1ckAr6Vc5DhXsNrNpgBVlsTyWJsaNY/0dyJCWpK606z -MtUd47KjkDK8WEyxkeSyHtqY3QJ2/cVntjQdduetfVgxu0a3R1mQ370j8QIDAQAB -AoGANRoxlyfSQKcPR2PzVUjAX3k6xA1c9RMWrVjKWeJd/qymH5SR2yAYxOMKzJu4 -1IYycF5lRyLYd+M/f06mOmVyysH3D7hkrNz57Z07UrZ0dO/mmUKRL7zc44mo22ck -JtQRwWJMplgew7N8OyqEZbcLOpahjlkL4+KZIWOuO7X5m30CQQDob/rzNY8gfhEm -oEHHQ4dCqa/b5as2OqpFoGBZ+iX3dumBf+UKuSHlvEozt4ZMm29DYSjhiGXgLUFw -6NBhWxpXAkEA20oNdGiYAyyGJ6TKkD3FNZYoqB5+E/Cq6c0AACssB4OrJtiGiBFq -R1h5HTEwYMe+ciZ4CI5MvBukjAdlfn7W9wJAXOIqyTe060oVdncB8ivlCFmgweHk -ajZFRq+Q8UPKGjq1kx9VmtRiXFjC2inTjBds/eL8oCuOcmgDR6hxZQYv3wJAcMLv -kECIinlGsvQGRY297wQ7+9dSNaa3/Gmx6mRIy8RlKiCFbUqnP/C6tswoeFu+DqzB -ZITn6IK+ZlMXWaiXmQJBAK7V4rR+4VdpYUu1OqPRxChkcM+Y4Wa985A46/8yoo3i -0PEenvApVzhVzS9jF6WEqIKcffBAmOxaXOn3kmn8w2A= ------END RSA PRIVATE KEY----- diff --git a/test/resources/kitten.jpg b/test/resources/kitten.jpg deleted file mode 100644 index ffcd2be2c674fddc4886117c12f3b29e4367aba8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110969 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

    J66l>z7T20v&BrMUK^shcTlD$)2!& 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

    hPAkbbTr(^R-tMK2 zKN0>_fp0py#L1`FRr&*5;-DDr4LLD|sj;tZQ3zKN@*cwl?-923bSUpcacn1JL{nYv zZtft-cac}hZN}n*gHxCHDFv?0CxyQfV9AWc=f(?@7if+U5rSJOQNp>2UTVfCY}SbF zqaxj|>0{M`iOe$Rt9u3RW`~8f`k|HzD zF^%opSyX9+jYUn z&%KiTR$^Vi!830A>~toy)LJOz!?S86`uLBg2Ua4rSk58ug@-Kly(2<+7h z$4T(;B=Ewob?78uEe=6);xm2g<$@*#FTG96J_{Tve(iRE2dF!0pqX(Mqorx|Jm0x? zn;B>Y0=^EuFo+Rx8l(n?tgXnM=cN6S#$h3#tgPlvKPyb0qr#omugkC3hSZIPlx`Qm zvZDfran~yU0tQVc^qq>L(4W@B9>`>is0P||d53}ZJl!nJr>ead>u!vlnM1|`e*Xms zx4$4%27eqmmG(+M4H<{0|CsP_*Ls={{hFIIzEq{!#hDC7*rt6dNA6FgHCE_&kKXSgyWf0T6hkPnzXgD zq4eo@6Lk4bK|I1#K+_75c0QPlwJo>K*xk=|54;NMt!uuNsV{Bfxmf#738k{MdPf2n z`^po}=l7!Cwd+$7`6niNYFh^%l#vXpwrONCp6VgnR?Ol9Ha@A@J)5MHD>i}aKH!!% z8#3+sK|2b=6#N{XdVRy>j|Qf&G6?aq;T^Z7iC#t(IdC_9tV-0pTutTGQtR4vtZYDU zbIrRyLZ|hq*bmh6^7hgK0&R>QPR;_(NVA&k=7q^Y0SYmUS;0Q<;zexTndS5N4(}la z?oNwrZo7FJt+-SD9b^q4b~Arrav(?Sd4|`mhi?prte4`S9DS(ncm7Ez)ZbnkPRDnN z0ZZ*Hz~hg!|1fTZ(Cb@S&`1mgxM1n)e3!5{XI)|f`M`@)BCM(`Rb!v<;VKU$ z$y09BH;tcS-v<~`(RqLOVg;Ue;R4Tq$+Q0{8%JPe!goy?$K<(N@mC(*`}#p zCa7nmM7qGa9Ny3?c8=&X=-Bn#I2G@oWbv&XBjuz)PFBW!xPRCuGn6O?cdqhk+}9xLsyJI|)dcQ7%PI=(?BoP&&tt2<9Udg<0YlLFU&$)A<=b{O_@mAR-C8?(@HZo= z(<%gUV5$Ho8siaRU9!-@v!|ZZgpF6OhegI_&2r5?H0q;?3?zPIjLab8y=$MZJVo$*EQ>u`JXy`1j@@{YN;(mL1~|&H)xKF6gTz{uF;2> zlL#8K8u5TZ0Y4w1R;%MP2d{s-sr_9T9M0c8=4u2ZH8R6T&m+xfBR;|_pJ)&h*J^~O zA3t*oxnV-%*;pn5=I7j(4pyT8EBv%daJZN26^m<-IHEHPgRlr}1a6>nI=lTzgH>Xc zOdpn?p1n9%*+jR1DYQF-!RDYza>CX={rqF7ujf0siB^p)mLvf$KbC5e62j9Ry>T1; z08;FyJIURCr|gsXCQG-TRi;$ESu*DyDUV6fUXMNCGxnuI~dAUJzO8#6NUx;9&QfT9Mg;f}KN> znh8dNpshkl)u9(Ip%;NKf)ibmGjBCzt7M008mx$5cwqO_a4>(~d%E{CXU4r{SJDYB zyeL}rZmBX_5Md{4x5A6TX2(EleG_d{KzzFdYm@zcX9t%pt~iB6`_&7G0X4U-Bv*V^6#fu1z zWdqkq}J(~TaA&G>H&zxkVtNSv~L%nAXbjFuN~x6wrrARi#y%DRM#cF&C30@RoX-OezPAI8PYw!62fhHh{vM*0a-Kb=qX zru|7^P<>Cusc1?9(gQ3jLoUl~7(+VlXa_QMZEj86vN^jo;`*-J{PSPH2ki$=5Nw1a zxE`6k2HIl{8h{;DTdr`ywHuEG-ZJ!*${#kbS(nO&=2@%7i8@Bp?AW?WM5r$g9`gnW;+R)l-b*$*?EEt+alshA9LgB)?M*w zf9@(tQX6ps&?#fF19D!_^*p!4%}b#H#$9h;Mb!u2&V;1zypoCKfEJ% zbO@y~wwB;gtTT%a*8C!&i{$GVDoP8d_7>nFTB&ozHZwfTzEOOZ_zVk;%)UjjsvD}@ zU{1Z*C8Ij0eMcHyrmyJBykxXmnjFunS85YV>3CPW`wQ&ppe3s(g7m@?rp) zS)Xb0-APx0`>mn4LACDGYF7Du?;nN)X?{)DQ~EDXrByui*{ z2sB27i8Tm!H%;3&KAn269Dy&8Y;MXN<=io=eRRnX@*BPLYTbS*yZC7Dt7}SVi-J!! zN}z)fG-JmBMF7`0w^N%u7UNsM8&kDkO=Zo4LA(tk&5q zy;B30AVhVuPE+@ur8Kji8QTsPMLWa+-2QmOXlZf69?_@PS#jb8~fa*B$!H1=7l9ZCcOK6$(h$eWkcsgxh z>1*)FVwr#6)D-kDU^;ur)hW^$JX%hAP5vK$`%)zg`bvwjA_aefac zvCVT?nnH>hhRyjjTdq&XTer1AP=FXePj_*vm4%#BO9!`FpUnN7EFzM5MIxRw>mHW+ zvj&&&6O*DsSdX_A4|#Ke?$v>(n>xQr)5gnOauaC2wsd*Jj5iTU$FY3j5`?6}N!E~j z%D_Z44H*9)0HHu$ze|FNdv0)OCl!N`MkyMhdM2Q}Mlw$IYPCvGBL=9DW zloCM%Qku;!(K}8ut;c$~+O9fqb$ixXY@P-@!_ZZ#-Et!XIQ!8-HH%%$5Io-+2c}*TocSG^Q>?ABv4?;0`?1MU8g{0INTGkZS4({6ik1m{yCD zXa4|;^{10K9rX{+stCGw3a8O?2YlABQEx=;@D)y?aQGa%(byl!ZY7>X8 zDO3Ld#Z`~mDIBVXe)5LFA<7mSEEfx)Z3eTRtG^u!4V)`}4Lfw|6(|&?!}c{xyGn9M zQ6%7ZpxDV2`?S}j&CsPSNm93MESEL?vN83|YaiLxS4mrMS4)dUah{KEac-m>rvs3s zg(wk?@!GN(jK+Dwi7|qi2ltEluM2GZ_63r!VG|aWhbR{V{ z3IPGWGmiP7*U~yv#{Mt$E6aFJ>6`p(a8OIBQd%oZs5=pm2`AVL8lkZw`EhMcLFya4 zr{re}jKi*!H~e}^lcW>4{J`R^eHnS_nYNPTE|tVMsv)I4DW^3k^0ctWh_=tVmXqz; ztq=Tn=a`jQWC9?09WDr-pxp$6sDQ>HrT)gLmTKaY=UZ)t9*o zMzv^za%)n6;W`*gWAj<9DZwwKon&w7C3=cSS)wP($La}fg%A<*YA8YyJ7KpFQ}G0E^UDb;imX{gB&>N>Vq|hcu*toazZ2@ljl* zx+z?ATkW$S>n0ty+~l&kPpwWxkbs{ROZ-c3$_h6bQa{96HljQsVd0okMgh+wvq)5U*02a9kg>+#xoX0b$qefu*59d5o2Dl)!R z=9aG$!cjW~9DWiBKwog64i3kB_;nuBZtCl0_SFo=L$m%yU@2M{OJ+H3dONb>Tqq3aXaOvB#sJFXxgqLH=@eT$Qq@=A!a)hjG;M^qPK!I0FO{L~9Qf4I_{#AaQh+H+ zu$&Rx6(t~UHaXnySZBjjT`tmBYLwaehHwV5b+}Rq(~+E=haJLwYw2%_HydA5baI>$ zVvB@CONjh-jx#siuwNlr!6;&4_3u+H0HNdRNBBg0+OKSMov!S zoPVts`dtSfdo@m-wxzoqj*Gd(L^a%GG^R^R%Sw!t7YYi21T4Ad83S~Y-l`o+r+R?e z;kLscwB%e~>3Odqpnz0`1x8xH<1DF1ZgihYyxAQV+x+{4>1E*!r^FN}1f|DR;$KT> zC!gLWzz3ltgIVWNNtGY3t<&v64@S5_i+qk2bdx?i%2-Q@7(2DCvY*5Pxvt+!CsuRj zNwZ=2UkW|<;cdp@c9@w@+?$^I8Cgj1U5zCLwPYNEqUNj|WOg-_b#3Y+vfMCU`PE0) z4Kf@9QcsShX0qI{V zLYr}!bdO?f(1|l8w$c!_X$ez{01|$R&2=e0$wgAOT9I=y%c3}OPfG#9p^wA~)wiJp z{RiHqrgc@h_dZ?R2HJ=V2xVWpTGF(Flb_+xSApE(p!!bnYJ=34%VegO!qhL>PQv7Pw9Ay=o$8w8f`sA2mRVBI!}hnk>Uit0#7Mfkfno+D1dY7 z7fpOTvUE>VUEj1xTUPsWx@{4h8dSGlh?%cGgFehW$;z(w_c!| zVYKuevGlZ=n%3DZig6$#OI%ta#gkjLlrA^ z3v@Dbp*oaGQJzLqfPIa5`La1WM`kD`vZ}tV7p|ph!PCllw&oUKsh|p!Ji1O*?USEx zTKWdlDbAbtTuq`u`N{9bw0`#**6K<;RRj2Pp^#OObfEV&iF`HbbMz(SiD>&1oDM3mzwxuZJ%UN6toO{Ht z+A?Ake$RCdmKP~3$B31jl>@@xJ83EBggcKl!UAqEUWhiHsj81;(30 zUSirXe|mgkfPaLkWR&PNgohcQaFWavKy8SByDC^KZ7W*Bm6QBSP)W+WBm}3qt7C*# zJ6(~sM^x7oUyF}X10+g;6tsSY(R)>)Sw zc^Pf?T2>YnjX}m1q@Bt^&XEbxy;R%@ETY>c-7`XplO4GaLbwLAf)X7{!=oI8sc75y zM;f!e8PneoVy6E9Ze^)UbssG1SzByO0RALvLW@gQMpKmX2mIC9iN;zvWpH--GI@>l zEQpdmc`_wPN{+da-;T6gLNHU|$w>)JgbZN5&?g(@f@(hN5*JvgmZrSVQ6WKUYCuE< z&RWsu3oYtw7g9XMAOf&R!Agp)mmYxlzta;-{SVc#CdG6Tt9AO?xKjd~-xwez)cXYm zf=-nbkSUig&visODZBLi2p701E=aYh_L56^8Fn+SfTZC;Ckq;P2O#Wd<61+)OSE8< zEH7GRWxI{lO)DX@G1SUV`2__@C0Pe4K3>$jb-vTd zHt!ADw%90s_YjjRJ4^`ET^P!P@rfvLc)`@1>c)H3KFqe_hRcj4hnD74*wv>TZj-tX z({F0>Jx)Jrc`8mG^JN1EIRxYU>*s&%P}f?^_^)@B z)Sx*TE}xTIZwFEt&J&Z8QhkZz7_X*&70A8V`fhJfD%T~}BSUfa(-jGj>eY?6Dgzkx zP_K;orYrYL^!wY*mV~#~@)IG%m$Z|R2+2EDeKTv*?)G>;>dkxHSAw13t2+Z;4G>15 zQ-2}b6oUi4nihEt5ka0dz@p>CQq%^V`cd)iOK^-Nnw+=T)N$neg(RBrG-G5`dt%lx zq|xnJ)u(jTrX7gj5|7TMnXzDF!^rwl6;LQf#(B|Axn*8IyZhFZn+KQheqxaAgkY#Q z`cX)37txkIXIeoTXeVJTa+=d-SjY#+Dgt$!VE*qPYBVc>NO6KTl?=j;=6h_E*8lsl9-dVC3(;NU}B=%|tNgn0{3#$)C;~!d=alCe*-ETQ3*1n+jlOmjKjj4qo=QSeg1q$PShZL4F0x31% zlby3f7QyZBUhs(oID zLU1xFh+77cs1kouK{`pvsTWbUJMw4;k%CThSXRcGbxo^0VARAp;UEm)gHTRwk_Bk({mXCb|4>sRg$0PL_+xN987pXWNCPgv< zuD5$n^PkR%YW19DRhBW-VB-#cw0k{6fIRryn#R$mvw0X8P{-DbMbr_EXZ-6jV*Uxo zh)2?-qu+K1#LWQqUsrWd8y-FC8s~Ue1Ov@Oi_!{nuOfw!a%s`A$@ihrg)Zu%?gF_K z6#2lQk2dt$NK%UNkbd-uEEgLpIZ35#bgZad*0ngIr00FJS_k%ZhULeuoVP3~EXso+ zl>r$}f>eT>5&;Msoyi~NR9mV;e~TI4nwL)|%RB197BsZXiM~!%-s}ZJPUAuDTvYjk}ATJ0S>Ehh2Tv+*TWN#86Qw#zK7AI5|#9^{w^WEH>SGz&1)&E8AF z8hk`UVND@m<0(*az!W=qnQ*T}~p@rPsp zwf$1q9`$I)f~GvAvO8tH{-eJK-=EQN>@Gn_D{$J(ha|EO1=inMo$SznNK;GZGV zq-zN|@iwl77&U1e^P5^IRw(^8!pq~R?TtuuDJoMkS1G}Plj0>hqpSY_mg2wat=IcJ zU;1Lg^~%9}hQk(X>e-0>&MbzP9yG8?W#z<5))10+Iv3dCKAGQ9bQGIo&0OtmH+YQG z{tA9ou^ky!OKrTDKvPOr;Q?Mns{smGHDvfoi)>4q-A~g!0V+Mt4rBElqGH{7^}tXI zIuPQD7C;5W;1I1F5TK@&MUG4MIh^9w%}e6P!-zd04Od>Ev8E(Yr_`U9I^#0pP9JZ^ z8>J4mpyO&7NlKNe0Zr9*Erq*7@NJXtGFn&4i-bFXT3l{qc#&Kzc%RIqvb2IfiLkH< z(Tdw$l-=W@jkW1Au2La3WlX2DoOLC)+2K1bXa3vtQ{=k z1ARRvRQn&Cywi}@*06>#f;4!_C9~zilt!GK%4sj98ZEuD)zgpIZ#J0n9JL^!W9R%` zO8xsK#D|mS+$lOi0bGYwQj(y$ZmwI@l!?;})VS+yfTWm_)Jk-y?p2{fZ$M2^EsK$L zd2m@vFrivTQietpRii$N-_oyt6K~ebC8}ih zBRa#*-eU*6OzPB?6@{xA&IE-3PrtQ%+QstWH{wA@%)1iohmf`(Nb^QpCsK|F7;Ply zw})Mr<58qdzE0V_{{Uz<=g8^%W$VXGseM5aH#trr1NW}7*NZ9TD}oAS0kFrD9O9_I z55HwxI$x-IfY_4t#SxoC2RTqu+(RxnpK+9s2L*ohru;y*C#bqAP0nzNi<8!;8MLys z97Hcd4=F0ZAvr=pDFCO)S04}_3 zx|TgFDLF^(Z6-#>Tqk<^H-jU=Oje7YAzrMy%VA+4o&3v2Gs(`>&&3uW>D%mUqM;@@ zx-Cz%>OsSaZKj(fpI;RZZGd?vIjHwwy=&=bZgODn1=ue$Icw76?z%sEfI9#ZoO`Q1 z>f9r<@i(RHod?@cPVO0Kxx>nI8wmS}QP%bO7-Uz~3ffm{yZD9%E7as^G9HeJ(1$^=F zR^blk))v+;&M7wOueTCv3xPR~>d+U39{Att1xt;_*`n2-T-z^DM^m$4xJhxxWkYf4 zO(VrPk}?TD;oNGf?^X02 z(Q;d#5@4BeOqW6nSD&Dl2aK;We&EaE5ld!E;fZFu%b8fHUh9enC;%9 zde5nthiQe?-9Tc;xX8JvIVELIGadsB68h9vooXl!0Ca(^cmSMIX7#PHH<_2o*C&j7 zW39|^vW-boib53Qji)#&Q#oxTI|c2Hs*`qRFIZh<^%d&XA|FU}i|$<~q4x-CT*W$xdixZQnbS&~Ev|6Y2eB1v3S4#MM5!rFrw%U-?+k&HnDldwV zw+O+&KfEDW8+d3)8nNMnTX^zsAt(5A<2^{D7Ql0k} z(`@*X{pRN?+~g~5&Bmd|F`FTGSM()r6el{LC<-G=e1T4HsEwh%DRmRQJq_Q6{{Rha z;@;aTDZAVnPBgl-oW}}l%;TgFF~|e}k%Z+&-q!R-+;v7|!zZK`s3>{qGJyX8ew51! zR-ZTb0ci?I1BGeIlz){w84G_~^fOm5i;|-?af$#ANQzSIbd$mqk_g}UicU5gRSqG4 z!<5HNPFK#P_)_Ow<0?mhjD8fYg`l!de-gRutZrdniPaFnRqk!oFj!7om1~w?2qa)> zFCkSrwc){Yd@hNqoqc*!fr|I8`-8FTFyPORz9l$4~n0UWofLp&sGnwpPs>#mud@)sn`a%>jD)o+m6jjiMydE&`R zlR}YPn2@ga_{mrAV7!Sj7poyjTw|}n$mc@9--`Iprhm1^#7lIvS~}~cZC7}0BgoA5 zi^;}tmA2_6dyHg-xGO}n{Ac)0)IqDFpmkSB-qeL4Na>62QH{s`?)J)1&dDTa81rpi zD3jlAcggRL-R6Ter=k zcZUk#!a!M;!CGHd1^`kV30~*;Navqgwb{DYr25X-!)y45XzIsetL2d@d_~!>ER1C# zdJY^p1QHTA^{#clP2FC!1;K7r`4PW-BCABlkPi=g&zIRR!kGXT+luQ;XTnm#zk~u#kaJ%h77`Rf5E46A(|v1nzjUXgQcF3_D5sz<5d6+&Sz#m5dZYay< z*vInr^s3F$&7)V6wfWYxfN+n`imF}hX~8-`$m12bf>82ex4T&xP)Qi)ZR(#oI-6cF zd98Z=Z2>Dfd-_&+dV&EY3Ur7{1yyP6GAX4ed}ALii+)ij`5x&4u zGKqo|0qASG2cY7fDDU}VxKKtxAFUv4V*xwq#X8=@Z>=R`JbKrf=bB(K^m-bRia6VP zk8cG@+r3IfP6B-?tU%WG98-x%$l{p?1A0(&0x6IvK0XF0v5a%IMkVl`4HVP*)11{% zjach|VBk;^5;KegMk_eT=7xA@YHAUYj*d?M07?zscI`*V8b|f0_3m&^$F&M1*~Ca& zV>vWqS3-fwpb7`lnRg*42OmmFAX`kD{j-6)nETU=-}Ec-ezj40LXnY@2fYC0$!)9g zdsMEOOEn1nMZfo)d(rXwlS$cilQVoHZAbH?n7$r-Pu8fNg&O>RrpO+By>rb;vDAZM z;(lharZ41U_mw9XsJO$2erdyI#>Y^ed`+rJi`v}b%zf)3jnqjv^X_V5CsDW*07+Q( zJWys~yHC-{cjzQVI5_x*bfjeYd?a zJ|hgsN|urU^5=d*{*)EP=F9m5JMU69xoSg*TS)UHg#*|hZ%VI$6Lx)d)KeLrmSkJo z@mTOig=tX)5rQ_x-)vXT9}v+ccRAlGsID$SIk((e3qb&RwC7?;0D!EIL&a$w9n}{J z$qSs*h*57spDu?QQ6tdqKEzcot!>dIDnyICHm0nVArcvP3qo*kr6e4jdvEKJUp?xR zSr1PIBz%+A##hdn%aeFZZjs`{@my&PHh=KI!Af!2I}CA0o|A4(lr8q;LW43}P?IT5 z6$Twn=~IVep=2X-woY@tOZ{~Lgvoihw|@Y5o@Ki|LO4NANYSL69&iV9wS5)+p6)ie zeJs_VtzC?N){=#UoL~?Xl_y|DYRr5;=y2;7--vxRGAzjDUq0evpq5=) zbQjg5`^HoO1!&czoTvC<;lBs7MKUTCS%l$gQcO(quSGdnhRhc}D{n)Cvh8WO1oE+Nzqz(Fsaf9k45>z`Jvn6#l}L%g7C< zrWD?$QnfS`vH?nx(aJ(qry)o>c2FP@Sx;T{ z!?zpKE>;QGG&gaG&yNpKC|8bD60IkP8&YwoY_I}Vns0e@Bymdi4}bW`__xB3Wwyq) z^*mV3++j4*RAa3H#Vz)8&Im5Jlq>?31%bY#(jwlVS*`+mEJLGFqP9qG(Q5qg( z9Oo%?v~kad;z-I>q)khHwbLFX8mX}}qD_lr|)T=V4 z+TzEOZP6h)9uwuvw&aJ~@mT->Hp)VO=5a%{Uu+3w-jqj8rtamOSbCD!_1X6x327{} z#DADbKf;B!9S4vy6Tztdn3&z?qHQ+|laB`Nw@2SWQ#eXAy8NJAN#6}R(ndkpI0s>q z`o7g>wI&^w$8C9N>lR|fej!ha7EsvwZfYc;PhLA%%CZ5A+@Q^hBBlnDv!Q7Q1v3J+J-;e3+jE74L0b`e9B7XCm278 zC$&!WUrW8$cl(vn@UPjkEmk=%E^d(L%e2xMOK8$g>se@nz5?7=(zR}ZgKv{}wavB| ztXuAOFZOvZqspk!(IVnnZ6IX$g;CL|Kx6lS5;>>IJ}IOuV%xHj@#n5OcdI&r>(P;| zZNZNMBJHu-OPFq3Sq``pqi-^aM#Ex|dsoi=A^RpLTiUKJvK)gh0a0x$C=JSCK?+82 z2+EL?#@;CU)#Lj_^p)^l6VijHM&8n@V+igr3z^AEQEDI%nP&E~&Iy-nZW!nwF%^jTzYu z6`^jn>KR+wRtdsgB09kYlAU!_z2DxE8o|`PM{kxIgK=rqLx_@3%wVkzA$Z*&A)=GQ zd^3P|6&=&<*}CFOjzV=Xo4#L@F{tBCrkx8)T2gRw3fxK)jtU6pQKx#I@p@IED}g1Z z=Ank1@S?V%8T6_|h-?rOpW-1)^yiA_;F$K(q7SGnuE(@Yi6OKpl4|0I`cBV3tU7J!@-J5vTq9aguo0=ZLroV#kC7>Gy!y(r1`=15 z6QsL*Yn+>`X9Q(8G)`r!dns^)FykSl$du}pokU>1oRTmUui+V;>DZkW8L3@QbcEE1 zw&gi)>HA!l6j0<=;s+ZJl@JzEg!qe3a^qxzNfhwV`yLFW7 zYfgjbOj|XfASmfS@S9LcIsWm|Qb`HiR-tq=-w~dXV}o>JJ(?s%E%l*~4Wx&OQr&m( zB_+h<=qgi%v>+`&Y6c$H)l=pOPxT~8jH>LnZa3c%M^w2AD8R_UEVkE*tvqRM9o zpBe2oOEi-z(jT?NWyuVil9!pU6{L9>NZlZ52g(AKfH56HDF_8=0R#^=3u&|bMCo}-g51T2<;ZCU+=dvqC&D>W zl=hLM%ruQWl6TF0hMukKkBe}jbj&M=wZf5YYDC$qj*?t))2l;qf|M50H2_PeO4c&3 z6954vbQev+YJHcY`lI$+ExZ*zU&x{P4?e7FenYyDTy;uNT3aMG+DJYb+bzm6Ku%FU zS-R%cX-;qm&IM^d8onK?sqPl8fwB}r>FY#9Ou9}eNpX}P z?((#R;lkQhu#lXRuymZ7%x~Qta7BP$8b!w2V}P{Bv_ewWkmAltQGvhV%9N$#lB}F$ zR*Fdv7Tcn}j;FgL#CqS=x0!b39e=YfvKHw|iOKNZ*4VB zP*Eg>IKqh8D+6T?Frl)&0m!9Iha%wPCRyc#k>4Z{w|d5Y7JN917I1N^QSf3k9Z*E1 zdCouLx!8Y&Cw?pH9*Ik@!C~n#nr)_(l%ce2pTawBx1qoT70(NEq)XIIaVQ_!XQ>%( z&td7O7R0Fxu%8g2B@ZJB0H~9cC>(K&``67YQ=TC>UjG2i>00OZfW4-7TR$1cK4DRl zp(-c|DMukW0B7s(St0QpR&YQV{cBnLHMvG0N5QH4!BvYPyG$QCeNJ0r+XC;?JO@=V719xTOk0 zm&QFi)Y;D@Iz4?U=8taP;+R3tU@7*8eg3qplM6`$1M;Gpa~q5us5A}vr`iNyjke83 zNESu90sjCh4b=C?){k{K&P53FK^)i7Gu4YD+)3Xw!jLnZedxzi*kYL}89Q@G(MYgW zCw<0g3P#*fD@Q+{(vVTMPTbNA*dmdiT1s0W=ZY^t+PG1;q6Tq>RHbJdpK6wj%7FV& zkkSe7QtaTQ4slG7sA#sV9gQ;LPI=mnKcS{pfJxe&L?bO6jFK~)QSnqb%>n>P-`9ap zvf@I48~tjL><%P}$QzyLXR;1+k9v4{hH00G!5QP$s*sL91bm#})N`csQV)`H1q8O# zPShFCIE@7XkbkW-?mJV9LWbj-X+c=r;EG1YS|a8k?gteIEwTy5)n$;zpiZ7DB3xoK zw-p&tM9~IRq3SEil;TGx=}x-N2_RDoY=O8m6oX^QxhXu8MX`7w{vtm04zvTcAR%cv z6-zeDqzkZF8CM?lY_iVQ;2xEg#apO22N|m!nfab70uD~xR@hO$bj#75)33;VNUOv| zwyoP(AbM73(a|LqQny|uKg-^)uvu}Hr+^P}No7*eYHTOL$ymUsJFK$abSUrdQZ6Yi zM$}dLnyOsqqs9YYC-bI`#+45@48500B>Eb;biJ7ql9V9;IgEPMe!>wTgt#{6aaw0g zL20$6*1}VcO*?cV?381pt~R(0-yL6skQ5T6x$le=t8ROWqV?;-U8Pc^v`f$zt8aIwjD zIU(g3uOVTTXUtQPuYW=^2-wxfr}_usUs7A2LM6^>#e$?^p2OD{{TW?BPLC@7*n?EqjE5aq+!Og5L|4d@iwqYNE_|7 zVUaqA;v$gZBf9K}6ZA}N#@udW4wNWiTAL+$c?C(_9D)XLXF7)E)K@h^YpCx9*|w)r zyI)c=+LJL2qjvmk>RM7pbqu(Jm1D|jR_l{q1D!v9k#39?bJZ0{9u#KWaVa{Gw#iFM z)T9p)wFD?Qm47m5@<{@NXWTlS`;-9KVf!@ThupPJXvjlsD32Jn=5msS6=VW3k`J+R zOIGA{1-@%9NsiQ~T(`ds)hTId+@nu|<}#3=Tv9z zbIhsv9{vC)i~8=4Op5A$LFvwyLJ1xkl(N`q#o&2P_#EytxT{t3rsj1sFEMVBF!efC z%QM?b^s4t6rj+l_wd(RPmR`Zin@DtIrF9u^izBEYe1-ikMsXyv)9QapWU}c?-UGQF zVt|vS!RAh!`GML_Me5yQHduGq_c)iHl4TRwkje_)Ub5?He<|#etb>v=Qa2lhjJUe( zeo(D8j`ch#SBVxm4&~bqQpJO6>Iiz!!VOwECeM-)xcsh@YkTx2WG?F&SNynPE zPMNv2)v}}8rJ+iO4&jN!ytlvYvRv@*T&Dpp#td@*->uI zy>FJwYe+B0aXXJO8B$76v|!{Z0~yVIukg`}H^b{ao>PnYrpo?(_X<4tURE4H-1$pO zLbI?gBaP`ZW8%kz?G?_rVZM;~WoYX-j4k?@?eN0A%Wyea@mdaF?HLE)j zkoO!NWaAj22Nr_cx+`A|?bjAMC*p5Xbk|cdtK#FO$d46%?ZiIv-+8n*z!_6$!5bY| zHQt~2gK@N8Q4-rZw=GaFjzwkFq$Mj2$Y?m?fZM|HR4oG{{ZH9-lw`7;o2>|OwUr@5~r<=!I>Hju}B5K0u*JH zzO@06ubFX50Y@iEC$%%8lc+QyvLVi78$Z zSPwsq&ZpCoPD%2o03ZS*CbbZ>p4n3PASQLBewmAHakvf(t>oMqVUG>hm}Qn0G&pu1 z8Au5~@YX@^R~BKwhQ)^S9z4e4;7oB3Ct6vz{L7A{g%gFLBq_nGB>3nX5m8?WM@@A2 zf`4r|wW;TA<=E>_gjFC&aqzK_NC-}~{{R-CgpI}t*1nsa)*U46rgYntzDHEtU!3#u zn^P{^`hF?UrrT=+!3%Y!nQgJUu-L%P*=|+i{{W)POJeQwFZOiLyeYul-h9U)*i3>% zcA^9&Z%kTF{u6QAN;0fuH2(l5t5;k7rtc=-)3zBh-eOcXQ)FT1JWRHyb(dG14Os=( z3Xa6AZ~zvMh!*)bXm-w>vATvEx})Xix|hnt3lt@xpxJlvWzxfnQ=278ZPfrno7evU zXa3H%*`>D5FBb@3B!p!6%8ulR6qKZ3k>VsQ>c(`Se>G9HLM7zo=c*f+^nRPRnJ99= z(Q)EN9G1(gTFx*J_lnY`e+X=)CYfD^={7qkk#hNKbI458!)c%r>rE*DscQUKQrF~4 zyt&+rBnmI1m__zw=ch|;N4i`SHQ92Jr&3yEYdXlrkV(K#a@o$wz=%bbW6)>XS95k$ zsS}w>173Ga5!9dEB?Ek^Wwhk^Q;;+GjZHn$9<*3`FROkbvuSkOuaXwK%0$PI%H1oJ z2_c4*r2t!Pv@0Yhk^G>OT9-|~WVb+y%>Jih;`?l-NJ~U07j&q&@k%mMl0m|p#z4s6 zDznF-TP+qrV7JXBnT*PiyOXip2$ZA{(NOav{oTBTpdarvq~HQG2R`Dv1V-#_B6Foq zh^T=*%?>!0LP=0z8j5#L$s;4WhXknd6G-|`r&zYhj_tbW-L1(=Sep7KyMkU&3PxKQ z*e4&4QqBQ57y`ZVzR_Tn)9aHcyK-2I;l@L4t%%W*4p!nD1R!sp-nvK_*yjm3=fiJ{ zms=-=G{&;DWtB4?)XOPfjUb#Y=@0pwBO`>AgQZ^7N5=mEjonVgzr?qA_jjf&G?gX8 z9s>^ny1y1zft(}~Kjq$xewRk$Zqf6nP(IbF?DSYDGi6P{FG>ngKs;c%q@A|}gzh*S zRlniIg5>L;v?jUZ&bK^SM071Jq1`;IK4mC@u>m_1&1P3y@?_q`xw4&pV+Cn&tbwV) zp8N#)j=*-}weN%yXI?H5Zf-_mL1ivA9mbThl9EyswQiJxZ~)YOcFjZ7N!25-9VNcc zq`nzJyWAUH-u9^{=A7}Ff*B(K0urYZqp}A2QMVa2KWDkR$5cs6w?pBCFc|lKW~0xw)WQQLXqOQ>1-)rdIPdS*lpUg6#PVqV`6RIOHCQ~Ql$8K@c>XK zAm;}aEL~`v_V>{V@e$9P>np4O05n#WK1vsWay>TZ>)xuQYIG#H7me~pKRWaB-kS2B zWhp>$MByN5-?!43LxB$Nq?4cF?N_6cTXtEk2uLXy`qk#n+D?;}tH)4eLuRN`KiVv z4LsG5loaOTqUK{5qt^Jr-l6U?01DoTh)a^v0nZgny||JU?^YYMu$<)8BJ(>|q7M}| z_BB~h>LrXT2D1L4SOsA3S_S$}vafp1@0d_l{Blh+Xh|9M-9);g2Hw>SK_ru$b5=W? zZEI1+NT~A&o)Sf87e_SXWJ-qnn&y=dFe+7WGq@P0%oV4EoK$2&Q3GowTmFPUevrl>h)0BEws6`qY~yR~-Y}y$Iq+Ab>_F z*E{-9%NgdrjP#-g;&~X&Fi?KfV$qMTDZK|fQZlK6sk7;dMvPHq2apXUwtYFI88A&9 zx%*cN4mjqD@W~?pd)JW4iN-4e&cS;qIxEwBm~MVxLQzoZ&S-idh7v$TW}uASh98vOypwhEr-Csnmj){B$6kovO)a zb+(;KjtCpoYB*3k=B91r_^BSdaZH%$(YSO+Q^C@f9?}jrC;e+ljU}lmNolZhIUTDK z==hj<3P2o;1s$h1wE^l z80m1GK#+X}JQ<`*bmg)2AQp(uz>E-aSG!~jvod2fZkn$Z;t*T;ojV?RPx;b%TG*ct z0U;UOX1jHIMYc*7r&vR2BL@q~KWZ;#iK=x)<#tY;jT+?L%3O3ZH6*wkPDbZZ0YHFw z&0`mSpoMar<7i>Tpn?{qqab8{6@+CV@A$UItzNpS^k>*3&TihPVg!U2SaHOy2OE_E zl%IUn8Vsv_!SvdgmV`)cUPnsG0*5+D$DbpyA46VGsBO-Uyh$Zw0JO`t%WOls8HJ@J zswxl?^KE1CC&M6+KbRekGwD{zy+L+Gp~Xw-L3w5?XXTe7D@l~n$R2jzK+hfbJPksf zYR?!rBHQjV7<9N@Ojyka769<{_UuWCyvn^#QLgBjChWiO8PtM0)>HujW#HS?$ z9YaY5t+t+(xgzYjJu>uLf}uxTt6Ygpr6FiY0c?kW!jZVtf;8amgUyGcA-i|zSn#d) z*jJWe$Wsv(^hCD%ZRjd#w;n;%Qlpfi%{}xeCz_Ip(qXhFS4PH+W8BoQoawo83f9zu z4!;bb6samstQ28sP~#esLwC`ZyD0H`j>US8^c#v-CRL{G8RKhkCAmgA(&9@2Ux2!d zl%sT&DYWTP){#^8Xx9_g_GBdtbpv)7NL)LJK#r-f-!3b`aU>0GsYxK^AhhsC!z38_ zjP#UQk*gEOjdpCmYiJ;;ECnURyvh*NfD|}!9s{@s8B2f+ zqzbup2TVwlW1ZAChoVQ4-O4S-Gj2PTq0p^sV{l3wNiGyPPvHTzgsCLg&$Ip@l)o9f z-O4Mdi25vd0`4;PzEad#dBlu>rX&!R4gz!(k&3E=m(ov??G+w|_)XK-JFvFJY1GbC zhW=J-MJ_BlhJ&oEdJh!r!5b;}sDmq`skjK{X1Na-z z;?;xbJw;r;2lT`{z2Z!jxa4tZMxceDt#M^G;PF@_l1`F^Cnxw->5N(aIrJpnsp!6< zm2FO|q_>!Y(*eK03^c}ZEY@!_KIUVruGz+fV~ePjRhrmRu=b&G$Y;C?`46PI<4UQD(f{V0ARF z4l?3Hfc&d`xz85-1-#o~Lv<1Rv@K&%{{V^N;~P^+H1sBsj|+_Ze_vRxvykGXwCI&2 zvciI;uv|-MQC0~+R+p8Xu#!i5)_UIeaocmD2k02#UXcVbyDfH19V|)!xD+6;`_r))amrjF$ zeCeBGNQ~t9483V}nBE#trM%%wq~irCZ)6U|YF;vPLJ7}H!KBhB>>YnD4_QmFT;7ry zxCB69E;w8vO*XfWjilr{mzUxxqH-0dSUN}$-`ZQLA8V)jYTX7&ZdL9Sc?>dMEw>6H zJj<5&In#|^8+x}SKIY{}HE8$|@KLY!H>P^7?h$ffyPV0Aknm70NJ7b8H_6CJBpr$t zu5&{Cc=&w=&ueneHhFP{Om_J#*H_{=`whnb0CR}92nPw>eiC*bKmo=>rwB%68uRfpc#CT^7o#!WfqV z;^pJ1UV@^ez=a+HtQTm z5|uOar3KgBP{v&F)Z$cj0I5L($_A~1&C^mkL!~Sq4_TiP9YWywi>$dP#aga!l_<1Q zjo)zyTjxnu3c}o3N)nKx2)g9_nr&YcReE-O85b_5oqLGemYbv~p~w-`W47j}63j>q ztw1a#O~z7A;I|YwwH<2|CF8Et)Aufd(+mr-8}TWLs8Q0k%@9Nyr|8iQtpyf<4}UYMJ}mph}wY-hLV7WThzB4eO@3_i#k^3%1AR-_xplBU>Pk!==HVJ zy)d^CbDq>e!EL-{)ta^H47j%*%G?X}*O*yN> zLr8HskEvQqgso#E1xr?}fB^<$tDtz@MQ^${bdCE~ET0?{f{VyJSu)2Z;$-b!hN8b)l2zgLEM$)V`87d)eq%RttJE&{Y5(!X~*F!Dn zg}imN9-Ml_^oYrN@TDc;9Z||(ZZC*d*jONj&H&P#&Z4ydkc4737gKdVQoh$n+2=@YhFVjpXXMshxM?Q}0idANbp9d<^AVR8 za1trTx)J@%X4K8<$nxK}^u5ws(p(|9tbl^<`bw4%Jcvp_N>CN0ARWmh*0Fu*Tc!U1 zrQT($YtS3;v*E*vqT}vhX>A1g+!^5OLJ0(&fTeYV7JituL7UROO53;h)Bft@>#GTl z(hkaeAt@OrASDSICz5xgI&-OKHGONR(KgA?zT)qY0*@oVz#cN>DZUcgA<}9g*2}to;*_>EU!5WR>T91^zYBE)g3#cH8v*O z_(>*y0h== zwL^SP=%y|GJj9E5Y0m{j0);09B^;NG7BqsBO4T_ zkJ6ZpaRGi&fx$WF>t48!>I;r6B`19$q0anwsDB+ubR-o4wn3uN6GyU5*QW|UonLJ7 z5_FvVb6EYBZj}!;XckH2=^BTvO{P*T)h%*{5;OJ9TrG1jK*`#(D_rd9IsIzcZn)_K zCwiZf>WwB0%p@F&n__U%Jk>&jaoZ(TGR4RQ?y7Ym8Z9o*0Y8eNE;j%M=74_bfsE8F z`<|eok@?Yb0z(&h`hoL)wM@H7$vf1Y%6_bne_E^DWa`ci)QL70(O$c~t5*EyV^r&+ z3d(>RRjzp|Bx63Jpk+rcDEsYKNRwqr_=QV5AIi9kW=|O%jad_HQq+9Ad(my$PCpGZ zS|-(%GidVcpVFCj(yV?CYV?aiC-DB6t~+GF&WvWc=BP&mXAjzr{!>CC-mvJ{?Y9-6 z$+PLm2;5Xtmd!3?pbq~4YJ`@Kc|!UkBf=mLE`8|cVT6pS8T!=JTX-lQUFflFDg|0b zYnirkxa`T+GtD-%_9BT=H^wvml#z%^`8X#?*c?}}w?oNZFdjurXSYm5d? zF;wJ><76X@jm{`il9D#S^{KaGk4lezImjSj6In>Q=onk&HsY(4hv zUJRUUIjOlEQ;H+qV>}JLDmp}Bb|S1Lf_9-^iIKsm%48mjq=*A@Y93q;a-Y_&k3<8J z?NPEIg>QkqMaP6$EoN=cT0#dQ)KsB#f$T1Rhgl)h%tlOg?Je8wT zQd5rOiqo2H5o3)=*p5M}r)NwE%j=Dl;Ac6ax<*10vXyU8*L8TxA-w(1CZ~EL++L7? z{>jeuR2dpAmDeAAJ{ZDJJ*d8+5zI9t;4e-Bi6c+mr6IL!g4$G-q$>ieJzXR`(u*oi z2jV%~+irg$LwB=8_e(Cqh%#81>;5~5!)r^(TX`AfK!TsRrtl&>89{xu=PkO!=tv;? zo(Lwr(KD4H=NYwIVT7Rr#gXCos4Dj^0i5r)KJ>fgx)XN+Jy8_X>GNu5LfTh+08f-{ zzBlx*FV(NA+1-ZdJ;?XV#le)zA)Ap`io}K#rAkV1_?%ct@lZWRahw{kxeDt1CtJQY zsIysdd3WU;a7jXvHwsRjlr|wd?ZK#CoVvBucNpltNKzS!vRe6dG?ErElz^h)+#c!0 zXjjN$-x)>?ruO4HJR(`OB12CS0Z4Qw0X~6V+#SVB8gdNUj*Noo*7B`ZsWNQVYqUk3 zY{GR0L#*l^Qd5$U&`{th3C0f~;;MZ@<*%q6Y5t|XrVRUoi1FPFINOoc{5knxhnr9s z0ZMI2SSP({(`NOBvDVhb$EVLz4eGyU=(3aZ>n6!l-??;4` zxU{I`mfS|%g(XzaTl5W{)g7p;b*;JEjm4odGf%T8Q;SIet(P?nF8~tS(vN)-@RE94$($wC&xq!l)K1Wi`&NE5a zp|!BIB0- zHFv*s_ruh@;{IaPt58F(Eep4)))TEKTG=5=QgCyrwT0kr5(x&edo`ASL-kB4&>M$u zwaS*J;Jy;<%Ot{~%3W{wtQ8{!rv)cYbf*N4h)Kn4n@U|DMqaGV!H0Lb!ffTlEZ^Y1 z+FN}D>2bMiDGDvRk1n93Y38mREgJw zNV~D&lH$^+<&`_DxN}=hE3hPyoZx*ccelNDu*I}nTTy-Y&AAFwX(>onu;YsCbm78A zdyMl~hxSzIxv+X>(PNa_K4vQ^NA468;xhIY(q05*Nz^pz17H)_*8K9_QMgJWOvGt& zJRLYn9PwN!3dZL-B}cF-$<9S_MqH&Q_b-ujesBDGwc4RwtQT7w%6YqVnJH>0N|4Kp zG*aA&t}Q?fDp*4efChXb5)RdTjq$Zid^;9vg6A#CscGqGYB*VS)*S>ftZa~_wvQ28 zkbsnp*NSWVRoE?%I(x!)EVtWs;}s|^C0{Wq)V5q-h@}tRlH-dhNf}BKwQ0)9s((OO z+5Z69gytqT`%>hkF10D$+hE9ZOHGb5;ipp6zv5c4{1s&=@n5*DREX|?>PPMot`|Ae zd-`U&Y)y{v# z`SIk#dQ1|D_Q}D*2?^Y5QBLX!Ep8LObnY`w z{SfHhoA`!{G^ZjrZA`gsVJ^KXNn39^GN%^dL0Snvz=W0ga52d1p5~~JQ8K%?M>E#< z_0OD`_In|gwwG=bOLH&N0S-I!4y7f;w2XqLk_z$?6dMRg6>gxt^h_DjdPdy>G9raJ zn2p?qQqyS|irWaoY_tFxmgAqsqC(W8LjM40sqesu*Gb6uy30}$(Ta;(EijE9UXh$BZK?L7bZf|b&W(Q>IqVgkenx(&Zmpi z7dt#q44pJSsOc{bZ5%coCXrnHh{vv;@$ za&d)~HshWUc;O@|(!A-wNx%Z<5-(KQS!PxF_;+U<%MhE6^IssyCk0NGr@=z>fD(kQ z!lROKHmOVM`i=T@jCAxki2^a7wFPugR$h@U*6S7N*eRw#jWPP$wtQ zeJi&Zks(SB%9845B{^+^^GZt`=}USOr)=;Bt@)}o(%~@|C9{8&kuAVNO#C5BPIekj zb$eo^#a@TERc3+H*DJ(l79q-u;4tPA6P;=s{{RZoG#(E1eCjKUZhSMl-Y?d-kjr>2 zWxo)%;TYAeD#lKEAd1LrZvic(yviCw%Su5F2K4^`<~xtIXrC0P!GqBa+ime$dC3VY zd%ty%L0*h~pBS}l<7=_r!f$od12eT`R~ktNZk#qqy98kv(Z z!s5c2E5YAeVLwhP9#T-JO1D8LI!MU;=tP5S#N^lkSp;R@N_pZZ9^BRYlbQ_Icz3LJBzS@emsEJoyM2%VA zia>HDCfq`aIr~&Ri8}MW1mxngoaT@mD&K!fv{5$BJT>;Fk#AF{AbqIlvCGGU_Nn+5 zu%Hf{?YF&EAv+->*^b^%(vND)0l-PCR9is)UHxi7yE)hdewDi#tfwN*Mn;3Zc~*Q3 zXsgm~8-5W@xoQ9$Bz{#nM$JZZD$1S6N8i?<<=Ki?l&2W&T8yhu9H;*P%}}##ARK@z zno+G2akf#|x2Xg0??k3Lf_SR|w*-^0A8Hm%aD^NVzLm!y>YcVBkh7JQkoe^j}gXq2A)K2!8CA;amF{N z*^Rd1qVf&M4H1FCz|S00sfslI6TL_xHvAfSF~%@Qed&F`a8;oAvW9@{!o#c?h9wg!$2?TX?UI)G5!!o2kT2Lz7AQDVT|vC=}C4j zMsZUqh^%VC_O7lXvA733*F3Tuk}A(d=gUDo7di5u%B>GVIXh(WnhkJ?BoVbiy6{X+uMq(+HFf={YX!gsR=)QJ{yia5K&`S@&Eymx(}cPxSu)S{JadlfMrOHwTpp zd`6xK!AMlM8943bN#7OQf^l~cpi&SKutxzsvDbS z>INhLpBhU`j2R#S<{HvLD#mvl58=&hx5=F;aI?QGLo?$(G`Zr$`Nrd9=Tg>$1*JLQ ztn7Eqe328X?)Jynyg1XyLqzCX(zU4P@gYPcD;eyeMl=1Qp_uMDbz<1bXDuk1GCXIG z8PeLb^f>wu4JJ6tq>VJ6X5!aQ%1LSY_WMj&vICwK-rH_Zi50K_8E`X|XWaeG4{+(J zc4iVKK)6n_q-8%c(@AST#I);98_uDyImsBuHIc;l`4aJHofQ_PyJu{r)fp4k$_AY3 z0mW&?Nf=4aPaxECem~f{owm@cauXqf6A~zn%f*Zxma-5MJLv;fPUlZGr;dLHi(e+& zU}vXovf5h+>Q5QvtHUtR?1>0K!3`H$ASoSya4#9}+faupWP5x_mZT&);PIM|axXC; zC@b-(w!k?~dkmlE2C;sc_>Xas8R-z`-mPh6Ki;k6wzrfA;te!{vz_@V2OQ&SrO*D; z9}BGCFd0?bsXC8XOod#eB(~dOSqf2cejAD;0u%etFbQo$U^WHIt%E9Q(>GqF==+t< z_M64)rEXSYwJ4(D9zxKQg#HEih+154Cm<~<89M>Fr{{b!v|V8v)EKdpw1utd7RUS_ z2m!vPRFW2VI}%5*8&}3%bMbSj`hxJQr%qhDo=w9@2%6>P_Srscg{80>(}Tv8B|8r; zC_6vJU+o1YZ(eNHHm)}dQjsQ2&N*?p#0BY2rx2v2%%~7GA!S?S5yg1&`cfxccG>n% zPv2z3LSBl>E>M_nN>zYPoTJ-gUp4;#XfBSn+xpntBijC6ETuN< zR|xL-iHx#$3rHAJPI3Ih1D+48f7_+m3T=;w{TSqVtsM?dfSpQE&V22a44i?u)87KL zE}iR7hF=?D{>Y0dhsu$qEV$x<3nyYm#RoXS;1HaR^ISRSlhZqrbai2ba`g6X-wh)& zlOAV_6qKO|WpS34(E104vY>(dszA~>^AX=QZbk0bY=a%U>fD!bag@3tv=p_r+P?;r z5w=_*I>^RUH~`}~={}o(rC=f(M5XA-W;+UaYQv#v)U)ZVX&D;|ExxNPGTZWH>K8e3 zJVz4w{o`S2OMZL3AsCNfpdv*Okp4<{WVJqOl$zM z+BA+Af`FxY)I7vv7|Cun)!n`wUN8MaW?i1d#HFn-IaUV z5Se|pnZlcG(4?}Kk&Nn7XI8b0<0~1?anlyLDiOC-W*-6iP08}5E*p=-i++k>C7DN4 zo*`jDD?)pymqrP|2}s=5*YIPbru5fWKW}~%c;V}Wx1i5$(613CYFnfMwxo~}b$M}& z9kW$`hnEQb94#(iVW;+$jh3RZH7nQiPI&kX5yq_qDJvP+0o!};Nc9B!=S$iqwGg6x zy|A^y()01ac!_?;5xHZmR!=0;3?E|uUczNBP@CtAFtVdMo^ z0|)A^y@7n{nfCsgu*Xz}p(N!oUSVzxK!TON`<@F21f6JU#c~i6_^Tu6D}9&Y{fQRI zH!={@Npj-y){v)_&VoQXwI4ChlmMS8$OeA%{h~j!_d>MlBtL4nO&KAjT$)HjgdZ}> zjtaNnAw&;P!&Jwc+|vx;)8vT-d>*lMOU_(ww>v$NX-Yf^R|)29w^B2fS!gB2D;<)7 zw)`E$=S@k!^(=I(S?;%6bCDOQ+$~xm#%5|kH z(5gp;zX?bwK3o8Ju9zq#S4TXfk-KWmK3`6Bdunp@rF5H>)2)uE1=x|*;9xfFcE~tVlD4os7LF7WalQ@-&MNs6;&;QouY(MA zeTfm{OhR6RB16bQ(g7d==52fvq%5ya`bG{1>RXM8HiI_795{y%rkZgpdH4>Xa@l#M zVL98#>Jmml0E(kZv`Rd#q>A;nRfnnCL9=3jAN2B zRqLPa_${NS<+IXKZLhLg1iL9-I!dstJlmzDWB_reZuzI#lW@C6a@ix{q$gm=N|&MA zv!*d#z@timEpNY-AY*)IG*y<*)AkuJ+^lFs3xhG#+AZ-WL~$W!gee8&sbFLH^n;wA zO6F7K&W=1*i92fN~(>s_O!tmz-dY=9NE z9CKRvD>s?j-o7s4fjDQDH;vm8*TTi zA{PcPO)b{mY?l$C2}?w&Iq#m?{ObM@m-lY8CdypR6mJo{{SICA^fOh=8=fWINXgnsfcHB zzV!zcK}Q7bQ;@;P$KMq;z=`-ysXBM5_>#1MGrd4VNjTUHcBxqDQ8?3{{L(anPPK4S ze~5uZe$-M^l7A|QLR2>u(vys23N#xeS9H~|S85T-gy#qKpp~d_bMKnsNhi!{k`=JD zJ(XaZl!no8=_=-8%}FLO#&AC>>5LBer1x3{k)9b(+w`w#{{Rh17|PBKaL2|lKTau@S~ZcYZ#e`| zuE#r&Fa=B&FgV-vrk23Y^+@Iln;_g{{52aYHl6XGTCzI9PI1T5fRMo>7^=rT9hmYm z05lFpJLaEeKBJ8^X^b<14G`3Dk_a2)nxgd@D)J366-0g-kY+akjwwvGI3kT{p`41# z)DfKQLL+BzGf#+sk)A%3V+cvYig*-a8b;Wx^BnS#8X;h310%H|$fE}dCZ-hFBS`JV zGFnd@=bDuAk#5LGA_Z@c(wG+VPU#zZ)ahU-@%dL%7!$CrXOJ-=k!}MA6tCH7Jf|kC z4Z_a)M*h_4hqgB3(ucS@R8|*i1abOP4qEUrq*buP17Ik3p<|J^(y1JjG9hi2@S+e7 z6;hI|#kK}ie?93;=w(?0j`T|3OX(>I;BlJteKuPQHNF$2S`aQD3Uwhw;1R!`tdnCt z+o#5G=^Nsz_X%m2G)8eaNauk-bPdvWxHl!Z+~D@49HYWf3M6;iWBF12Qymuw4v1=P z#^R4LBsAtv8d9U?r;#Qtsn&{HVn|5}(wB7U`(vNI9n>9D7Uu~LL5L#CXca5Yf`jN(+7mlYMZ4yP1b3r+^<)YcR91RC$crP`$2V$6}#_Jx$bmQ^84 zr7L$(&Xp~*qfqRk5PMYNCkQr*O(`@~dQ;%F`u^*zqBxN3R|ACs#$HlA2yD0xah_5~ zt}E#8guem$TGMc0i{wj-swq%H-)RUrUdl-f>rqawFhYKuRjruTo|t&7Hi&llX!182 z5?%4VRFV>(IOk4IeR&nEbjMQk)t1(j+pe+Qj^Ng{7u;pdB<`glSRo^xPAi)xX=axh zJrs^Ljy$5a6yL)>owjuK$}B5vR$DMug)6}1gdGP~axihgHTj9vy%y_TNn`4V;uM>m zu)6A6I+R^Y%Vl65#1NeS0L&}(>dL5BSjv$O=K^~YTP>;9W-WLa#s&wJobF0%^TXp` zN4wIpZ#_QrH>1hvxzi;u;q$olvQbLXusGDqt+a4C8nc65pP|OLiFsN$^GW`<;D@nv zyQpM#q`#h*>Xm~HjTSS}^K?>X46Wi(Fti0Y~)| z7!Yg^D%l)Qb|7Uw3oWFm`7)I_q=SK_Dp+h_6+6dz6585bpDto1I~7sVqFCcz31m$Yy%yf@H z>GGjk;>hlav|R2iMrFw8mej>KsEAu(3WAhIWwaef%%8-Pq5&sK@0yRFe7N+?w(N0k zQdAg>%xMonP)f_JIzV+G{uL;UC;{A^ytQ|;Po9?Le8f1@Au#jG(HXx1#}e=uY-%|H zDNBb>Do%oT=Cf;oBK_CV;=d9*vTl+PjHHYtHWZzCJd#{GhTA6_k(y-H#Q^l(A~N%$}0}75u4VK+iocQZp( z(iD>Bq~k{5q=Uf}M^tpAIM)Fqqs;iNMN&}>{FE#xek}zgtqqgDl>$9LtZU&7wNd)6 zY@B8rOt#Q2Em$gAfyVrh0*~IlsflA|d;uX!LKoqZm8&`c;O86f+iI`+m#KG2B)-e- z{YiQ#admC}?8UC15jvK{;$M*(8dUb7a(XLC@aHy^5}%liKxM;_qO1fGe90}jmle5u z9k_KLSi_%rwOgICGW#rormje73wKYMD?`9|$=nQ_x|{%|xy@wv4xF{#y5b#@I)c*L zR_w*NtTxy|)5sB(pxU0cKecLAOA_0jY- z^GC)%gsN+#nK_NNY0Y@Yb_^sYA#QvD-t7B8*-kXm(NJ^CzprkaE zf^?^CIA5VTPC(+nJbKFMZM929zSm@3sm7E^9tsd#17M-3c%Hf7RF_BmM!ZCIq_-tX z#!^zS3KgEpPB3excy)0mvR;|?KgA2=exQQMcjj*HFar`Jt+Y0@0CEc4@dmNn0H8VB zH9+YbG)Y&MS%D?yn$~uEDAAtYLVvAPU%kqm3&amP(pH_)!%0hc$8`FANgqnT{47e| zWphkGP|)EFrA0({&}uWa5u#dp^~elFMX9MQ$CTS(1uT`NUbym;`JYP1Zpf0$YQ%&q zPWVblKc>}w>T9R%NeV;XGL=RJ4YsK5wh|8`%4;^i$dHmkrRhl+(v=aB?geJJ#Lic; zyCvnV%&7?{JM)j$nq*~r(=M%UJ)9g5QcXz1jHQ4``sSk{8~F@?!BHD#tgs@Dwg+*| zM_b{Lp|K*ZcF0-MHlzXxiyH#~f2B&tkAdabQ_&!#Bm+`R!brfzDQLh+kKdE;O}fzo zH8b*u8x81b5y==SKT10_5N{2;(`Y$W9Q1Gw>HR6Tc%iJRtsZA zBx5_&R3s<=07j?n=*a-~JXI2)7CTA?pbcE@Bqtl@srLT>FMrakmgn@5zZFr7)w$Gw z1x%sDY2;N>1c87-six!tk>;ad4SB$GKu93u*PVQ0ni1&Fq=zy||}a zY<3jtTgLdVFLzr!-vc3{2NdEO$-o&D@*Bv;1w5AZRArbcv>~=}+jH$iw$TGLrrRTt z^rG5lSMc%eNV3>$Lv3fa^zVbe6jL!2NZ8Rr8Ne7k(BLj*xh#-^Yq*7>jphn{03=OH_7#?xONTMDF zam6|EqqR3CQlEk(!x-F9l4D^cV~%tBRfNaD;LxweAH)Z>JR4&osJQDc=}HfpkB@Fr zzY}1C>qZs|To^~^UVZ6pqX__vSFPz@&WI`)a?pe+E7UjMq^;7G3>0MXwLtju+OhtA9tsQk_$a-10IxsK1Es zFoIL92sr2P(sFiDX%!oLZb+5#&CH71s>XbGG^CHND)#S8v~0?VyR@$nO_eREZ6vfw z8){fkJNwpEWuH1k{{RK1*m1VisH?_XYAMeNR^vZv`Uj=DX}6-Hd}-=zV+z)^zNHb^ zsHhS40+Th-NNJxQb^idv`}BLtybElUG6_&a%2qZQ$x=WE?fKM~!`_~~+x!<~O|!Wk zQOcuh!|6J=0a{CeM<30D`d8K6MbH!O0ZNRZ@mTVqN%05QNhAuO-8wTu!;P^GwYG7o z=a7`6d=QX?rw7<%``4cH$v!P;>R8j*b-fWAkd~$1Os5%W1xJhEIWdm<9Nj7-7~G8i z09v!!y3#B;X|p(DA|jRI;q@eysBxVNDs5h>&fcc7gC64*GoOT^mq$;lby;-xsrZ4{3erf^G8k!0vk!l0~tsg4DYp0bT7vKyXnh{ zEi>fDy4jo>5E^x1Nk|@CD`biBhtxhW{VO%H>pmG2h3dX|#;{es#KgNE8>U6f+{$?YpNCqUf!j#zq@TI_Tq~{y% zYw3Q!_zCd);`|mmucha)Yw9;z3Pgxbq@mn)T0qjQZ@NZ%8u`!S7xrQJlhZ4HOum+f zdW#rpT)1E|;**VvkdwNPY>My5{V%O*b9^WLnavfK57QOOKiw7wJ}2~#OWGQCWz)ex z%ZMzsl@*kflpJR%!9BgrWY2XS^RI61Pd+2WS$;P#)Y?5<jq(^z zlpKGDBQ$Y(Ur*_uxhba2DdoxZ#x#SvU#sFk_~+A?IBu8OwM;Oikdfgh#2P$3c_BpM zN7Ey0<2CwXBsFsBf1X;vLYqp`NWmZ=AH(mrKBs|SmkOT)p}IyFMk+f^OTau93PK_fuJEgn<*a4h+dh9&!^-Pgp z`94)0UXL8-uZC4Fkmh{-(TF}ANUbaw=}E$zfA^YIfq()^%9Ft=NLQe$z0O9fJZYvA zF^poE?YJ0Ef|ggQVZ8E;nQAwf7%{5Ti`U_AUeKAoL;F|Krq<;EjS zw5F`dY+NKSEvar4G}4kn8fcxtQjmv^LCGn_N_1o?i1?|~kBKp#M5kSF4-AhGtt6Et zJf!8-0(Ss~cBz+ba_!bwRwoh+ZAmI=andxolC4Ww?Uby9@}iF%kuUniv| zJ-H64wKUldX*k~$CNmx$GCuE%GCmWvQn*8k+M0F*l`^a?`>y4sZfMz7X#v8m%1~9YPvHP= zR0Vq<>DeQZxqKdfs>LjEQGQJa;744wVpr@t!>DzpR?|peyi$BnqNKZ#z6inR8w&ad zsw}ZDmdA^PHzCrJoG-@6T6YII7|w8>PIs@4JZp?=nxyqTr7CrCl9r!>Q*L!3Ng7r| zXj4uMauk7`@!kfW=TM>*r4YO(dr*4NR!KFlc(6Fo^~sPF@REQu>~ z@E|siF(mf%8TYJ$C&a&q_ooayZ1(KPRucN(Uy9ntBzeG2c-*LQR$hg%{KB7aqbuFTswN zVQG7;dgb30_mEN>fo{mw;y^vY(2RaBIT;QEU!a!mn}yL`Cj+OgF~mkKaZwGIT^g1W zl!T2bISJz=>^bJK?}UF3<9sB8E3JCA`wCxDU4MBpWXF>nwgN^HHLDFJBVmEC$I^r9 z--|Ze)PyX)TY%9~MrNTbNm7Oa{%oZQZ*2EbJ8_z}T=8jA`d37uiQRd9m#(GiNmkIL z-z7qf9Z7AaGj5WE6^)Lk6f}dl-zV%V9qSK>_L&Sj5-dr{kdRwT%t*oVk*N7l;Xa&k zRr{w|!RgtEW(@nNOweDkNd#vFK_j*{ ztb)%D99yGoNlS}xAN)W9PuOqzR*fN zDwqqIdH_}LPxE7^Kj`73Io${YVs|WfIwS4Q-7fBasEIAe+aZf8sw%ljx2lG3M=4z!(veL6Nz0Hd{bFM^!<0w+jTO)pU864uO zciU`qFczlJl`DK4X0OxYH!(-RC|;bCDsySH%4xd z+v+MA-+n4CKs=05&M4`{0V_L@c>cA+lcSzJ#S4-zV0kp-0q^aY4n7R&WJat?<=>k+G`c ze7Xu&afq46mw$ROxIOgqQgJQiKoSlhhX zr61wr+J`ohKZ3M%SaaKo9d;eR4|*T%*|?eUvy;L|q#II!$rYfg!;Q)~q$15PM= z3@K4WZq`OJj`ZqnXBa3ym0d-MpWxf+PNv2&_&*_A*kMsA3ALPl98;*Z?}D6fwOxN= zV15pJ;*^U%4oY$Kt?V`qET*R0ry5hYu%}T4Tol#7+46Qrw)M~0;DO{nTvC6xVc=EU zw{M&pDX1z00Fp6Q1lmac98>Mu$T-uQKETQhfUNM7L18FCbOgn8TABR4a@xwzTQI~EdUcR-}TQw&tJ5`!Y5L7@$2==J? z5u_!x5~2nxzXUkDDwP$bGa4iNr#s_*X!)%NA?s-%Z9qQ4%#0Dis|-7A+oWYiw#8HH zLUk;RB@L(fmbGChPr8T|=yG&XXs+4sYlQh{&CWAWop%m(?bJuN#eQsuPwv=h3R{jJ zK$L<{+OP0>H>ct$6K~feDGNSS#&_l76Y29SNn77W8&~||r24n1a}- zDJ-_8T37tTQFZVBtA#({tzRbP@MV1?@VBX(Z7+35vvll4I0Dx$V~UWB`n1s@KSv5r zwQP1Cr|1rtjq*0B^Dgf;Fyx6#G1*uAxdB9bWQzIH*>j)NokoFjxj!YwloHBZAU4Q9 z!rN&92p{r+T0E|rCfZqQ=(ws^D{*Hz_N2)w(nTP-ZL>_c^?YlbcXT1o45I-EkcMOF z_tF6Sl4~-)-kEV4U49cwa|mWSA8Gybx0TET$id_5Qe8aD%*S#A1yT|bhb20nDuy;b zbztEC0EV-!v4?rI$dDtdN@6ICx?E@_N8bTBA8*dQ%)hGScENo#jV8r6sBI0kZjo>k z{$qcZVmnR@_6r1{Ap8DawE@*}qg%W;WZ31}+6yjdinkE8C>@j=LX-jLjFa1X%HTM} zCS1BiR2eGC(x+hSLKU6ztb`o;l?t?3`irFBf&=zh3!SA!*Wuh?G|IF1fIuqJIQ}9@ z?NhDUN+p}M+2u@vmhSCGo?QuJ!hOU^aB`#;(`m>wHm{DnJ(Of=z2IBN>p*>{n1L)K-)Q2 z)3r%jFVgyDO|^2f^uwqT40sZ*&Ce1`qzx^%p)HoicilwqkWE(>g~@(F`ZKQYT{PUN z?Dc4Ax00n4DfS!kqzo-6DoTR1gPbiu5Dw(l#qj&%lM#WG$$=G5CP>Kx zQt{z~@2rzpwEm%k)M$80mu_NGG$KkZ;ciD$gE)B&tpm)Vk(8?>gOGEYkAC5Cf*HBY zn|HZH0WCb_)}2xr2{;}Sf^?vqfrVg>{*=;bx)DbEKE1R2Rrq6iNRJVldmb}MQX5c5 zh*EzMI6AOB>pA%2eChLJTXvSx+=pB!Y%QdrF6487HtaPW_xc*gVoqXgUk=`=vRqSb zC2dGlW^j{}mt1izww8e0ft9HL01!Ae$$e;|!}jK_(3_GHmK$;mbviLOPGCwFI_^@Lp0>g)KTpPBFIHv+7UYwx5O8 zz}mvnOBw-bBr9};x%5O+QZvKEdeV0nTauh>vQF z1ZK-AZ3!w`Op>i?BL^c;JY*2Ae%KdFgovxPqSVaQwBbq;mk^9N0Mt&Bk_p)=PW)#) z*Po6{gG{90`KF3xy3pgWSeo2fDp?9chC^Yr5LB%V>eYo5lC>u}0373UQQbti@7r96 zqar)ewRm78lC4R?&=jPcCzG7_BDD0mY?Ekh*QdB~YcSzg=xfmPP zL)LF5(ENyY_Fqsa@YJI44U&+Y0#xP{p`G`}TOb^nlY8O&yzkn-ES@1oU+{% z@5{Dx8DU;j60?E;I7!j~$m9y3SU+jaLs8>IOoA2TXnCOEQifnK&4SA9s}7R16)d6L@STn^zS?=L)1qfP7U12=&woryfo;sTnF&_D zE7CTIk>jN{^oA$Ss#r z8*_9mDH_sFoQ^6o;d_Gg*OfD+MYJi9w|+_*qq6ZMNpoL_3RQ%R6(`r6=DJ*ydwqQD z=Hlpq>OY7bZPZuRAYQtuaidA_-Yqe2T4Ow<`I3byLc9+ElO|zL?+#8IAz{$6 zGx$v%+A5c8Ght}7qBC+IdnHQ?2z3DQg|ecPo_Idupg;Ck0&~i4dRlR0 zoN{8I@T7990C*4A=ww7bZK_EEF6XB3K1uESq{{S!0Ri$Y{S@GSD6{P7d zke4(cMIFwcQ^@o*lgT9M1~FB#6X7SqJA4<|l>@_0h3s9Bk~I(E!mvM6>}zwrS^APJ z9|5GzWsM)o5|!=zD%=o#Gg18m5$LeoT*zo?r47)eY0`H5KzUEpbDYtWXWk%4b#~=V zf)ANrv!({}J#YZe(B_*GQg-_nCC65065>8mTAvv#xCfQTeERqH9+ev9v9(|}TXjHS zlmm~}v{iLElH!Hh*Jrwn;F)m)GBfVBi~vv;yPz|s*uKj-_mK>I4G zC?-PLoyN0ec__|JO~SF`PnUYT#^^*nwVwHCxRbbT5^d5j~S&T5$s68`x;+w>>EXT6`YJ6CZ{6XFamT`lvqFy!%~sr3=ODp84(D!r_!Hp z+%x!d_NlbQ0B(^>5yzztA+jkJ@Blh^q`d(+kJWPzUhQky{sY*FHB9g4tC+f=I& z(g!pX1ScF~q#9VG#KCPN0*q-pXYWBF+;^fHdm9x}@c>Xj?kTmWBWmx8F}*b7*v>Kc zrE&NGJchHdH5Dc(!60r$OD({$oQe_Q*bD3kBz)C;>Z;JPQ zC#P*}c%UejzyQK(UNyHk%703eiv$g{;-%w28+mKr!+@$(M6+t(Y5gdqTJiil)cY_} z0Lo~V;HT2N5nM1wprG&?iixwQjTE719-jcyKrFwdT8ts+ zKPusc+$2`^4Mkt}Kt!mYjYc(z*LF9n<<%m*mIt z@$Fs_2cZ0@dmm#_TK$wUvTLH*zv1WFtY#QHX|H^qPH|h)>K+cNTAwxL_{HgRb=gF^0CvFL$-(%Gj-k7n8 zooFLlTL>XQt$l@ScZjp>tn0Rj+L8uV@!eldv-YYdPO|N`>}0f7l2lFyCarEmpFUHv zF0Xic##;(bcJ>uHzXM~@TXbBcfcx>-QoO_~!ql#L8ncS0bz4`9(pien&L!k*RHZf$ z4tC%vK>LweTqOP`B+be=8rlon*j0Y_I@;8Qwt%-%kKtP=&^^Kc-|t*mBAMBaTufR` zj^?M-+_hZgsRweUwxj|(`O}Z{se2uSNOh!7ea1^(WUUs&4FmpmlrnMaT3VX6U6}`4 ziE|!T9%&(IKA|HzXsaJXS`(^U5hBcRkTUzL)K%(2Ml<@?BhM&ybo**(sQjnww-a); z-|kVv4X6Ui#-e`~ol8nUJ;uMT!l9#e9fDNVLz>IcBlutMOLjt=QqDZ=#-mkC~aA9jdwBolGGhtE_ zCDpo=ZLrIUXdze|4PX#918TD|@DHcb>uthq;Gp1HX(5#%D($0E0^8>vQ<||#>Dw?>7TC!+Bphc>K%&yf?IS5DF3g3d?enC@W*i~#0A*aMxb{{RviVmjQi^vhQW47L*CM8zReJVcUI^Ti7#z<-BP zR@oypyWROI{`?1Fm2?fLkR?GDGES1R&tT4TalFZ&L#8_arqJ4d^)uh zx~R|^Y-?JMuM9MuKmeR)&Pqppsiu@&kA>f7&lXEWi)xiB>guExoKn^h6{t1Fl#PKW zow>*ct^Ff?m1`F0*9%kWeo{$Fn^KT+yaAPNMiLI=a7Y{0vp3<3whLFz%D0^A3scT% zcS2M_&nh`cCv_blas_2wQ5toPF&(|IGV{xIuMbK>aIeIHoPa$BKD7ztkEnlht|{V4 z_%|Mvy0s;dAt`yaINO1=f(l%0DL;n=L?~l?ng&I8MPm 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 z zQmwWKC`+O{gz~VLRQts4xAm27lTnwBw0<8^T&1b7({^aGZxWD3&CHUprkE$yDt;?i z^p2s$bWSm@2n(>|1;;VC zX*`^NF)Hn_$8(B{!vxzZvP!7AOK+%xA5$!)3M)!l2~Gy*d;!V8$DqinHP52&*Tugg z;KG#J-au`=T2GYWI6bm;9lcMjY&LuR8SaS1Ne{Asr8Y>)bH-0@DETbIZ8}y*mr8)n zH`@dXXemMcB;_RgG2Wf{e{iuRrHsb#3oa$lai9UGQTlm)!mLo?b!D;SCSREea0R7) zV3UEt?l#BTwEKHdq#;DOg(b$yR5{rH0Nj4H6L)clK_T`2?I=h&86yCIe^2XFe44hz z^&(MJ1Hn_ML-`>f%${lq8HVkHL+3}Rxpr|t`4J>1d-T< zf;}ov8g9xd$tq@v8trgQhNre%kcr`K?sqHD;VqHahZ%W3#RnT=phv~K_fExks_d;s z@gqq@S&XSayOzVKTAL1_`BaqzxP<~RNcF}O(%oRhxV=juw1WMHRDP*t19DqpOKwX@ z9)*=SulkmI)iz8gPhIs#!b}Z!bWOI}mUuJSTAzlK{{TALEf4rw30z3JGK-bYbaBViU-24j5g~H%@$kz z=3T1Eba|IoagCDwl5=2)*EWZBGE}6vj1cP!d9n|e8^L)!MLVeNeL>Rj(bTM^4E3i@ zLwPO5y|9cHZg5;aR5$~PafiqEWz?n3QQy|zbQE$cT4y|zpz8Ey&D{PYbeB?GZ7D#q zbz`AFGYq!V*3PG4Nz{<*Bq}q`Ek0)5NG-ew(o*3CcF%{sSrY3V);dm1PMLmdnVHhJ zQ}Z1;b9C+dw7yTtyiUM4p}xp{2MDOSNVIgU3wJ?vyytHn4LP?Y^q*3*6<;-MY`8;# zEgv?7ItpaKQ3`3$r8H8kc!2enOy2DZMZ#f>?8h5IoS6~8>|4B7K@87fz%c3@(xfk7 zb*Ib{wJ22FvPZAa&*;XezW)F}K0F$Ym0dj5;ZwRw=_ld$RWh|O^a*noh>kc8l{Va^ z`I_7j;K~IZyhB7@E)W%TcEj-xrlj=s=VdN+raDO|V$q6ysC-|FbM|Ud2@c#g*Ztw8=tFU@d7x)Tg*?rwEddU;@2S-h{Z!>F%Hh z_JT#Ceo92>j1b9Jqw&-DcjZlS-3F@|sJ}WZ@ov-PRWUBxUM@7EB)pYl853*{{ZI4a4Dxgr8)E>v>%ApKB2O?8c)2(t`(Guwa?;0!44fKWJ6gW98xDc`3J?aQ#aCfOo z4^dsZb@$%3Jl}^c0jaShHXe&LvBPh&e6A?S2pi1?^!=e=sYTi=5=F~iJ0jVne*LC0`L;EaL}K_hw%PGYB^bwh4jnCb4QjP1(O!gRz^ z2oo)!{{Z^4irAw&mKJ<7{5~4I)g67aJ!lL`YFkt7vvn#Q_5lsP6Zn!+e9gF=jcqH; zl1Ctg5ZAzLjasrXs)s4xf6koMk&o#~JfG5mlJYiDk?ZuP98WmS6h_P*^Ms$N#IIQG? zp||t3=tP9Z#PP6o5upKXs(Poa-$WDr!e zPik>2o@l=SdQ!tf(ue@VL{|z;7+dzFmU4KkU_AuWt+uT3PlkP|*4qPYRtSV7fGN}k zZLy~ijP|FJ-?d;3fNQ!*{ORfWX^fwQ!`9#TVdvQ;3Y6J|mga*bY>EJ%_zy07OOw z1=miN!PA^>qv(MwYkBm|n*{t6AzRa&oMv^Q_CLJQ%A{Bm-V|s9QTQy#PmG z_=wMQ&TB-3B0_}Qg?QU0ve9xXPz||&E0_MImm1PoY2_6WjlasLifKA2IN7L5y+>c* zl9HceOkzFhl}K_zV?k-eu;i7YP6Fz`3oW5OXP#H^w2jXvNZzsTm3HkdEl<8W`e4d& z#^A+e-raDU8 ztA)-|l!ha+q^D%0sY)tQ2|^H%RHdkdp(!d+024*G@lwOmF`T#bwe6YL7m`|^eq^X2 zNl?O=gOC)Kak8HvE#1OG8#rGy@#LP~qQ5_>cuFwR+bk~hqkRz|IP0+PeNNi)&{|OZ z8zRT?X*ooxVDX(yvU@tQnvv{PnzIjELH&2wPQE~fB*!Na1xM7AOdQ~bL$S8wMCKAT`_WE zdy`bxKve;%bN>D&j!01jn z>CdTKxiVUnBI2@tn59<~{{YfGt1Rjp%-MZAHcYdb{YiaxOqKQ_hJ%G8*gG%(0KguV zZD#1?Z-&z^Q{Er6dfxo}*#|f}Lzz(eYD|&za8*wE3^Q!$_?YvC=E?&c_>z>^KXDvX z@<}DHi~hg#2q$~}zo-3`Lj0T1aMQqGfcY^l`{O-N|MM=Cj^vZ^v-D)zOH)ZB`>u4 zv^^b4KjAtv`2{CGb5)X}rdASo0B7G6G$okqElU|o4Y&X!v2A1g=&X;SeYMy%%50WH zvl=JHaDvx;EaZ5pKjB)El>Ie!G*iAbYav4p5*?DcNm74_8ZeSR>N|Soqg+eUd?J9S z9B_DHBa(On{Hj@?(TZX=;#@=|h=F5;r&uLJ{E zMRk`X$zeq+NK>t-e+tvONc(|T&+Q1NM^&cs!vQH>!s8v#ld(*X0^*O>;Q0Rlrn@lC z@k{K^dBrS??u-xPA56q&;!VcyVUDQQUaV?ARN4?YWr^tV%On2)l<|F^V$Eqk4En17 z0MtD(3M@Gej=T7A4as&%2?1_g>`et1;S`;naw-(!(1V{6DxYfvG>};88#{eN@UGQy zV+((!F0w64Kg1zT)h;nU$&iW39-7CkQfxPiw@=^g{Xx^Rmv6k;ab;Vk*&GE?qD&zxlWTNy;icfytgG<` zU1@1Lka25|($aJ3T3=TfE`_B%ks&7-OZ#;>-q(N6 z{XgV_jcN7#fAB@BD?J(1P~EyT-1`;cBG9R@fmCayvBDDK+s@%>AS*(ETW++FKqGm% zT{#}lCPk&^lBc4qwj4>>)bf?2{{SkIQladjLG)v-^x9?g&F!fYY_g;mortu6+D265bpSqb zl#R(q4xoNC5$85X=iGXG=Ao_(OO2{(f~H$!l`1rnI9HVFZPENLpoOQ@jHJ?flc$@4 z!g|-DC(T{9B!7F9nQk!`TLx?2{LcvOs zpm0*4cLWeY2e>qUV5jmP)R$Rx53=ixI?~ePZZ@)&wzQn3X;2`gK!8C9Al0kIXRnl1 zp|(1$M5y>_N*cW$_(;NAm)`>jd$3EJE|cRAT5$_W7zgQtjD7o1uxu$xBRu47#twd+ zsM|g9cE1`Vh^d`yno=i0TJ#5W{udCf{5O-6p~1M|g(RsYsZXA%jP1=wRq8TJvqslfD{{Y2E{`7wk{ovoks0F&5@+fpQl9!txwxOBOC4B9wX;;ciD9}ky;%U_9_irO3 zQaIcxB&35;dD!NTvtQF`ThXP)Gc9wpqI5laONmZ&xP>3U@-j*n?ox7{VlP_17HU=+H7srU)TZbRo42$;N&PCx6*xf1A4-Y0 zwv{Cy?TVXWDFEQ)W18{(MAe+LXW0Tbd~Zuj0CG5^!&%tZd=F~&k=6*7y{W`Cj?_w7 z7^S7JYGVxsfv+4C%GF}(Oq8aY{hWxur>1!U0f3E z*Bc-llg)DGha;mCqN5(=B+3!n)JgB2YeTejqYv7J`>5D)*eN7OhL;K#J=F4mp}HFf z@whi1g+61H#q>u@M!2};=I9oSY*i#YTR3s|2+Cu$f1N-QRl*aMAK?nsQ3h4Xg-x1< z+rkplOtPFb!zma;NC$+ZeaR%8l1)iLEtPU~VZRWhUEdPp!&+UDHI~?&EtdQyiwMG- zgB830)JDi2TxG>16XgIL#uvlRqU#%jcF3%$a$YJ8x2zxx0;MTSSCt4z1xi|ul%x_! z0=}?UM@G6VG~9JMwJ0G811eIEzz=g#y-U*YtxzAZS@i0W<+)32{{ZheAchiu@DiX2 z{{X~L3O-bzRPyAPZ=k_0muJeIS7S#?+GTWSM9EXpC!`|%dI>3V^MwTn?Sq9i0SW+y z&J_BR9Cu*bZPH@&mr&j&vj*P+Oqmj0LlN0w1x`HT)B;e1j#Nni4|@7i>C#N)UMndf zKg4(*^ogstd@_op_-al5;K>ds3@X_X#Qp*NvK>M59$Rf%c2d=+pZ(0TKe8?DDC^|N z@0NMp180-a-6btAUT#eo-A{aBVQDBQ&1;6>{{ZaiAP3$b->hgKxj^wM?2GSDBUTjS zfOFV;)`Ksp>?&V|P==D6X-Yr<3YY0+^!w;#9r7Iq!@HPVgU;$SD0EC@CZD#8ysOJST%rvS44xr&_dFCBlhZ1`! zm@P|KJ(ji)aY(YfnHD&zMI2Nh=HALr#Y8o3wOD=_^rP!~%V}k|Q1DI+meb+}9#{CJ zE!mTZJSJ>({{X@mOa`V}x(4ZX!nLyz`y*=3;mMA+w@4npD;@Ob{{V?oGh`zF0D6^k zkr}q0nChM_pkXa%@U}e?goL3Z(ITdmXL0LqOZ37ckN*HoTo*OpMJ30R5wFP&JbsQ; z=j!E`E0S8OU&0E)p#-00cj85+WxgLtbeM4Xwo6J(fH*HZq`CF;rO*7;A9ronquAuE z;Zdy7nm_X>DMbGO<}M$oH8=4h=O*XXChvlzy&5}!j@r@U+uZ~a;ky*} z!jR!{ztPe^I!u#pa!ar9Xr!YXD~-17a^q`8rkntS=tusI7>p&RkgblBkD*bc8e}ab zDInx`G}_uvh!UizB_l}o-2VWrBx2WNxi16O<)>Ln&I#D>&%f5BTy0GyL0l0~%4NvT z4B({h4r);njEI^Ra#As$YJMVEhNUGbQ9LNnlcgaspKUz3r67Isf29v4YH1kH98jus zI8;21S|o7PMZN~fT0zJ1sU#ohKPsw0d2d{`5>Dc- zuvUlSNRCHzAJ_i$i?O7rZKjhVIcSYbI7k@hCm-ilUy3a%?Pa;m)TKC?mU*ew=;V2^ z$^7FsKEk6cF#Jo@1{L^zX{ETX-Wn7ZvVDS5qxPfjZ+z{JLvEip>-uff>x{A>vJanD}=-=;=cJ(V^ZIAwO9qPK>iHU_0 zqkxr=6c4B)Bl8tKV13@1>Igkk5g3tamdxUj{6&?A2j&t--lRI_)f=aJs`X%k<{N`G z)Wuh2$5GbF`?jPXy%@f5UvJOyD_ms${{WxhnD|$AwpuPuboWt95v+H&qb5tr84xBk zyu|?aKncH{kNz5zq%Rz@>!Yx%0gom#H{OAq%H!Q^@+D7)82%)_$i&Fw8=Xxy9Zoq^ ze2>LgJNGN%XEZ_zu<8=Dox*`o1QFh~#%@FO9hBSL7crsy9qQ{>&BhR-juw6zpoz#VlQ++dVdL7#7WR}B=f+oDvt)$?M4k;*c9*Y2Mcq3!6 z+G6P$aMokG=ykQEEvG>8xY^-D`VrWI4;1PsOT8Jr_R`YA++{^Rsm^OYb<2-WT|a4C zJRs(Uvg=PH#AZt>#>wMtMVM|E7O3c*t^ZLJ`KhY+DhGbMi&3vi{ng!Q^n zqBNDLC;g!b9#&l^Z=L{Q4jg#k1grPDRI-@3P3dcklOx1tTzM?2Ecg;L^7Epog0>yT z;1U9oq2@x;m8S(FtG!#(Q7y<~`P1?qnbWr=sSw_K9z1ta0bEHMc1!K#9YX~3I1DN2X#;!o zR@0R}kU5yz23!Mokdol@rOi|$m0+iEcLW`=^{rzi@=CFFdL!*ii2B)mm4_R32MR)m z1b^%FqwWSZ(i@lCTP?6!Df#$l*f6CK1FepH6&qeo96#nJFHa!dwIKBi#w8fYP?ujCZFJ!nUEv z4OsNHW!vSw0&89ri0du5jOPj-r7YumY_@s92iB#&LmTO&E*aw$y})oHf;Z!OQC2wL zYBa-9Ja0`o6+;_V zlCH;W*Upmc@_LV`Q*VgtZR*zR&%2)rGbtLJd3gR5Cx23Kk-CLzpAqk^w9BPVfB+ zaM^GTcu~sTa~pWB5EP@^%8}(jtIFC}hw{it8)Bk*bw4Qqu!Iqj%~`E+S&-tMlA;)B zc&{jO5IYZYNJ#fS)i~@jbTpWWvfF-W9Wo^(hR^;RMfIyIYCXkK?^kH3Qkx|? z;8MOM{6ddwOU}OP5~U504gx?JN=Kjo0rVAw#rTE()b#>jMU+})C~`!(Sk#D-n;M;X zj`~x;{v{M7ZlO@kA93+Tas4Zljdg@Zg2CzuNNr^cEy7{Jl)U)HR-xUt@(CZrqLlHf zs69_-wq71wi;SS_?x{{V%Kqs6AEj8uxpi;^4%3EGEzyi5*8JmQ$T6 zKC1pD9_mt!K({NYXQx}LaXV~sO>pJJmXkWM&GyNAufwKpGh^DU%!yOxzKGG8O0nWP zwFIPRu*n%chDqY0z9L#*nu~W$^mMIu9-KnsMR@}sIQrBTDsdjKIFYB(me^15)Rl$% zy@Jtp>Zvce^O7@SwC+Jd-EUOK$GRh^G!JD<2b9q9Z!27O(w4$PHrRHhZdjv9_A*N( zwq+efD)G{B)bo$Kge6NPWbiU;QaxdC>04#jq$MbP&qZ{CziORWKew*ZA|^^ctgy62 z{{Z}Fed=n>Vzf)2{?uO~wJzP$ZGPB&mq00f#@3Mq-;uoi=Xuv{y8l&}8)9u@bmioP9Nu2&C< zy)k5vhY-};+$it*cqQ}~GwtDp*3aNt4n> zKU80p{f!*%T4ssVV-v#>@Z)HQ2{`8fbT@S(L6>}Jx?OysRy^O~NEE9KvK=OIBO?Wj~N z6`>E66R-xIdU_Cz^yOp@YFr;>6uwK3&W53>5f!Rr^r|9A{{V)C;V1s3k`wHl{{S}9 zT|uMfy6I8Zskp4eipd;!apthXasL2HDhH#SYDZ979Fn2(@n8Ap$vXz4q`t4wtbOWI z+NHT*zP})9T!vvZ;V1Z)BshM%v}&+!zpwHC0A%Eq>+Ssi0MS!L#5k~`09tSqlfsAn z#acSLDZfVP>*H!6`L-y@S?;GIsc{$g#B^utREwcZIZ5#GjPLZTY>DYtKv?IpwFO(Q z*C;^t0mMg;k@>0L%++uv(?qiArY6nPzdW|w1t^fxhi}D=#a#M9q0+Z;GF&qv8|^s` ze01&e5gZtGvHmTB(t9lkJ*t47rA#-PxiGR4tZPrFtzRwuOA6%`AH6_QfYPjuzO~Vd z9J?VoBUguIMXKE0sJh&w!Jg;J%0h6NF{Cov%{rfDgcW14Q5}s*$GE_^q4*b+EjHPx z2#~a=C6x^5d=BLVlkSqV_oz;kxIb{{mtzoC_fE`?u)OY=Zx>P!eL@~yN1%9)!lz&P zYrIs%aUeM68wZS&wm(|*VyUK%8NF!+{Ao^AB$f;js70@`ti;W?Y9Bkl)at&23j3Hp10DbEFHaWXT@+(xxh&p{RV^ zAo)hkyQv?AP3C$GBwqS){+g1c36`Ms!qByxYHa!CM9KbOJaiA|L!fTpsH+>#S*MAO zM@)uXZBgY$Q6UneFCHT*^a=nFe}zdxhUpYN%Ke$RU+#)`$nW?~EfB^Jmado1mu}TD zD2V#xz?RZwL6EfIyuyh9tI=s8NLcK&D0)yIvq=6N9MZYgm{f0M3Sr~`Qho7Gw*(wv zJ5=f|#EfXoKHao}HWa?-coDE8qBH4nXewxehY~-DwQQmJZau)KU9@-(siH!HPRavi zr;x9>!TZ$P7Tz+R^m^dz+q-RPPg@OTvU+k$yWp!2%*($+XW_@-az)sJGS)=vHDX&+@h@D zlk}^jmVHM$4)mm1JLx-nQ+vSRR2sIBc%u<%ByI&-k!DJF(@(W)#>FO;<~IVMZA5Yj z`%v!NKp%&xoXnltwG-N}&dl zq?1ns`ikg4CV;^D52bfUwGrW0ygJ4P(d}Lc_M!`Tr1)pjv4+A(?Ow$>;h&{*PAC{o z_3lM?9+kogqG1XmkV(eWvXVV(pIXKSbw`RBWaoM{#=^K9(+XyRfy#w*C$&4^9w|v- zUX_Azutzli0NRq4jXajm6ijdvN*VO7=sl@nCV&nBj(DyfaZYK-;*q3^CJ?QuvZGF@ zW|>gM24N~Y^GPZmw3UK4rj(;b%|h0_BOm;4oe7vvL$8a}Bj8k9egwbTblEpu*0Wak$l z_ncF%y1mrniaAn`xg?sebw%ywUWwC+P!6)8HhNDYOQ{7dpLW^MoOV*(s|QR>L~tEb zhyYhUT`DV&do}ZPVm-SV!4((Q9eB@7^!ss?y$?FrmiW%&Q2o+A$U3p==e1W`yvLt1 zJZUd!jg>heWB6D?O5HzXC0~42JJ!8baO*3a$IyinA>}xvjuWWlC+G$M`cp&nno&uW z4z=oA_gdXuN(+f+RyOZfUW)j5^|z;5g0z&&>JJKR(mxU*Az6&i{c8pF4@9x)RzHWo z4N-FWf15@;bpW%D6Z}U$$;qvL4W=sPyD?26CBu}b0|`I@C`kGwBz>!&Ea^TaA#Lo+ z&%>z8DG6-uF`RdyuKgz(*989Lt3b|2e`?h(R|{>mId#U;bL&;dx{}8(^9>KW+SrU& zIq*jZb-G?A6{{fbjFVsz^$A-g1B0eVKpQ-{Tpudj%$A4Zeu zS>s5!Ta>mQWT^#v5G!6sR9Ym^?Gz< zpQcikexj+}D!^*r)y--krp(ic3hj%OERWW@px*?)bDio;(t2&hxoow}=dBWL)3WQl|Kup z+D}VMa67b{#8{(#%}0I3ey=xQJ;P@5w^Z|KR;M{2d)Ao^sZsi0r)}>8;!K%yxw*ga zm9rVM{{Z@rRU?ibeg%}}-~+5WklXId3qQIfV|tmibwZ|mGrO|VQn1HTTb31_(q4r% zIQ#e`@~ZD$^!t;Nmy^DvCvodWbbXQbdJn53s2?ob1cB^Q~aC@4a*SHsSrhi_6@pC~mJ=^+-0YFe-abvC_f&#P*6moxqfnzD5x(XMiA_jH!0 z+7{rFk}#w^(u#QQJWLAta^dkYn~N+*Z3{x;rD4PlRHdA)M0ydVZP_)WJ=D`Wj@4xN zg$?yPCioF-x&S=Lp8+XLs!;y`<21NWd#OwGHPWE?{>MAqjn}3xR|`DGpLbN2+c;VW z4z!_#p#K2Ol1H|4+O4lC5hN}vd3CHMY&w79G@+>q`=pHf=hCyznwqC#vb7+jl#NOB zKGmQ_@F!Z3px`cBy-xlD)&BtGte>-<)uYhOQ#{JvL&%WAi)(E`Xi()U2V+j%Ev&i| zK09Hy-zQUP;kO6kNcP_&*pEsfhLBP;kaO)u#$hTeisl1yFp^IRSo|vc6NBtcYP}n+ zqKiJxmkA{`T)A}mPDw^{T4alI(xoM6AwZBuI3tl!vTeyyPE%ajB3cZ`E*4D+Id3D} zP=edO$<1i@Yf1r82&($v43JY^blZzMBCP=1VT+Ze?w@fQ-j1ctw{5Tmvqg(v6*XFgH=jH67dqS`nE^rw+;18qO0Pa@xdFgyBF>9=piP0V!y2@t&#L^B`5 z40V&7YrHpO=015o* z#^UD#PTrWJvWnnCCJ5t6rHCW}<%kwopbw zc&k%Z%y60|HuRCe{HRzZ0)XwC93uzmL$1}6jXwVXTE0lOq#ii^YB}3}GBNoINj+%i zo-&+mz#a(tiYb;fU=dRCAQYWJG{}sk`U>Tgju}IXKFdxiM-(TDplhMiUtwsc5RB52 zMGU7;Cc1|+Ld`*7?~GH40QRO3plQ^fYQ~{jL9P@ESQ~e)5NMchM|$CJ+MY^9D5kX- z6t+EU+7q5B@{x`y1ORphia;7#0qso`Z^aTu+*blqS)hX!^SvcZjPXW<3gK-hwF3br zIP|56^h#R6;+;!Fy<-6Uxf`A7VX82-?MN-2!m)w$54|T&4Lqa~%`#5>&FILfd@IL%e1tx%86HM!z5KYvg8EB^qdpGs8ijMJj5yZp)52ksN47oUr zmG>l?wApV@xTf1m)xW-KiEDPe+FJstjErN$`~Zp4nQ+&;C>< ztTGVXD{)d16QmQcsi&$LfJ9Bdr|t4qjVKK@;Uxb6iDw`F=v`2HpWCN zuId%0D>0jKMROl@?0Zb`>KdeJYDW6roP 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 NZ`_ZLr78E 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%0ARQh+aoyvHFMcA@(3;r93j~|SXP>*Sc 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%0ALI!G*FG;Nyt{x8^#1#jF_T6<;Uyw`muDH?N5 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 Date: Thu, 9 Jul 2020 11:35:44 +0530 Subject: [PATCH 320/329] renamed to macos_xlarge --- .../api_description/environments/xlarge.yaml | 10 ---------- .../environments/xlarge.yaml | 9 --------- .../batch_inference/environments/xlarge.yaml | 8 -------- .../environments/xlarge.yaml | 8 -------- .../environments/xlarge.yaml | 7 ------- .../tests/health_check/environments/xlarge.yaml | 8 -------- .../environments/xlarge.yaml | 10 ---------- .../environments/xlarge.yaml | 8 -------- .../environments/xlarge.yaml | 8 -------- .../tests/list_models/environments/xlarge.yaml | 8 -------- .../model_description/environments/xlarge.yaml | 8 -------- .../environments/xlarge.yaml | 12 ------------ .../register_unregister/environments/xlarge.yaml | 11 ----------- .../environments/xlarge.yaml | 12 ------------ .../scale_down_workers/environments/xlarge.yaml | 15 --------------- .../scale_up_workers/environments/xlarge.yaml | 15 --------------- 16 files changed, 157 deletions(-) delete mode 100644 tests/performance/tests/api_description/environments/xlarge.yaml delete mode 100644 tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml delete mode 100644 tests/performance/tests/batch_inference/environments/xlarge.yaml delete mode 100644 tests/performance/tests/examples_local_criteria/environments/xlarge.yaml delete mode 100644 tests/performance/tests/examples_remote_criteria/environments/xlarge.yaml delete mode 100644 tests/performance/tests/health_check/environments/xlarge.yaml delete mode 100644 tests/performance/tests/inference_multiple_models/environments/xlarge.yaml delete mode 100644 tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml delete mode 100644 tests/performance/tests/inference_single_worker/environments/xlarge.yaml delete mode 100644 tests/performance/tests/list_models/environments/xlarge.yaml delete mode 100644 tests/performance/tests/model_description/environments/xlarge.yaml delete mode 100644 tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml delete mode 100644 tests/performance/tests/register_unregister/environments/xlarge.yaml delete mode 100644 tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml delete mode 100644 tests/performance/tests/scale_down_workers/environments/xlarge.yaml delete mode 100644 tests/performance/tests/scale_up_workers/environments/xlarge.yaml diff --git a/tests/performance/tests/api_description/environments/xlarge.yaml b/tests/performance/tests/api_description/environments/xlarge.yaml deleted file mode 100644 index f7bc5561d..000000000 --- a/tests/performance/tests/api_description/environments/xlarge.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -settings: - env: - MGMT_DESC_SUCC: 100% - INFR_DESC_SUCC: 100% - MGMT_DESC_RT : 10ms - INFR_DESC_RT : 10ms - TOTAL_PROCS : 1 - TOTAL_FDS : 73 - TOTAL_MEM: 100000000 #100MB diff --git a/tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml b/tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml deleted file mode 100644 index 97307b690..000000000 --- a/tests/performance/tests/batch_and_single_inference/environments/xlarge.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -settings: - env: - INFR1_RT : 6s - INFR2_RT : 0.08s - TOTAL_WORKERS_MEM : 4000000000 #4GB - TOTAL_WORKERS : 9 - TOTAL_ORPHANS : 0 - TOTAL_WORKERS_FDS : 78 diff --git a/tests/performance/tests/batch_inference/environments/xlarge.yaml b/tests/performance/tests/batch_inference/environments/xlarge.yaml deleted file mode 100644 index 23a443aaf..000000000 --- a/tests/performance/tests/batch_inference/environments/xlarge.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -settings: - env: - INFR_RT : 1.5s - TOTAL_WORKERS_MEM : 3000000000 #3GB - TOTAL_WORKERS : 4 - TOTAL_ORPHANS : 0 - TOTAL_WORKERS_FDS : 38 \ No newline at end of file diff --git a/tests/performance/tests/examples_local_criteria/environments/xlarge.yaml b/tests/performance/tests/examples_local_criteria/environments/xlarge.yaml deleted file mode 100644 index 6c3835292..000000000 --- a/tests/performance/tests/examples_local_criteria/environments/xlarge.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -settings: - env: - FAIL : 100% - P90 : 290ms - AVG_RT : 1s - TOTAL_WORKERS_MEM : 132000000 - PERCENT_DIFF_TOTAL_WORKERS_MEM : 5 diff --git a/tests/performance/tests/examples_remote_criteria/environments/xlarge.yaml b/tests/performance/tests/examples_remote_criteria/environments/xlarge.yaml deleted file mode 100644 index 674a6c1ff..000000000 --- a/tests/performance/tests/examples_remote_criteria/environments/xlarge.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -settings: - env: - FAIL : 50% - P90 : 250ms - AVG_RT : 1s - TOTAL_WORKERS_FDS : 80 diff --git a/tests/performance/tests/health_check/environments/xlarge.yaml b/tests/performance/tests/health_check/environments/xlarge.yaml deleted file mode 100644 index 689a5d66b..000000000 --- a/tests/performance/tests/health_check/environments/xlarge.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -settings: - env: - HLTH_CHK_SUCC : 100% - HLTH_CHK_RT : 14ms - TOTAL_PROCS : 1 - TOTAL_FDS : 67 - TOTAL_MEM : 750000000 #750MB \ No newline at end of file diff --git a/tests/performance/tests/inference_multiple_models/environments/xlarge.yaml b/tests/performance/tests/inference_multiple_models/environments/xlarge.yaml deleted file mode 100644 index 36b7dc0ad..000000000 --- a/tests/performance/tests/inference_multiple_models/environments/xlarge.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -settings: - env: - INFR1_SUCC : 100% - INFR2_SUCC: 100% - INFR1_RT : 290ms - INFR2_RT: 450ms - TOTAL_PROCS : 5 - TOTAL_FDS : 107 - TOTAL_MEM : 600000000 #600MB \ No newline at end of file diff --git a/tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml b/tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml deleted file mode 100644 index 5b9fd6c0a..000000000 --- a/tests/performance/tests/inference_multiple_worker/environments/xlarge.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -settings: - env: - INFR_SUCC : 100% - INFR_RT : 140ms - TOTAL_PROCS : 6 - TOTAL_FDS : 126 - TOTAL_MEM : 750000000 #750MB \ No newline at end of file diff --git a/tests/performance/tests/inference_single_worker/environments/xlarge.yaml b/tests/performance/tests/inference_single_worker/environments/xlarge.yaml deleted file mode 100644 index c945e1f91..000000000 --- a/tests/performance/tests/inference_single_worker/environments/xlarge.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -settings: - env: - INFR_SUCC : 100% - INFR_RT : 290ms - TOTAL_PROCS : 3 - TOTAL_FDS : 90 - TOTAL_MEM : 290000000 #290MB \ No newline at end of file diff --git a/tests/performance/tests/list_models/environments/xlarge.yaml b/tests/performance/tests/list_models/environments/xlarge.yaml deleted file mode 100644 index 611624824..000000000 --- a/tests/performance/tests/list_models/environments/xlarge.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -settings: - env: - LST_MODLS_SUCC : 100% - LST_MODLS_RT : 14ms - TOTAL_PROCS : 3 - TOTAL_FDS : 86 - TOTAL_MEM : 185000000 #185MB \ No newline at end of file diff --git a/tests/performance/tests/model_description/environments/xlarge.yaml b/tests/performance/tests/model_description/environments/xlarge.yaml deleted file mode 100644 index 00e0aac87..000000000 --- a/tests/performance/tests/model_description/environments/xlarge.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -settings: - env: - MODL_DESC_SUCC : 100% - MODL_DESC_RT : 14ms - TOTAL_PROCS : 3 - TOTAL_FDS : 90 - TOTAL_MEM : 300000000 #300MB \ No newline at end of file diff --git a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml b/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml deleted file mode 100644 index 1671213d1..000000000 --- a/tests/performance/tests/multiple_inference_and_scaling/environments/xlarge.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -settings: - env: - INFR1_SUCC : 100% - INFR2_SUCC: 100% - INFR1_RT : 290ms - INFR2_RT: 450ms - TOTAL_PROCS : 14 - TOTAL_FDS : 300 - TOTAL_MEM : 2000000000 #~2GB - TOTAL_ORPHANS : 0 - FRNTEND_MEM : 1000000000 #~1GB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister/environments/xlarge.yaml b/tests/performance/tests/register_unregister/environments/xlarge.yaml deleted file mode 100644 index 0e099afed..000000000 --- a/tests/performance/tests/register_unregister/environments/xlarge.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -settings: - env: - REG_SUCC : 100% - UNREG_SUCC: 100% - REG_RT : 15s - UNREG_RT: 10ms - TOTAL_PROCS : 1 - TOTAL_FDS : 66 - TOTAL_ORPHANS : 0 - FRNTEND_MEM : 75000000 #75MB \ No newline at end of file diff --git a/tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml b/tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml deleted file mode 100644 index 24c07f5cf..000000000 --- a/tests/performance/tests/register_unregister_multiple/environments/xlarge.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -settings: - env: - REG_SUCC : 100% - SCL_UP_SUCC: 100% - UNREG_SUCC: 100% - REG_RT : 15s - SCL_UP_RT: 1.5s - UNREG_RT: 18ms - TOTAL_PROCS : 2 - TOTAL_FDS : 73 - FRNTEND_MEM : 120000000 #120MB \ No newline at end of file diff --git a/tests/performance/tests/scale_down_workers/environments/xlarge.yaml b/tests/performance/tests/scale_down_workers/environments/xlarge.yaml deleted file mode 100644 index 6d43899b3..000000000 --- a/tests/performance/tests/scale_down_workers/environments/xlarge.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -settings: - env: - SCL_DWN_SUCC : 100% - SCL_DWN_RT : 10ms - TOTAL_PROCS_B4_SCL_DWN : 6 - TOTAL_PROCS_AFTR_SCL_DWN : 4 - TOTAL_WRKRS_B4_SCL_DWN : 4 - TOTAL_WRKRS_AFTR_SCL_DWN : 2 - FRNTEND_FDS : 78 - TOTAL_WRKRS_FDS_B4_SCL_DWN: 38 - TOTAL_WRKRS_FDS_AFTR_SCL_DWN: 23 - FRNTEND_MEM : 290000000 #290MB - TOTAL_WRKRS_MEM_B4_SCL_DWN : 450000000 #450MB - TOTAL_WRKRS_MEM_AFTR_SCL_DWN : 210000000 #210MB \ No newline at end of file diff --git a/tests/performance/tests/scale_up_workers/environments/xlarge.yaml b/tests/performance/tests/scale_up_workers/environments/xlarge.yaml deleted file mode 100644 index 9e3182c3e..000000000 --- a/tests/performance/tests/scale_up_workers/environments/xlarge.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -settings: - env: - SCL_UP_SUCC : 100% - SCL_UP_RT : 10ms - TOTAL_PROCS_AFTR_SCL_UP : 6 - TOTAL_PROCS_B4_SCL_UP : 3 - TOTAL_WRKRS_AFTR_SCL_UP : 4 - TOTAL_WRKRS_B4_SCL_UP : 1 - FRNTEND_FDS : 88 - TOTAL_WRKRS_FDS_AFTR_SCL_UP : 38 - TOTAL_WRKRS_FDS_B4_SCL_UP : 11 - FRNTEND_MEM : 290000000 #290MB - TOTAL_WRKRS_MEM_AFTR_SCL_UP : 450000000 #450MB - TOTAL_WRKRS_MEM_B4_SCL_UP : 115000000 #115MB \ No newline at end of file From ec6dc02648825d6d98b4803c795fe4392e5ff427 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 9 Jul 2020 11:37:47 +0530 Subject: [PATCH 321/329] Updated Readme --- .circleci/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/README.md b/.circleci/README.md index 20483a0d6..1bc078157 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -49,7 +49,7 @@ To make it easy for developers to debug build issues locally, MMS supports Circl #### Command Developers can use the following command to build MMS locally: -**_./run_circleci_tests.py -j -e ** +**./run_circleci_tests.py -j -e ** - _workflow_name_ This is a madatory parameter From fb927b97fb3c375081192c65ed594fa1ddb4f343 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 9 Jul 2020 11:52:27 +0530 Subject: [PATCH 322/329] corrected paths after movement from test to tests/api --- tests/api/regression_tests.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/api/regression_tests.sh b/tests/api/regression_tests.sh index d9eeee730..0e5e2f6c6 100755 --- a/tests/api/regression_tests.sh +++ b/tests/api/regression_tests.sh @@ -48,7 +48,7 @@ stop_mms_serve() { start_secure_mms() { # Start MMS with Model Store - multi-model-server --start --mms-config tests/api/resources/config.properties --model-store $1 &>> $2 + multi-model-server --start --mms-config resources/config.properties --model-store $1 &>> $2 sleep 10 curl --insecure -X GET https://127.0.0.1:8444/models } @@ -62,20 +62,20 @@ run_postman_test() { # Run Management API Tests stop_mms_serve start_mms $MODEL_STORE $MMS_LOG_FILE - newman run -e tests/api/postman/environment.json --bail --verbose tests/api/postman/management_api_test_collection.json \ + newman run -e postman/environment.json --bail --verbose postman/management_api_test_collection.json \ -r cli,html --reporter-html-export $ROOT_DIR/report/management_report.html >>$1 2>&1 # Run Inference API Tests after Restart stop_mms_serve start_mms $MODEL_STORE $MMS_LOG_FILE - newman run -e tests/api/postman/environment.json --bail --verbose tests/api/postman/inference_api_test_collection.json \ - -d tests/api/postman/inference_data.json -r cli,html --reporter-html-export $ROOT_DIR/report/inference_report.html >>$1 2>&1 + newman run -e postman/environment.json --bail --verbose postman/inference_api_test_collection.json \ + -d postman/inference_data.json -r cli,html --reporter-html-export $ROOT_DIR/report/inference_report.html >>$1 2>&1 # Run Https test cases stop_mms_serve start_secure_mms $MODEL_STORE $MMS_LOG_FILE - newman run --insecure -e tests/api/postman/environment.json --bail --verbose tests/api/postman/https_test_collection.json \ + newman run --insecure -e postman/environment.json --bail --verbose postman/https_test_collection.json \ -r cli,html --reporter-html-export $ROOT_DIR/report/MMS_https_test_report.html >>$1 2>&1 stop_mms_serve From 7868bf0502c3b410bb590c3260077e59ca79ec63 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 9 Jul 2020 12:05:32 +0530 Subject: [PATCH 323/329] dev test triggger --- .circleci/config.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 298c6532d..39f03db6f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -171,6 +171,18 @@ workflows: requires: - build-<< matrix.executor >> matrix: *matrix + ##### Remove after Dev test ############################### + - api-tests: + name: api-tests-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix + - performance-regression: + name: performance-regression-<< matrix.executor >> + requires: + - build-<< matrix.executor >> + matrix: *matrix + ############################################################# nightly: triggers: From 65549f32650bb048b15771940191e3741f0efb8c Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 9 Jul 2020 12:48:50 +0530 Subject: [PATCH 324/329] updated thresholds for circlce ci --- .../tests/api_description/environments/ci_linux_medium.yaml | 2 +- .../environments/ci_linux_medium.yaml | 2 +- .../inference_multiple_models/environments/ci_linux_medium.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/performance/tests/api_description/environments/ci_linux_medium.yaml b/tests/performance/tests/api_description/environments/ci_linux_medium.yaml index 73bbcb0bd..bc9f42172 100644 --- a/tests/performance/tests/api_description/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/api_description/environments/ci_linux_medium.yaml @@ -6,5 +6,5 @@ settings: MGMT_DESC_RT : 20ms INFR_DESC_RT : 20ms TOTAL_PROCS : 1 - TOTAL_FDS : 488 + TOTAL_FDS : 490 TOTAL_MEM: 888000000 #888MB diff --git a/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml b/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml index 1edde91c4..12770a3cd 100644 --- a/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/batch_and_single_inference/environments/ci_linux_medium.yaml @@ -2,7 +2,7 @@ settings: env: INFR1_RT : 5s - INFR2_RT : 110ms + INFR2_RT : 200ms TOTAL_WORKERS_MEM : 1630000000 #1.63GB TOTAL_WORKERS : 5 TOTAL_ORPHANS : 0 diff --git a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml index 5074e7808..1a0e1dc20 100644 --- a/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/inference_multiple_models/environments/ci_linux_medium.yaml @@ -3,7 +3,7 @@ settings: env: INFR1_SUCC : 100% INFR2_SUCC: 100% - INFR1_RT : 350ms + INFR1_RT : 1s INFR2_RT: 1.5s TOTAL_PROCS : 5 TOTAL_FDS : 521 From 63dd038a330b47d2ee3f74b312cfde28b9042d47 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 9 Jul 2020 13:22:27 +0530 Subject: [PATCH 325/329] updated thresholds for circlce ci --- .../tests/register_unregister/environments/ci_linux_medium.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml b/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml index a5b84eecc..690599324 100644 --- a/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml @@ -4,7 +4,7 @@ settings: REG_SUCC : 100% UNREG_SUCC: 100% REG_RT : 2.5s - UNREG_RT: 60ms + UNREG_RT: 100ms TOTAL_PROCS : 1 TOTAL_FDS : 475 TOTAL_ORPHANS : 0 From bcd948ef06d11c968868ff18de82aef2fd029259 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 9 Jul 2020 13:56:05 +0530 Subject: [PATCH 326/329] updated thresholds for circlce ci --- .../tests/register_unregister/environments/ci_linux_medium.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml b/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml index 690599324..62df2bbe0 100644 --- a/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/register_unregister/environments/ci_linux_medium.yaml @@ -4,7 +4,7 @@ settings: REG_SUCC : 100% UNREG_SUCC: 100% REG_RT : 2.5s - UNREG_RT: 100ms + UNREG_RT: 300ms TOTAL_PROCS : 1 TOTAL_FDS : 475 TOTAL_ORPHANS : 0 From 8098785b1449a132350b224559356786c61cdb98 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 9 Jul 2020 14:30:10 +0530 Subject: [PATCH 327/329] updated thresholds for circlce ci --- .../environments/ci_linux_medium.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml b/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml index 276dc1dde..15f1ec7ff 100644 --- a/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml +++ b/tests/performance/tests/register_unregister_multiple/environments/ci_linux_medium.yaml @@ -4,9 +4,9 @@ settings: REG_SUCC : 100% SCL_UP_SUCC: 100% UNREG_SUCC: 100% - REG_RT : 2.5s - SCL_UP_RT: 1.5s - UNREG_RT: 150ms + REG_RT : 3s + SCL_UP_RT: 2s + UNREG_RT: 500ms TOTAL_PROCS_WITH_EXTRA_MODEL_AND_WORKERS : 5 TOTAL_PROCS_WITHOUT_EXTRA_MODEL : 2 TOTAL_FDS : 524 From 2bfb5a0783e1142b0baf5dd2bcc076894fdff303 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Thu, 9 Jul 2020 15:11:15 +0530 Subject: [PATCH 328/329] Revert dev test changes --- .circleci/config.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 39f03db6f..298c6532d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -171,18 +171,6 @@ workflows: requires: - build-<< matrix.executor >> matrix: *matrix - ##### Remove after Dev test ############################### - - api-tests: - name: api-tests-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix - - performance-regression: - name: performance-regression-<< matrix.executor >> - requires: - - build-<< matrix.executor >> - matrix: *matrix - ############################################################# nightly: triggers: From 425f2c8137a3d9c0a1fe2ba47d0b866c24c16976 Mon Sep 17 00:00:00 2001 From: Prashant Sail Date: Fri, 10 Jul 2020 14:43:05 +0530 Subject: [PATCH 329/329] Renamed Job Step --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 298c6532d..76aa16fb6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -131,7 +131,7 @@ jobs: name: Store artifacts from performance regression run path: tests/performance/run_artifacts/ - store_test_results: - name: Store Taurus test results + name: Store performance regression test results for CircleCI path: tests/performance/run_artifacts/report/ modelarchiver: